//********************************************************
//
// Assignment 10 - Linked Lists, Typedef, and Macros
//
// Name: <your name>
// Class: C Programming, <Semester Year>
// Date: <date>
//
// Description: Linked list payroll system using pointers,
// typedefs, and macros.
//
//********************************************************

#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 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(pay, rate) ((pay) * (rate))

#define CALC_FED_TAX(pay) ((pay) * FED_TAX_RATE)

#define CALC_NET_PAY(pay, stateTax, fedTax) ((pay) - ((stateTax) + (fedTax)))

#define CALC_NORMAL_PAY(rate, hours, ot) ((rate) * ((hours) - (ot)))

#define CALC_OT_PAY(rate, ot) ((ot) * (OT_RATE * (rate)))

#define CALC_MIN(val, currentMin) ((val) < (currentMin) ? (val) : (currentMin))

#define CALC_MAX(val, currentMax) ((val) > (currentMax) ? (val) : (currentMax))

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

// =========================
// MAIN
// =========================
int main() {

    EMPLOYEE *head_ptr;
    int size;

    TOTALS totals = {0};
    TOTALS *totals_ptr = &totals;

    MIN_MAX minmax = {0};
    MIN_MAX *minmax_ptr = &minmax;

    head_ptr = getEmpData();

    size = isEmployeeSize(head_ptr);

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

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

    calcEmployeeTotals(head_ptr, totals_ptr);
    calcEmployeeMinMax(head_ptr, minmax_ptr);

    printHeader();
    printEmp(head_ptr);
    printEmpStatistics(totals_ptr, minmax_ptr, size);

    printf("\n\n*** End of Program ***\n");

    return 0;
}

// =========================
// INPUT FUNCTION
// =========================
EMPLOYEE *getEmpData(void) {

    EMPLOYEE *head, *current;
    char answer[10];
    int more = 1;

    head = malloc(sizeof(EMPLOYEE));
    current = head;

    while (more) {

        printf("\nFirst name: ");
        scanf("%s", current->empName.firstName);

        printf("Last name: ");
        scanf("%s", current->empName.lastName);

        printf("State: ");
        scanf("%s", current->taxState);

        printf("Clock #: ");
        scanf("%li", &current->clockNumber);

        printf("Wage: ");
        scanf("%f", &current->wageRate);

        printf("Hours: ");
        scanf("%f", &current->hours);

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

        if (toupper(answer[0]) != 'Y') {
            current->next = NULL;
            more = 0;
        } else {
            current->next = malloc(sizeof(EMPLOYEE));
            current = current->next;
        }
    }

    return head;
}

// =========================
// SIZE
// =========================
int isEmployeeSize(EMPLOYEE *head_ptr) {

    int count = 0;
    EMPLOYEE *p = head_ptr;

    while (p) {
        count++;
        p = p->next;
    }

    return count;
}

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

void calcGrossPay(EMPLOYEE *p) {
    float normal, ot;

    while (p) {
        normal = CALC_NORMAL_PAY(p->wageRate, p->hours, p->overtimeHrs);
        ot = CALC_OT_PAY(p->wageRate, p->overtimeHrs);
        p->grossPay = normal + ot;
        p = p->next;
    }
}

void calcStateTax(EMPLOYEE *p) {
    while (p) {

        if (strcmp(p->taxState, "MA") == 0)
            p->stateTax = CALC_STATE_TAX(p->grossPay, MA_TAX_RATE);
        else if (strcmp(p->taxState, "VT") == 0)
            p->stateTax = CALC_STATE_TAX(p->grossPay, VT_TAX_RATE);
        else if (strcmp(p->taxState, "NH") == 0)
            p->stateTax = 0;
        else if (strcmp(p->taxState, "CA") == 0)
            p->stateTax = CALC_STATE_TAX(p->grossPay, CA_TAX_RATE);
        else
            p->stateTax = CALC_STATE_TAX(p->grossPay, DEFAULT_STATE_TAX_RATE);

        p = p->next;
    }
}

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

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

// =========================
// TOTALS
// =========================
void calcEmployeeTotals(EMPLOYEE *p, TOTALS *t) {

    while (p) {
        t->total_wageRate += p->wageRate;
        t->total_hours += p->hours;
        t->total_overtimeHrs += p->overtimeHrs;
        t->total_grossPay += p->grossPay;
        t->total_stateTax += p->stateTax;
        t->total_fedTax += p->fedTax;
        t->total_netPay += p->netPay;
        p = p->next;
    }
}

// =========================
// MIN / MAX
// =========================
void calcEmployeeMinMax(EMPLOYEE *p, MIN_MAX *m) {

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

    p = p->next;

    while (p) {

        m->min_wageRate = CALC_MIN(p->wageRate, m->min_wageRate);
        m->max_wageRate = CALC_MAX(p->wageRate, m->max_wageRate);

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

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

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

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

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

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

        p = p->next;
    }
}

// =========================
// OUTPUT
// =========================
void printHeader(void) {
    printf("\nPAYROLL REPORT\n");
    printf("------------------------------------------------------------\n");
}

void printEmp(EMPLOYEE *p) {

    char name[25];

    while (p) {
        sprintf(name, "%s %s", p->empName.firstName, p->empName.lastName);

        printf("\n%-15s %-2s %6ld %6.2f %5.1f %5.1f %8.2f %7.2f %7.2f %8.2f",
               name, p->taxState, p->clockNumber,
               p->wageRate, p->hours, p->overtimeHrs,
               p->grossPay, p->stateTax, p->fedTax, p->netPay);

        p = p->next;
    }
}

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

    printf("\n\nTOTALS & STATS\n");
    printf("Employees: %d\n", size);
}
