//********************************************************
//
// Assignment 10 - Linked Lists, Typedef, and Macros
//
// Name: John Semenuk
//
// Class: C Programming, Spring 2026
//
// Date: April 20, 2026
//
// Description: Program which determines overtime and 
// gross pay for a set of employees with outputs sent 
// to standard output (the screen).
//
// Also calculates taxes, totals, averages, min, and max
// using linked lists, pointers, and macros.
//
//********************************************************

#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(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,ot) ((w)*((h)-(ot)))
#define CALC_OT_PAY(w,ot) ((ot)*(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;
    float hours;
    float overtimeHrs;
    float grossPay;
    float stateTax;
    float fedTax;
    float netPay;
    struct employee *next;
} EMPLOYEE;

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;

typedef 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;
} 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 *t);
void calcEmployeeMinMax(EMPLOYEE *head_ptr, MIN_MAX *m);

void printHeader(void);
void printEmp(EMPLOYEE *head_ptr);
void printEmpStatistics(TOTALS *t, MIN_MAX *m, int size);

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

    TOTALS employeeTotals = {0,0,0,0,0,0,0};
    MIN_MAX employeeMinMax = {0};
    MIN_MAX *emp_minMax_ptr = &employeeMinMax;

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

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

    calcOvertimeHrs(head_ptr);
    calcGrossPay(head_ptr);
    calcStateTax(head_ptr);
    calcFedTax(head_ptr);
    calcNetPay(head_ptr);

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

    printHeader();
    printEmp(head_ptr);
    printEmpStatistics(&employeeTotals, emp_minMax_ptr, size);

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

//**************************************************************
// Input Function
//**************************************************************
EMPLOYEE *getEmpData(void)
{
    char answer[80];
    int more_data = 1;
    char value;

    EMPLOYEE *current_ptr, *head_ptr;

    head_ptr = (EMPLOYEE *)malloc(sizeof(EMPLOYEE));
    current_ptr = head_ptr;

    while (more_data)
    {
        printf("\nEnter employee first name: ");
        scanf("%s", current_ptr->empName.firstName);

        printf("Enter employee last name: ");
        scanf("%s", current_ptr->empName.lastName);

        printf("Enter tax state: ");
        scanf("%s", current_ptr->taxState);

        printf("Enter clock number: ");
        scanf("%li", &current_ptr->clockNumber);

        printf("Enter wage rate: ");
        scanf("%f", &current_ptr->wageRate);

        printf("Enter hours: ");
        scanf("%f", &current_ptr->hours);

        printf("Add another employee? (y/n): ");
        scanf("%s", answer);

        value = toupper(answer[0]);

        if (value != 'Y')
        {
            current_ptr->next = NULL;
            more_data = 0;
        }
        else
        {
            current_ptr->next = (EMPLOYEE *)malloc(sizeof(EMPLOYEE));
            current_ptr = current_ptr->next;
        }
    }

    return head_ptr;
}

//**************************************************************
int isEmployeeSize(EMPLOYEE *head_ptr)
{
    int count = 0;
    EMPLOYEE *cur = head_ptr;

    while (cur != NULL)
    {
        count++;
        cur = cur->next;
    }
    return count;
}

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

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

//**************************************************************
void calcStateTax(EMPLOYEE *head_ptr)
{
    for (EMPLOYEE *cur = head_ptr; cur; cur = cur->next)
    {
        if (strcmp(cur->taxState, "MA") == 0)
            cur->stateTax = CALC_STATE_TAX(cur->grossPay, MA_TAX_RATE);
        else if (strcmp(cur->taxState, "VT") == 0)
            cur->stateTax = CALC_STATE_TAX(cur->grossPay, VT_TAX_RATE);
        else if (strcmp(cur->taxState, "NH") == 0)
            cur->stateTax = CALC_STATE_TAX(cur->grossPay, NH_TAX_RATE);
        else if (strcmp(cur->taxState, "CA") == 0)
            cur->stateTax = CALC_STATE_TAX(cur->grossPay, CA_TAX_RATE);
        else
            cur->stateTax = CALC_STATE_TAX(cur->grossPay, DEFAULT_STATE_TAX_RATE);
    }
}

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

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

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

//**************************************************************
void calcEmployeeMinMax(EMPLOYEE *head_ptr, MIN_MAX *m)
{
    EMPLOYEE *cur = head_ptr;

    m->min_wageRate = m->max_wageRate = cur->wageRate;
    m->min_hours = m->max_hours = cur->hours;
    m->min_overtimeHrs = m->max_overtimeHrs = cur->overtimeHrs;
    m->min_grossPay = m->max_grossPay = cur->grossPay;
    m->min_stateTax = m->max_stateTax = cur->stateTax;
    m->min_fedTax = m->max_fedTax = cur->fedTax;
    m->min_netPay = m->max_netPay = cur->netPay;

    cur = cur->next;

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

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

//**************************************************************
void printEmp(EMPLOYEE *head_ptr)
{
    for (EMPLOYEE *cur = head_ptr; cur; cur = cur->next)
        printf("%s %s %.2f %.2f %.2f %.2f\n",
               cur->empName.firstName,
               cur->empName.lastName,
               cur->wageRate,
               cur->hours,
               cur->grossPay,
               cur->netPay);
}

//**************************************************************
void printEmpStatistics(TOTALS *t, MIN_MAX *m, int size)
{
    printf("\nTotals: %.2f %.2f %.2f %.2f %.2f %.2f %.2f",
           t->total_wageRate,
           t->total_hours,
           t->total_overtimeHrs,
           t->total_grossPay,
           t->total_stateTax,
           t->total_fedTax,
           t->total_netPay);

    printf("\nEmployees: %d\n", size);
}
