//********************************************************
//
// Assignment 10 - Linked Lists, Typedef, and Macros
//
// Name: John Semenuk
//
// Class: C Programming, Spring 2026
//
// Date: April 20, 2026
//
//********************************************************

#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_STATE_TAX_RATE 0.08
#define FED_TAX_RATE 0.25

#define FIRST_NAME_SIZE 10
#define LAST_NAME_SIZE 10
#define TAX_STATE_SIZE 3

// macros
#define CALC_OT_HOURS(theHours) ((theHours > STD_HOURS) ? theHours - STD_HOURS : 0)
#define CALC_STATE_TAX(thePay,theRate) (thePay * theRate)
#define CALC_FED_TAX(thePay) (thePay * FED_TAX_RATE)
#define CALC_NET_PAY(thePay,theStateTax,theFedTax) (thePay - (theStateTax + theFedTax))
#define CALC_NORMAL_PAY(rate,hours,ot) (rate * (hours - ot))
#define CALC_OT_PAY(rate,ot) (ot * (OT_RATE * rate))

// FIXED MIN/MAX
#define CALC_MIN(val,min) ((val < min) ? val : min)
#define CALC_MAX(val,max) ((val > max) ? val : max)

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

// employee typedef
typedef 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;
} EMPLOYEE;

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

// MIN_MAX typedef FIXED
typedef struct min_max {
    float min_wageRate;
    float min_hours;
    float min_overtimeHrs;
    float min_grossPay;
    float min_stateTax;
    float min_fedTax;
    float min_netPay;
    float max_wageRate;
    float max_hours;
    float max_overtimeHrs;
    float max_grossPay;
    float max_stateTax;
    float max_fedTax;
    float max_netPay;
} MIN_MAX;

// prototypes
EMPLOYEE * getEmpData(void);
int isEmployeeSize(EMPLOYEE *head_ptr);
void calcOvertimeHrs(EMPLOYEE *head_ptr);
void calcGrossPay(EMPLOYEE *head_ptr);
void calcStateTax(EMPLOYEE *head_ptr);
void calcFedTax(EMPLOYEE *head_ptr);
void calcNetPay(EMPLOYEE *head_ptr);
void calcEmployeeTotals(EMPLOYEE *head_ptr, TOTALS *totals);
void calcEmployeeMinMax(EMPLOYEE *head_ptr, MIN_MAX *minmax);
void printHeader(void);
void printEmp(EMPLOYEE *head_ptr);
void printEmpStatistics(TOTALS *totals, MIN_MAX *minmax, int size);

