//********************************************************
//
// 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>

// constants
#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 TAX_STATE_SIZE 3
#define FED_TAX_RATE 0.25
#define FIRST_NAME_SIZE 10
#define LAST_NAME_SIZE 10

// macros
#define CALC_OT_HOURS(h) ((h > STD_HOURS) ? h - STD_HOURS : 0)
#define CALC_STATE_TAX(p,r) (p * r)
#define CALC_FED_TAX(p) (p * FED_TAX_RATE)
#define CALC_NET_PAY(p,s,f) (p - (s + f))
#define CALC_NORMAL_PAY(w,h,o) (w * (h - o))
#define CALC_OT_PAY(w,o) (o * (OT_RATE * w))
#define CALC_MIN(v,m) ((v < m) ? v : m)
#define CALC_MAX(v,m) ((v > m) ? v : m)

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

typedef struct employee {
    struct name empName;
    char taxState[TAX_STATE_SIZE];
    long int clockNumber;
    float wageRate, hours, overtimeHrs;
    float grossPay, stateTax, fedTax, netPay;
    struct employee *next;
} EMPLOYEE;

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

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

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

//********************************************************

int main() {
    EMPLOYEE *head_ptr;
    int size;

    TOTALS totals = {0};
    MIN_MAX minmax = {0};

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

    if (size <= 0) {
        printf("\nNo data.\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\n *** End of Program ***\n");
    return 0;
}

//********************************************************

EMPLOYEE * getEmpData(void) {
    EMPLOYEE *head = NULL, *curr = NULL;
    char cont = 'y';

    while (cont == 'y' || cont == 'Y') {
        EMPLOYEE *node = malloc(sizeof(EMPLOYEE));

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

        node->next = NULL;

        if (!head) head = node;
        else curr->next = node;

        curr = node;

        printf("Add another? (y/n): ");
        scanf(" %c", &cont);
    }
    return head;
}

//********************************************************

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

//********************************************************

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

//********************************************************

void calcGrossPay(EMPLOYEE *h) {
    for (; h; h = h->next)
        h->grossPay =
            CALC_NORMAL_PAY(h->wageRate,h->hours,h->overtimeHrs) +
            CALC_OT_PAY(h->wageRate,h->overtimeHrs);
}

//********************************************************

void calcStateTax(EMPLOYEE *h) {
    for (; h; h = h->next) {
        float rate = DEFAULT_STATE_TAX_RATE;

        if (!strcmp(h->taxState,"MA")) rate = MA_TAX_RATE;
        else if (!strcmp(h->taxState,"VT")) rate = VT_TAX_RATE;
        else if (!strcmp(h->taxState,"NH")) rate = NH_TAX_RATE;
        else if (!strcmp(h->taxState,"CA")) rate = CA_TAX_RATE;

        h->stateTax = CALC_STATE_TAX(h->grossPay, rate);
    }
}

//********************************************************

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

//********************************************************

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

//********************************************************

void calcEmployeeTotals(EMPLOYEE *h, TOTALS *t) {
    for (; h; h = h->next) {
        t->total_wageRate += h->wageRate;
        t->total_hours += h->hours;
        t->total_overtimeHrs += h->overtimeHrs;
        t->total_grossPay += h->grossPay;
        t->total_stateTax += h->stateTax;
        t->total_fedTax += h->fedTax;
        t->total_netPay += h->netPay;
    }
}

//********************************************************

void calcEmployeeMinMax(EMPLOYEE *h, MIN_MAX *m) {
    *m = (MIN_MAX){
        h->wageRate,h->hours,h->overtimeHrs,h->grossPay,h->stateTax,h->fedTax,h->netPay,
        h->wageRate,h->hours,h->overtimeHrs,h->grossPay,h->stateTax,h->fedTax,h->netPay
    };

    for (h = h->next; h; h = h->next) {
        m->min_wageRate = CALC_MIN(h->wageRate,m->min_wageRate);
        m->max_wageRate = CALC_MAX(h->wageRate,m->max_wageRate);

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

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

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

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

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

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

//********************************************************

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

//********************************************************

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

//********************************************************

void printEmpStatistics(TOTALS *t, MIN_MAX *m, int n) {
    printf("\nTotals Net: %.2f", t->total_netPay);
    printf("\nMin Net: %.2f", m->min_netPay);
    printf("\nMax Net: %.2f\n", m->max_netPay);
}