//********************************************************
//
// Assignment 10 - Linked Lists, Typedef, and Macros
//
// Name: <Kyle Merrihew>
//
// Class: C Programming, <Spring 2025>
//
// Date: <April 14,2025>
//
// 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.
//
// It will also take advantage of the C Preprocessor features,
// in particular with using macros, and will replace all 
// struct type references in the code with a typedef alias
// reference.
//
// Call by Reference design (using pointers)
//
//********************************************************

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

// Constants
#define MA_TAX_RATE 0.06
#define VT_TAX_RATE 0.04
#define NH_TAX_RATE 0.00
#define CA_TAX_RATE 0.08
#define DEFAULT_STATE_TAX_RATE 0.05
#define FED_TAX_RATE 0.10

#define FIRST_NAME_SIZE 20
#define LAST_NAME_SIZE 30

// Macros
#define CALC_OT_HOURS(hours) ((hours) > 40 ? (hours) - 40 : 0)
#define CALC_NORMAL_PAY(rate, hours, overtime) ((hours) - (overtime)) * (rate)
#define CALC_OT_PAY(rate, overtime) ((overtime) * (rate) * 1.5)
#define CALC_STATE_TAX(grossPay, rate) ((grossPay) * (rate))
#define CALC_FED_TAX(grossPay) ((grossPay) * FED_TAX_RATE)
#define CALC_NET_PAY(grossPay, stateTax, fedTax) ((grossPay) - (stateTax) - (fedTax))
#define CALC_MIN(a, b) ((a) < (b) ? (a) : (b))
#define CALC_MAX(a, b) ((a) > (b) ? (a) : (b))

// Struct Definitions
typedef struct {
    char firstName[FIRST_NAME_SIZE];
    char lastName[LAST_NAME_SIZE];
} Name;

typedef struct employee {
    Name empName;
    char taxState[3];      // State code (2 characters)
    long clockNumber;
    float wageRate;
    float hours;
    float overtimeHrs;
    float grossPay;
    float stateTax;
    float fedTax;
    float netPay;
    struct employee *next;
} EMPLOYEE;

typedef struct {
    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;
    float max_wageRate;
    float min_hours;
    float max_hours;
    float min_overtimeHrs;
    float max_overtimeHrs;
    float min_grossPay;
    float max_grossPay;
    float min_stateTax;
    float max_stateTax;
    float min_fedTax;
    float max_fedTax;
    float min_netPay;
    float max_netPay;
} MIN_MAX;

// Function Prototypes
void initializeEmployee(EMPLOYEE *emp);
void printEmployee(EMPLOYEE *emp);
void calculateEmployeePay(EMPLOYEE *emp);
void calculateTotals(EMPLOYEE *emp, TOTALS *totals);
void printTotals(TOTALS *totals);
void calculateMinMax(EMPLOYEE *emp, MIN_MAX *minMax);
void printMinMax(MIN_MAX *minMax);
float getTaxRate(char *state);

// Main function
int main() {
    EMPLOYEE *head = NULL;
    EMPLOYEE *current = NULL;
    TOTALS totals = {0};
    MIN_MAX minMax = {0};

    char choice;

    do {
        EMPLOYEE *newEmployee = (EMPLOYEE *)malloc(sizeof(EMPLOYEE));
        if (!newEmployee) {
            printf("Memory allocation failed.\n");
            return 1;
        }

        initializeEmployee(newEmployee);

        calculateEmployeePay(newEmployee);
        calculateTotals(newEmployee, &totals);
        calculateMinMax(newEmployee, &minMax);

        // Add employee to linked list
        if (head == NULL) {
            head = newEmployee;
        } else {
            current->next = newEmployee;
        }
        current = newEmployee;

        printf("Do you want to enter another employee? (Y/N): ");
        choice = toupper(getchar());
        getchar();  // To consume the newline character after input

    } while (choice == 'Y');

    // Print all employees
    current = head;
    while (current != NULL) {
        printEmployee(current);
        current = current->next;
    }

    // Print totals and min/max
    printTotals(&totals);
    printMinMax(&minMax);

    return 0;
}

// Function Definitions

void initializeEmployee(EMPLOYEE *emp) {
    printf("Enter employee first name: ");
    fgets(emp->empName.firstName, FIRST_NAME_SIZE, stdin);
    emp->empName.firstName[strcspn(emp->empName.firstName, "\n")] = '\0';  // Remove newline

    printf("Enter employee last name: ");
    fgets(emp->empName.lastName, LAST_NAME_SIZE, stdin);
    emp->empName.lastName[strcspn(emp->empName.lastName, "\n")] = '\0';  // Remove newline

    printf("Enter employee clock number: ");
    scanf("%ld", &emp->clockNumber);

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

    printf("Enter employee hours worked: ");
    scanf("%f", &emp->hours);

    emp->overtimeHrs = CALC_OT_HOURS(emp->hours);
}

void printEmployee(EMPLOYEE *emp) {
    printf("\nEmployee Name: %s %s\n", emp->empName.firstName, emp->empName.lastName);
    printf("Clock Number: %ld\n", emp->clockNumber);
    printf("Wage Rate: %.2f\n", emp->wageRate);
    printf("Hours Worked: %.2f\n", emp->hours);
    printf("Overtime Hours: %.2f\n", emp->overtimeHrs);
    printf("Gross Pay: %.2f\n", emp->grossPay);
    printf("State Tax: %.2f\n", emp->stateTax);
    printf("Federal Tax: %.2f\n", emp->fedTax);
    printf("Net Pay: %.2f\n\n", emp->netPay);
}

