//********************************************************
//
// Assignment 9 - Linked Lists
//
// Name: Seth Hin
//
// Class: C Programming, Spring 2026
//
// Date: <replace with the current date>
//
// 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 TAX_STATE_SIZE 3
#define FED_TAX_RATE 0.25
#define FIRST_NAME_SIZE 10
#define LAST_NAME_SIZE 10

struct name {
    char firstName[FIRST_NAME_SIZE];
    char lastName[LAST_NAME_SIZE];
};

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;
};

struct totals {
    float total_wageRate;
    float total_hours;
    float total_overtimeHrs;
    float total_grossPay;
    float total_stateTax;
    float total_fedTax;
    float total_netPay;
};

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;
};

// 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);

int main() {
    struct employee *head_ptr;
    int theSize;

    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};

    head_ptr = getEmpData();
    theSize = isEmployeeSize(head_ptr);

    if (theSize <= 0) {
        printf("\n\n**** There was no employee input to process ***\n");
    } else {
        calcOvertimeHrs(head_ptr);
        calcGrossPay(head_ptr);
        calcStateTax(head_ptr);
        calcFedTax(head_ptr);
        calcNetPay(head_ptr);

        calcEmployeeTotals(head_ptr, &employeeTotals);
        calcEmployeeMinMax(head_ptr, &employeeMinMax);

        printHeader();
        printEmp(head_ptr);
        printEmpStatistics(&employeeTotals, &employeeMinMax, theSize);
    }

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

//================ INPUT =================
struct employee * getEmpData (void)
{
    char answer[10];
    struct employee *head = NULL, *current = NULL;

    do {
        struct employee *newNode = (struct employee*)malloc(sizeof(struct employee));

        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);

        newNode->next = NULL;

        if (head == NULL)
            head = newNode;
        else
            current->next = newNode;

        current = newNode;

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

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

    return head;
}

//================ BASIC CALCULATIONS =================
int isEmployeeSize(struct employee *head) {
    int count = 0;
    while (head) { count++; head = head->next; }
    return count;
}

void calcOvertimeHrs(struct employee *head) {
    for (; head; head = head->next)
        head->overtimeHrs = (head->hours > STD_HOURS) ? head->hours - STD_HOURS : 0;
}

void calcGrossPay(struct employee *head) {
    for (; head; head = head->next) {
        float normal = head->wageRate * (head->hours - head->overtimeHrs);
        float ot = head->overtimeHrs * (head->wageRate * OT_RATE);
        head->grossPay = normal + ot;
    }
}

//================ REQUIRED FIXES =================
void calcStateTax(struct employee *head) {
    for (; head; head = head->next) {
        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;
    }
}

void calcFedTax(struct employee *head) {
    for (; head; head = head->next)
        head->fedTax = head->grossPay * FED_TAX_RATE;
}

void calcNetPay(struct employee *head) {
    for (; head; head = head->next)
        head->netPay = head->grossPay - (head->stateTax + head->fedTax);
}

//================ TOTALS =================
void calcEmployeeTotals(struct employee *head, struct totals *t) {
    for (; head; head = head->next) {
        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 (FULL FIXED) =================
void calcEmployeeMinMax(struct employee *head, struct min_max *m) {

    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;

    for (; head; head = head->next) {
        if (head->wageRate < m->min_wageRate) m->min_wageRate = head->wageRate;
        if (head->wageRate > m->max_wageRate) m->max_wageRate = head->wageRate;

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

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

        if (head->grossPay < m->min_grossPay) m->min_grossPay = head->grossPay;
        if (head->grossPay > m->max_grossPay) m->max_grossPay = head->grossPay;

        if (head->stateTax < m->min_stateTax) m->min_stateTax = head->stateTax;
        if (head->stateTax > m->max_stateTax) m->max_stateTax = head->stateTax;

        if (head->fedTax < m->min_fedTax) m->min_fedTax = head->fedTax;
        if (head->fedTax > m->max_fedTax) m->max_fedTax = head->fedTax;

        if (head->netPay < m->min_netPay) m->min_netPay = head->netPay;
        if (head->netPay > m->max_netPay) m->max_netPay = head->netPay;
    }
}

//================ OUTPUT =================
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---------------------------------------------------------------------------------");
}

void printEmp(struct employee *head) {
    char name[25];

    for (; head; head = head->next) {
        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);
    }
}

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);
}