int main() {

    EMPLOYEE *head_ptr;
    int size;

    TOTALS totals = {0};
    MIN_MAX minmax;

    head_ptr = getEmpData();
    size = isEmployeeSize(head_ptr);

    if (size <= 0) {
        printf("\nNo employees.\n");
        return 0;
    }

    calcOvertimeHrs(head_ptr);
    calcGrossPay(head_ptr);
    calcStateTax(head_ptr);
    calcFedTax(head_ptr);
    calcNetPay(head_ptr);
    calcEmployeeTotals(head_ptr, &totals);
    calcEmployeeMinMax(head_ptr, &minmax);

    printHeader();
    printEmp(head_ptr);
    printEmpStatistics(&totals, &minmax, size);

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

//================ FUNCTIONS =================

EMPLOYEE * getEmpData(void) {

    EMPLOYEE *head = malloc(sizeof(EMPLOYEE));
    EMPLOYEE *curr = head;

    char ans[10];
    int more = 1;

    while (more) {
        printf("First name: "); scanf("%s", curr->empName.firstName);
        printf("Last name: "); scanf("%s", curr->empName.lastName);
        printf("State: "); scanf("%s", curr->taxState);
        printf("Clock #: "); scanf("%ld", &curr->clockNumber);
        printf("Wage: "); scanf("%f", &curr->wageRate);
        printf("Hours: "); scanf("%f", &curr->hours);

        printf("More? (y/n): ");
        scanf("%s", ans);

        if (toupper(ans[0]) != 'Y') {
            curr->next = NULL;
            more = 0;
        } else {
            curr->next = malloc(sizeof(EMPLOYEE));
            curr = curr->next;
        }
    }
    return head;
}

int isEmployeeSize(EMPLOYEE *head) {
    int count = 0;
    while (head) {
        count++;
        head = head->next;
    }
    return count;
}

void calcOvertimeHrs(EMPLOYEE *head) {
    while (head) {
        head->overtimeHrs = CALC_OT_HOURS(head->hours);
        head = head->next;
    }
}

void calcGrossPay(EMPLOYEE *head) {
    while (head) {
        float normal = CALC_NORMAL_PAY(head->wageRate, head->hours, head->overtimeHrs);
        float ot = CALC_OT_PAY(head->wageRate, head->overtimeHrs);
        head->grossPay = normal + ot;
        head = head->next;
    }
}

void calcStateTax(EMPLOYEE *head) {
    while (head) {
        if (!strcmp(head->taxState,"MA"))
            head->stateTax = CALC_STATE_TAX(head->grossPay, MA_TAX_RATE);
        else if (!strcmp(head->taxState,"VT"))
            head->stateTax = CALC_STATE_TAX(head->grossPay, VT_TAX_RATE);
        else if (!strcmp(head->taxState,"NH"))
            head->stateTax = CALC_STATE_TAX(head->grossPay, NH_TAX_RATE);
        else if (!strcmp(head->taxState,"CA"))
            head->stateTax = CALC_STATE_TAX(head->grossPay, CA_TAX_RATE);
        else
            head->stateTax = CALC_STATE_TAX(head->grossPay, DEFAULT_STATE_TAX_RATE);

        head = head->next;
    }
}

void calcFedTax(EMPLOYEE *head) {
    while (head) {
        head->fedTax = CALC_FED_TAX(head->grossPay);
        head = head->next;
    }
}

void calcNetPay(EMPLOYEE *head) {
    while (head) {
        head->netPay = CALC_NET_PAY(head->grossPay, head->stateTax, head->fedTax);
        head = head->next;
    }
}

void calcEmployeeTotals(EMPLOYEE *head, TOTALS *t) {
    while (head) {
        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;
        head = head->next;
    }
}

void calcEmployeeMinMax(EMPLOYEE *head, MIN_MAX *m) {

    *m = (MIN_MAX){head->wageRate,head->hours,head->overtimeHrs,
                   head->grossPay,head->stateTax,head->fedTax,head->netPay,
                   head->wageRate,head->hours,head->overtimeHrs,
                   head->grossPay,head->stateTax,head->fedTax,head->netPay};

    head = head->next;

    while (head) {
        m->min_wageRate = CALC_MIN(head->wageRate, m->min_wageRate);
        m->max_wageRate = CALC_MAX(head->wageRate, m->max_wageRate);

        m->min_hours = CALC_MIN(head->hours, m->min_hours);
        m->max_hours = CALC_MAX(head->hours, m->max_hours);

        m->min_overtimeHrs = CALC_MIN(head->overtimeHrs, m->min_overtimeHrs);
        m->max_overtimeHrs = CALC_MAX(head->overtimeHrs, m->max_overtimeHrs);

        m->min_grossPay = CALC_MIN(head->grossPay, m->min_grossPay);
        m->max_grossPay = CALC_MAX(head->grossPay, m->max_grossPay);

        m->min_stateTax = CALC_MIN(head->stateTax, m->min_stateTax);
        m->max_stateTax = CALC_MAX(head->stateTax, m->max_stateTax);

        m->min_fedTax = CALC_MIN(head->fedTax, m->min_fedTax);
        m->max_fedTax = CALC_MAX(head->fedTax, m->max_fedTax);

        m->min_netPay = CALC_MIN(head->netPay, m->min_netPay);
        m->max_netPay = CALC_MAX(head->netPay, m->max_netPay);

        head = head->next;
    }
}

void printHeader() {
    printf("\n*** Pay Calculator ***\n");
}

void printEmp(EMPLOYEE *head) {
    while (head) {
        printf("\n%s %s Net: %.2f",
               head->empName.firstName,
               head->empName.lastName,
               head->netPay);
        head = head->next;
    }
}

void printEmpStatistics(TOTALS *t, MIN_MAX *m, int size) {

    printf("\nTotals Gross: %.2f", t->total_grossPay);
    printf("\nAverage Gross: %.2f", t->total_grossPay / size);
    printf("\nMin Gross: %.2f", m->min_grossPay);
    printf("\nMax Gross: %.2f", m->max_grossPay);
}