//********************************************************
//
// Assignment 10 - Linked Lists, Typedef, and Macros
//
// Name: <replace with your name>
//
// Class: C Programming, <replace with Semester and Year>
//
// Date: <replace with the current date>
//
// Description: Program which determines overtime and 
// gross pay for a set of employees with outputs sent 
// to standard output (the screen).
//
//********************************************************

// headers
#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 NAME_SIZE 20
#define TAX_STATE_SIZE 3
#define FIRST_NAME_SIZE 10
#define LAST_NAME_SIZE 10

// macros
#define CALC_OT_HOURS(theHours) ((theHours > STD_HOURS) ? (theHours - STD_HOURS) : 0)

#define CALC_STATE_TAX(thePay,theStateTaxRate) ((thePay) * (theStateTaxRate))

#define CALC_FED_TAX(thePay) ((thePay) * FED_TAX_RATE)

#define CALC_NET_PAY(thePay,theStateTax,theFedTax) \
((thePay) - ((theStateTax) + (theFedTax)))

#define CALC_NORMAL_PAY(theWageRate,theHours,theOvertimeHrs) \
((theWageRate) * ((theHours) - (theOvertimeHrs)))

#define CALC_OT_PAY(theWageRate,theOvertimeHrs) \
((theOvertimeHrs) * (OT_RATE * (theWageRate)))

// FIXED MIN/MAX MACROS
#define CALC_MIN(theValue, currentMin) \
(((theValue) < (currentMin)) ? (theValue) : (currentMin))

#define CALC_MAX(theValue, currentMax) \
(((theValue) > (currentMax)) ? (theValue) : (currentMax))

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

// FIXED typedef MIN_MAX
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 * emp_totals_ptr);

void calcEmployeeMinMax(EMPLOYEE * head_ptr, MIN_MAX * emp_minMax_ptr);

void printEmpStatistics(TOTALS * emp_totals_ptr, MIN_MAX * emp_minMax_ptr, int size);

void printHeader(void);
void printEmp(EMPLOYEE * head_ptr);

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

    TOTALS employeeTotals = {0};
    TOTALS * emp_totals_ptr = &employeeTotals;

    MIN_MAX employeeMinMax = {0};
    MIN_MAX * emp_minMax_ptr = &employeeMinMax;

    head_ptr = getEmpData();

    theSize = isEmployeeSize(head_ptr);

    if (theSize <= 0)
    {
        printf("\n\n**** There was no employee input to process ***\n");
    }
    else
    {
        calcOvertimeHrs(head_ptr);
        calcGrossPay(head_ptr);
        calcStateTax(head_ptr);
        calcFedTax(head_ptr);
        calcNetPay(head_ptr);

        calcEmployeeTotals(head_ptr, emp_totals_ptr);
        calcEmployeeMinMax(head_ptr, emp_minMax_ptr);

        printHeader();
        printEmp(head_ptr);

        printEmpStatistics(emp_totals_ptr, emp_minMax_ptr, theSize);
    }

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

// GET DATA (unchanged)
EMPLOYEE * getEmpData(void)
{
    char answer[80];
    int more_data = 1;
    char value;

    EMPLOYEE *current_ptr, *head_ptr;

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

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

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

        printf("\nEnter employee two character tax state: ");
        scanf("%s", current_ptr->taxState);

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

        printf("\nEnter employee hourly wage: ");
        scanf("%f", &current_ptr->wageRate);

        printf("\nEnter hours worked this week: ");
        scanf("%f", &current_ptr->hours);

        printf("\nWould you like to add another employee? (y/n): ");
        scanf("%s", answer);

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

    return head_ptr;
}

// SIZE
int isEmployeeSize(EMPLOYEE * head_ptr)
{
    int size = 0;
    EMPLOYEE * current;

    if (head_ptr->empName.firstName[0] != '\0')
    {
        for (current = head_ptr; current; current = current->next)
            size++;
    }

    return size;
}

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

// PRINT EMP
void printEmp(EMPLOYEE * head_ptr)
{
    EMPLOYEE * current;
    char name[FIRST_NAME_SIZE + LAST_NAME_SIZE + 2];

    for (current = head_ptr; current; current = current->next)
    {
        strcpy(name, current->empName.firstName);
        strcat(name, " ");
        strcat(name, current->empName.lastName);

        printf("\n%-20s %-2s %06li %5.2f %5.1f %5.1f %7.2f %6.2f %7.2f %8.2f",
            name,
            current->taxState,
            current->clockNumber,
            current->wageRate,
            current->hours,
            current->overtimeHrs,
            current->grossPay,
            current->stateTax,
            current->fedTax,
            current->netPay);
    }
}

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

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

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

// FED TAX FIXED
void calcFedTax(EMPLOYEE * head_ptr)
{
    for (EMPLOYEE * c = head_ptr; c; c = c->next)
        c->fedTax = CALC_FED_TAX(c->grossPay);
}

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

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

// MIN MAX FIXED
void calcEmployeeMinMax(EMPLOYEE * head_ptr, MIN_MAX * m)
{
    EMPLOYEE * c = head_ptr;

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

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

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

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

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

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

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

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

// STATS PRINT
void printEmpStatistics(TOTALS * t, MIN_MAX * m, int size)
{
    printf("\n--------------------------------------------------------------");

    printf("\nTotals...");
    printf("\nAverages...");
    printf("\nMinimum...");
    printf("\nMaximum...");

    printf("\n\nThe total employees processed was: %d\n", size);
}
