//********************************************************
//
// Assignment 9 - Linked Lists
//
// Name: Seth Hin
//
// Class: C Programming, Spring 2026
//
// Date: April 11,2026
//
// Description: Program which determines overtime and 
// gross pay for a set of employees with outputs sent 
// to standard output (the screen).
//
// This assignment also adds the employee name, their tax state,
// and calculates the state tax, federal tax, and net pay.   It
// also calculates totals, averages, minimum, and maximum values.
//
// Array and Structure references have all been replaced with
// pointer references to speed up the processing of this code.
// A linked list has been created and deployed to dynamically
// allocate and process employees as needed.
//
// Call by Reference design (using pointers)
//
//********************************************************

// necessary header files
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
 
#define STD_HOURS 40.0
#define OT_RATE 1.5
#define MA_TAX_RATE 0.05
#define NH_TAX_RATE 0.0
#define VT_TAX_RATE 0.06
#define CA_TAX_RATE 0.07
#define DEFAULT_TAX_RATE 0.08
#define NAME_SIZE 20
#define TAX_STATE_SIZE 3
#define FED_TAX_RATE 0.25
#define FIRST_NAME_SIZE 10
#define LAST_NAME_SIZE 10
 
//================ STRUCT DEFINITIONS =================

// Structure to store employee name
struct name {
    char firstName[FIRST_NAME_SIZE];
    char lastName[LAST_NAME_SIZE];
};

// Structure to store employee data (linked list node)
struct employee {
    struct name empName;
    char taxState[TAX_STATE_SIZE];
    long int clockNumber;
    float wageRate;
    float hours;
    float overtimeHrs;
    float grossPay;
    float stateTax;
    float fedTax;
    float netPay;
    struct employee *next;
};

// Structure to store running totals
struct totals {
    float total_wageRate;
    float total_hours;
    float total_overtimeHrs;
    float total_grossPay;
    float total_stateTax;
    float total_fedTax;
    float total_netPay;
};

// Structure to store minimum and maximum values
struct min_max {
    float min_wageRate, min_hours, min_overtimeHrs, min_grossPay, min_stateTax, min_fedTax, min_netPay;
    float max_wageRate, max_hours, max_overtimeHrs, max_grossPay, max_stateTax, max_fedTax, max_netPay;
};

//================ FUNCTION PROTOTYPES =================
struct employee * getEmpData (void);
int isEmployeeSize (struct employee * head_ptr);
void calcOvertimeHrs (struct employee * head_ptr);
void calcGrossPay (struct employee * head_ptr);
void calcStateTax (struct employee * head_ptr);
void calcFedTax (struct employee * head_ptr);
void calcNetPay (struct employee * head_ptr);
void calcEmployeeTotals (struct employee * head_ptr, struct totals * emp_totals_ptr);
void calcEmployeeMinMax (struct employee * head_ptr, struct min_max * emp_minMax_ptr);
void printHeader (void);
void printEmp (struct employee * head_ptr);
void printEmpStatistics (struct totals * emp_totals_ptr, struct min_max * emp_minMax_ptr, int theSize);

//================ MAIN FUNCTION =================
int main() {

    struct employee *head_ptr;   // pointer to first node in linked list
    int theSize;                 // number of employees

    // initialize totals and min/max structures
    struct totals employeeTotals = {0,0,0,0,0,0,0};
    struct min_max employeeMinMax = {0,0,0,0,0,0,0,0,0,0,0,0,0,0};

    // get employee data and build linked list
    head_ptr = getEmpData();

    // count number of employees
    theSize = isEmployeeSize(head_ptr);

    // if no employees entered
    if (theSize <= 0) {
        printf("\n\n**** There was no employee input to process ***\n");
    }
    else {

        // calculate overtime hours for each employee
        calcOvertimeHrs(head_ptr);

        // calculate gross pay
        calcGrossPay(head_ptr);

        // calculate state tax
        calcStateTax(head_ptr);

        // calculate federal tax
        calcFedTax(head_ptr);

        // calculate net pay
        calcNetPay(head_ptr);

        // compute totals across all employees
        calcEmployeeTotals(head_ptr, &employeeTotals);

        // compute min and max values
        calcEmployeeMinMax(head_ptr, &employeeMinMax);

        // print report header
        printHeader();

        // print employee details
        printEmp(head_ptr);

        // print summary statistics
        printEmpStatistics(&employeeTotals, &employeeMinMax, theSize);
    }

    printf("\n\n *** End of Program *** \n");
    return 0;
}