void calculateEmployeePay(EMPLOYEE *emp) {
    float overtime = emp->overtimeHrs;
    emp->grossPay = CALC_NORMAL_PAY(emp->wageRate, emp->hours, overtime) + CALC_OT_PAY(emp->wageRate, overtime);
    emp->stateTax = CALC_STATE_TAX(emp->grossPay, getTaxRate(emp->taxState));
    emp->fedTax = CALC_FED_TAX(emp->grossPay);
    emp->netPay = CALC_NET_PAY(emp->grossPay, emp->stateTax, emp->fedTax);
}

void calculateTotals(EMPLOYEE *emp, TOTALS *totals) {
    totals->total_wageRate += emp->wageRate;
    totals->total_hours += emp->hours;
    totals->total_overtimeHrs += emp->overtimeHrs;
    totals->total_grossPay += emp->grossPay;
    totals->total_stateTax += emp->stateTax;
    totals->total_fedTax += emp->fedTax;
    totals->total_netPay += emp->netPay;
}

void printTotals(TOTALS *totals) {
    printf("\nTotal Wage Rate: %.2f\n", totals->total_wageRate);
    printf("Total Hours Worked: %.2f\n", totals->total_hours);
    printf("Total Overtime Hours: %.2f\n", totals->total_overtimeHrs);
    printf("Total Gross Pay: %.2f\n", totals->total_grossPay);
    printf("Total State Tax: %.2f\n", totals->total_stateTax);
    printf("Total Federal Tax: %.2f\n", totals->total_fedTax);
    printf("Total Net Pay: %.2f\n\n", totals->total_netPay);
}

void calculateMinMax(EMPLOYEE *emp, MIN_MAX *minMax) {
    minMax->min_wageRate = (minMax->min_wageRate == 0) ? emp->wageRate : CALC_MIN(minMax->min_wageRate, emp->wageRate);
    minMax->max_wageRate = (minMax->max_wageRate == 0) ? emp->wageRate : CALC_MAX(minMax->max_wageRate, emp->wageRate);
    minMax->min_hours = (minMax->min_hours == 0) ? emp->hours : CALC_MIN(minMax->min_hours, emp->hours);
    minMax->max_hours = (minMax->max_hours == 0) ? emp->hours : CALC_MAX(minMax->max_hours, emp->hours);
    minMax->min_overtimeHrs = (minMax->min_overtimeHrs == 0) ? emp->overtimeHrs : CALC_MIN(minMax->min_overtimeHrs, emp->overtimeHrs);
    minMax->max_overtimeHrs = (minMax->max_overtimeHrs == 0) ? emp->overtimeHrs : CALC_MAX(minMax->max_overtimeHrs, emp->overtimeHrs);
    minMax->min_grossPay = (minMax->min_grossPay == 0) ? emp->grossPay : CALC_MIN(minMax->min_grossPay, emp->grossPay);
    minMax->max_grossPay = (minMax->max_grossPay == 0) ? emp->grossPay : CALC_MAX(minMax->max_grossPay, emp->grossPay);
    minMax->min_stateTax = (minMax->min_stateTax == 0) ? emp->stateTax : CALC_MIN(minMax->min_stateTax, emp->stateTax);
    minMax->max_stateTax = (minMax->max_stateTax == 0) ? emp->stateTax : CALC_MAX(minMax->max_stateTax, emp->stateTax);
    minMax->min_fedTax = (minMax->min_fedTax == 0) ? emp->fedTax : CALC_MIN(minMax->min_fedTax, emp->fedTax);
    minMax->max_fedTax = (minMax->max_fedTax == 0) ? emp->fedTax : CALC_MAX(minMax->max_fedTax, emp->fedTax);
    minMax->min_netPay = (minMax->min_netPay == 0) ? emp->netPay : CALC_MIN(minMax->min_netPay, emp->netPay);
    minMax->max_netPay = (minMax->max_netPay == 0) ? emp->netPay : CALC_MAX(minMax->max_netPay, emp->netPay);
}

void printMinMax(MIN_MAX *minMax) {
    printf("\nMin Wage Rate: %.2f | Max Wage Rate: %.2f\n", minMax->min_wageRate, minMax->max_wageRate);
    printf("Min Hours: %.2f | Max Hours: %.2f\n", minMax->min_hours, minMax->max_hours);
    printf("Min Overtime Hours: %.2f | Max Overtime Hours: %.2f\n", minMax->min_overtimeHrs, minMax->max_overtimeHrs);
    printf("Min Gross Pay: %.2f | Max Gross Pay: %.2f\n", minMax->min_grossPay, minMax->max_grossPay);
    printf("Min State Tax: %.2f | Max State Tax: %.2f\n", minMax->min_stateTax, minMax->max_stateTax);
    printf("Min Federal Tax: %.2f | Max Federal Tax: %.2f\n", minMax->min_fedTax, minMax->max_fedTax);
    printf("Min Net Pay: %.2f | Max Net Pay: %.2f\n", minMax->min_netPay, minMax->max_netPay);
}

float getTaxRate(char *state) {
    if (strcmp(state, "MA") == 0) {
        return MA_TAX_RATE;
    } else if (strcmp(state, "VT") == 0) {
        return VT_TAX_RATE;
    } else if (strcmp(state, "NH") == 0) {
        return NH_TAX_RATE;
    } else if (strcmp(state, "CA") == 0) {
        return CA_TAX_RATE;
    } else {
        return DEFAULT_STATE_TAX_RATE;
    }
}