//================ INPUT FUNCTION =================
struct employee * getEmpData (void)
{
    char answer[10];

    struct employee *head = NULL;     // first node
    struct employee *current = NULL;  // last node

    do {
        // allocate new employee node
        struct employee *newNode =
            (struct employee*)malloc(sizeof(struct employee));

        // input employee data
        printf("\nEnter employee first name: ");
        scanf("%s", newNode->empName.firstName);

        printf("\nEnter employee last name: ");
        scanf("%s", newNode->empName.lastName);

        printf("\nEnter employee two character tax state: ");
        scanf("%s", newNode->taxState);

        printf("\nEnter employee clock number: ");
        scanf("%li", &newNode->clockNumber);

        printf("\nEnter employee hourly wage: ");
        scanf("%f", &newNode->wageRate);

        printf("\nEnter hours worked this week: ");
        scanf("%f", &newNode->hours);

        // initialize pointer
        newNode->next = NULL;

        // insert into linked list
        if (head == NULL)
            head = newNode;
        else
            current->next = newNode;

        current = newNode;

        // ask for more employees
        printf("\nWould you like to add another employee? (y/n): ");
        scanf("%s", answer);

    } while (toupper(answer[0]) == 'Y');

    return head;
}

//================ COUNT FUNCTION =================
int isEmployeeSize(struct employee *head)
{
    int count = 0;

    // traverse list and count nodes
    while (head) {
        count++;
        head = head->next;
    }

    return count;
}

//================ OVERTIME CALCULATION =================
void calcOvertimeHrs(struct employee *head)
{
    // compute overtime hours for each employee
    for (; head; head = head->next)
        head->overtimeHrs =
            (head->hours > STD_HOURS) ? head->hours - STD_HOURS : 0;
}

//================ GROSS PAY CALCULATION =================
void calcGrossPay(struct employee *head)
{
    for (; head; head = head->next) {

        float normalPay;
        float overtimePay;

        // calculate normal pay
        normalPay = head->wageRate * (head->hours - head->overtimeHrs);

        // calculate overtime pay
        overtimePay = head->overtimeHrs * (head->wageRate * OT_RATE);

        // total gross pay
        head->grossPay = normalPay + overtimePay;
    }
}

//================ STATE TAX CALCULATION =================
void calcStateTax(struct employee *head)
{
    for (; head; head = head->next) {

        // determine tax rate based on state code
        if (strcmp(head->taxState, "MA") == 0)
            head->stateTax = head->grossPay * MA_TAX_RATE;

        else if (strcmp(head->taxState, "NH") == 0)
            head->stateTax = head->grossPay * NH_TAX_RATE;

        else if (strcmp(head->taxState, "VT") == 0)
            head->stateTax = head->grossPay * VT_TAX_RATE;

        else if (strcmp(head->taxState, "CA") == 0)
            head->stateTax = head->grossPay * CA_TAX_RATE;

        else
            head->stateTax = head->grossPay * DEFAULT_TAX_RATE;
    }
}

//================ FEDERAL TAX CALCULATION =================
void calcFedTax(struct employee *head)
{
    for (; head; head = head->next) {

        // federal tax is flat rate
        head->fedTax = head->grossPay * FED_TAX_RATE;
    }
}

//================ NET PAY CALCULATION =================
void calcNetPay(struct employee *head)
{
    for (; head; head = head->next) {

        // net pay = gross pay - taxes
        head->netPay = head->grossPay - (head->stateTax + head->fedTax);
    }
}

//================ TOTALS CALCULATION =================
void calcEmployeeTotals(struct employee *head, struct totals *t)
{
    for (; head; head = head->next) {

        // accumulate totals for each field
        t->total_wageRate += head->wageRate;
        t->total_hours += head->hours;
        t->total_overtimeHrs += head->overtimeHrs;
        t->total_grossPay += head->grossPay;
        t->total_stateTax += head->stateTax;
        t->total_fedTax += head->fedTax;
        t->total_netPay += head->netPay;
    }
}

//================ MIN / MAX CALCULATION =================
void calcEmployeeMinMax(struct employee *head, struct min_max *m)
{
    // initialize min/max using first employee
    m->min_wageRate = m->max_wageRate = head->wageRate;
    m->min_hours = m->max_hours = head->hours;
    m->min_overtimeHrs = m->max_overtimeHrs = head->overtimeHrs;
    m->min_grossPay = m->max_grossPay = head->grossPay;
    m->min_stateTax = m->max_stateTax = head->stateTax;
    m->min_fedTax = m->max_fedTax = head->fedTax;
    m->min_netPay = m->max_netPay = head->netPay;

    head = head->next;

    // traverse remaining nodes
    for (; head; head = head->next) {

        // compare wage rate
        if (head->wageRate < m->min_wageRate) m->min_wageRate = head->wageRate;
        if (head->wageRate > m->max_wageRate) m->max_wageRate = head->wageRate;

        // compare hours
        if (head->hours < m->min_hours) m->min_hours = head->hours;
        if (head->hours > m->max_hours) m->max_hours = head->hours;

        // compare overtime
        if (head->overtimeHrs < m->min_overtimeHrs) m->min_overtimeHrs = head->overtimeHrs;
        if (head->overtimeHrs > m->max_overtimeHrs) m->max_overtimeHrs = head->overtimeHrs;

        // compare gross pay
        if (head->grossPay < m->min_grossPay) m->min_grossPay = head->grossPay;
        if (head->grossPay > m->max_grossPay) m->max_grossPay = head->grossPay;

        // compare state tax
        if (head->stateTax < m->min_stateTax) m->min_stateTax = head->stateTax;
        if (head->stateTax > m->max_stateTax) m->max_stateTax = head->stateTax;

        // compare federal tax
        if (head->fedTax < m->min_fedTax) m->min_fedTax = head->fedTax;
        if (head->fedTax > m->max_fedTax) m->max_fedTax = head->fedTax;

        // compare net pay
        if (head->netPay < m->min_netPay) m->min_netPay = head->netPay;
        if (head->netPay > m->max_netPay) m->max_netPay = head->netPay;
    }
}

//================ PRINT HEADER =================
void printHeader(void)
{
    printf("\n\n*** Pay Calculator ***\n");

    printf("\n---------------------------------------------------------------------------------");
    printf("\nName                Tax  Clock#  Wage   Hours  OT   Gross   State  Fed      Net");
    printf("\n                    State                           Pay     Tax    Tax      Pay");
    printf("\n---------------------------------------------------------------------------------");
}

//================ PRINT EMPLOYEES =================
void printEmp(struct employee *head)
{
    char name[25];

    for (; head; head = head->next) {

        // combine first and last name
        sprintf(name, "%s %s", head->empName.firstName, head->empName.lastName);

        printf("\n%-20s %-2s %06li %6.2f %6.1f %5.1f %7.2f %7.2f %7.2f %9.2f",
            name, head->taxState, head->clockNumber,
            head->wageRate, head->hours, head->overtimeHrs,
            head->grossPay, head->stateTax, head->fedTax, head->netPay);
    }
}

//================ PRINT STATISTICS =================
void printEmpStatistics(struct totals *t, struct min_max *m, int n)
{
    printf("\n---------------------------------------------------------------------------------");

    printf("\nTotals:                          %5.2f %5.1f %5.1f %7.2f %7.2f %7.2f %9.2f",
        t->total_wageRate, t->total_hours, t->total_overtimeHrs,
        t->total_grossPay, t->total_stateTax, t->total_fedTax, t->total_netPay);

    printf("\nAverages:                        %5.2f %5.1f %5.1f %7.2f %7.2f %7.2f %9.2f",
        t->total_wageRate/n, t->total_hours/n, t->total_overtimeHrs/n,
        t->total_grossPay/n, t->total_stateTax/n, t->total_fedTax/n, t->total_netPay/n);

    printf("\nMinimum:                         %5.2f %5.1f %5.1f %7.2f %7.2f %7.2f %9.2f",
        m->min_wageRate, m->min_hours, m->min_overtimeHrs,
        m->min_grossPay, m->min_stateTax, m->min_fedTax, m->min_netPay);

    printf("\nMaximum:                         %5.2f %5.1f %5.1f %7.2f %7.2f %7.2f %9.2f",
        m->max_wageRate, m->max_hours, m->max_overtimeHrs,
        m->max_grossPay, m->max_stateTax, m->max_fedTax, m->max_netPay);

    printf("\n\nThe total employees processed was: %d\n", n);
}