//********************************************************
//
// Assignment 8 - Structures and Strings and Pointers
//
// Name: Tim Ticknor
//
// Class: C Programming, Fall, 2024
//
// Date: 11/10/24
//
// 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 are to be replaced with
// pointer references to speed up the processing of this code.
//
// Call by Reference design (using pointers)
//
//********************************************************
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <float.h>  // For FLT_MAX

// Define constants
#define SIZE 5
#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_TAX_RATE 0.08
#define FIRST_NAME_SIZE 10
#define LAST_NAME_SIZE 10
#define TAX_STATE_SIZE 3
#define FED_TAX_RATE 0.25

// Define a structure type to store an employee name
struct name {
    char firstName[FIRST_NAME_SIZE];
    char lastName [LAST_NAME_SIZE];
};

// Define a structure type to store employee data
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;
};

// This structure type defines the totals of all floating point items
struct totals {
    float total_wageRate;
    float total_hours;
    float total_overtimeHrs;
    float total_grossPay;
    float total_stateTax;
    float total_fedTax;
    float total_netPay;
};

// This structure type defines the min and max values for employee data
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;
};

// Function prototypes
void getHours(struct employee *emp_ptr, int theSize);
void printEmp(struct employee *emp_ptr, int theSize);
void calcEmployeeTotals(struct employee *emp_ptr, struct totals *emp_totals_ptr, int theSize);
void calcEmployeeMinMax(struct employee *emp_ptr, struct min_max *emp_MinMax_ptr, int theSize);
void printHeader(void);
void calcOvertimeHrs(struct employee *emp_ptr, int theSize);
void calcGrossPay(struct employee *emp_ptr, int theSize);
void calcStateTax(struct employee *emp_ptr, int theSize);
void calcFedTax(struct employee *emp_ptr, int theSize);
void calcNetPay(struct employee *emp_ptr, int theSize);
void printEmpStatistics(struct totals *emp_totals_ptr, struct min_max *emp_MinMax_ptr, int theSize);

int main() {
    // Set up a local variable to store the employee information
    struct employee employeeData[SIZE] = {
        { {"Connie", "Cobol"}, "MA", 98401, 10.60},
        { {"Mary", "Apl"}, "NH", 526488, 9.75 },
        { {"Frank", "Fortran"}, "VT", 765349, 10.50 },
        { {"Jeff", "Ada"}, "NY", 34645, 12.25 },
        { {"Anton", "Pascal"},"CA",127615, 8.35 }
    };

    // Declare a pointer to the array of employee structures
    struct employee *emp_ptr = employeeData;

    // Set up structure to store totals and initialize all to zero
    struct totals employeeTotals  = {0, 0, 0, 0, 0, 0, 0};
    struct totals *emp_totals_ptr = &employeeTotals;

    // Set up structure to store min and max values and initialize all to zero or appropriate extreme values
    struct min_max employeeMinMax = {
        FLT_MAX,  // min_wageRate
        FLT_MAX,  // min_hours
        FLT_MAX,  // min_overtimeHrs
        FLT_MAX,  // min_grossPay
        FLT_MAX,  // min_stateTax
        FLT_MAX,  // min_fedTax
        FLT_MAX,  // min_netPay
        0.0,      // max_wageRate
        0.0,      // max_hours
        0.0,      // max_overtimeHrs
        0.0,      // max_grossPay
        0.0,      // max_stateTax
        0.0,      // max_fedTax
        0.0       // max_netPay
    };
    struct min_max *emp_minMax_ptr = &employeeMinMax;

    // Call functions as needed to read and calculate information
    getHours(emp_ptr, SIZE);
    calcOvertimeHrs(emp_ptr, SIZE);
    calcGrossPay(emp_ptr, SIZE);
    calcStateTax(emp_ptr, SIZE);
    calcFedTax(emp_ptr, SIZE);
    calcNetPay(emp_ptr, SIZE);

    calcEmployeeTotals(emp_ptr, emp_totals_ptr, SIZE);
    calcEmployeeMinMax(emp_ptr, emp_minMax_ptr, SIZE);

    // Print the column headers
    printHeader();

    // Print out final information on each employee
    printEmp(emp_ptr, SIZE);

    // Print the totals and averages for all float items
    printEmpStatistics(emp_totals_ptr, emp_minMax_ptr, SIZE);

    return 0;
}

// Function Definitions

// Function to get hours worked from user input
void getHours(struct employee *emp_ptr, int theSize) {
    for (int i = 0; i < theSize; i++) {
        printf("Enter hours worked for %s %s (Employee #%ld): ", emp_ptr[i].empName.firstName, emp_ptr[i].empName.lastName, emp_ptr[i].clockNumber);
        if (scanf("%f", &emp_ptr[i].hours) != 1) {
            printf("Invalid input. Please enter a valid number for hours.\n");
            i--;  // Decrement the counter to retry the input for this employee
        }
    }
}

// Function to calculate overtime hours
void calcOvertimeHrs(struct employee *emp_ptr, int theSize) {
    for (int i = 0; i < theSize; i++) {
        if (emp_ptr[i].hours > STD_HOURS) {
            emp_ptr[i].overtimeHrs = emp_ptr[i].hours - STD_HOURS;
        } else {
            emp_ptr[i].overtimeHrs = 0.0;
        }
    }
}

// Function to calculate gross pay
void calcGrossPay(struct employee *emp_ptr, int theSize) {
    for (int i = 0; i < theSize; i++) {
        if (emp_ptr[i].hours <= STD_HOURS) {
            emp_ptr[i].grossPay = emp_ptr[i].wageRate * emp_ptr[i].hours;
        } else {
            emp_ptr[i].grossPay = (STD_HOURS * emp_ptr[i].wageRate) + (emp_ptr[i].overtimeHrs * emp_ptr[i].wageRate * OT_RATE);
        }
    }
}

// Function to calculate state tax
void calcStateTax(struct employee *emp_ptr, int theSize) {
    for (int i = 0; i < theSize; i++) {
        if (strcmp(emp_ptr[i].taxState, "MA") == 0) {
            emp_ptr[i].stateTax = emp_ptr[i].grossPay * MA_TAX_RATE;
        } else if (strcmp(emp_ptr[i].taxState, "NH") == 0) {
            emp_ptr[i].stateTax = emp_ptr[i].grossPay * NH_TAX_RATE;
        } else if (strcmp(emp_ptr[i].taxState, "VT") == 0) {
            emp_ptr[i].stateTax = emp_ptr[i].grossPay * VT_TAX_RATE;
        } else if (strcmp(emp_ptr[i].taxState, "CA") == 0) {
            emp_ptr[i].stateTax = emp_ptr[i].grossPay * CA_TAX_RATE;
        } else {
            emp_ptr[i].stateTax = emp_ptr[i].grossPay * DEFAULT_TAX_RATE;
        }
    }
}

// Function to calculate federal tax
void calcFedTax(struct employee *emp_ptr, int theSize) {
    for (int i = 0; i < theSize; i++) {
        emp_ptr[i].fedTax = emp_ptr[i].grossPay * FED_TAX_RATE;
    }
}

// Function to calculate net pay
void calcNetPay(struct employee *emp_ptr, int theSize) {
    for (int i = 0; i < theSize; i++) {
        emp_ptr[i].netPay = emp_ptr[i].grossPay - emp_ptr[i].stateTax - emp_ptr[i].fedTax;
    }
}

// Function to calculate totals
void calcEmployeeTotals(struct employee *emp_ptr, struct totals *emp_totals_ptr, int theSize) {
    for (int i = 0; i < theSize; i++) {
        emp_totals_ptr->total_wageRate += emp_ptr[i].wageRate;
        emp_totals_ptr->total_hours += emp_ptr[i].hours;
        emp_totals_ptr->total_overtimeHrs += emp_ptr[i].overtimeHrs;
        emp_totals_ptr->total_grossPay += emp_ptr[i].grossPay;
        emp_totals_ptr->total_stateTax += emp_ptr[i].stateTax;
        emp_totals_ptr->total_fedTax += emp_ptr[i].fedTax;
        emp_totals_ptr->total_netPay += emp_ptr[i].netPay;
    }
}

// Function to calculate min and max values for each item
void calcEmployeeMinMax(struct employee *emp_ptr, struct min_max *emp_MinMax_ptr, int theSize) {
    for (int i = 0; i < theSize; i++) {
        if (emp_ptr[i].wageRate < emp_MinMax_ptr->min_wageRate) emp_MinMax_ptr->min_wageRate = emp_ptr[i].wageRate;
        if (emp_ptr[i].wageRate > emp_MinMax_ptr->max_wageRate) emp_MinMax_ptr->max_wageRate = emp_ptr[i].wageRate;

        if (emp_ptr[i].hours < emp_MinMax_ptr->min_hours) emp_MinMax_ptr->min_hours = emp_ptr[i].hours;
        if (emp_ptr[i].hours > emp_MinMax_ptr->max_hours) emp_MinMax_ptr->max_hours = emp_ptr[i].hours;

        if (emp_ptr[i].overtimeHrs < emp_MinMax_ptr->min_overtimeHrs) emp_MinMax_ptr->min_overtimeHrs = emp_ptr[i].overtimeHrs;
        if (emp_ptr[i].overtimeHrs > emp_MinMax_ptr->max_overtimeHrs) emp_MinMax_ptr->max_overtimeHrs = emp_ptr[i].overtimeHrs;

        if (emp_ptr[i].grossPay < emp_MinMax_ptr->min_grossPay) emp_MinMax_ptr->min_grossPay = emp_ptr[i].grossPay;
        if (emp_ptr[i].grossPay > emp_MinMax_ptr->max_grossPay) emp_MinMax_ptr->max_grossPay = emp_ptr[i].grossPay;

        if (emp_ptr[i].stateTax < emp_MinMax_ptr->min_stateTax) emp_MinMax_ptr->min_stateTax = emp_ptr[i].stateTax;
        if (emp_ptr[i].stateTax > emp_MinMax_ptr->max_stateTax) emp_MinMax_ptr->max_stateTax = emp_ptr[i].stateTax;

        if (emp_ptr[i].fedTax < emp_MinMax_ptr->min_fedTax) emp_MinMax_ptr->min_fedTax = emp_ptr[i].fedTax;
        if (emp_ptr[i].fedTax > emp_MinMax_ptr->max_fedTax) emp_MinMax_ptr->max_fedTax = emp_ptr[i].fedTax;

        if (emp_ptr[i].netPay < emp_MinMax_ptr->min_netPay) emp_MinMax_ptr->min_netPay = emp_ptr[i].netPay;
        if (emp_ptr[i].netPay > emp_MinMax_ptr->max_netPay) emp_MinMax_ptr->max_netPay = emp_ptr[i].netPay;
    }
}

// Function to print the header for output
void printHeader(void) {
    printf("\n%-20s %-4s %-7s %-5s %-5s %-3s %-6s %-6s %-6s %-6s\n", "Name", "Tax", "Clock#", "Wage", "Hours", "OT", "Gross", "State", "Fed", "Net");
    printf("----------------------------------------------------------------------------------------\n");
}

// Function to print employee data
void printEmp(struct employee *emp_ptr, int theSize) {
    for (int i = 0; i < theSize; i++) {
        printf("%-20s %-4s %-7ld %-5.2f %-5.1f %-3.1f %-6.2f %-6.2f %-6.2f %-6.2f\n",
               emp_ptr[i].empName.firstName, emp_ptr[i].empName.lastName, emp_ptr[i].clockNumber,
               emp_ptr[i].wageRate, emp_ptr[i].hours, emp_ptr[i].overtimeHrs, emp_ptr[i].grossPay,
               emp_ptr[i].stateTax, emp_ptr[i].fedTax, emp_ptr[i].netPay);
    }
}

// Function to print total, averages, min and max values
void printEmpStatistics(struct totals *emp_totals_ptr, struct min_max *emp_MinMax_ptr, int theSize) {
    printf("\nTotals:\n");
    printf("Wage: %.2f  Hours: %.1f  OT: %.1f  Gross: %.2f  State: %.2f  Fed: %.2f  Net: %.2f\n",
           emp_totals_ptr->total_wageRate, emp_totals_ptr->total_hours, emp_totals_ptr->total_overtimeHrs,
           emp_totals_ptr->total_grossPay, emp_totals_ptr->total_stateTax, emp_totals_ptr->total_fedTax,
           emp_totals_ptr->total_netPay);

    printf("\nAverages:\n");
    printf("Wage: %.2f  Hours: %.1f  OT: %.1f  Gross: %.2f  State: %.2f  Fed: %.2f  Net: %.2f\n",
           emp_totals_ptr->total_wageRate / theSize,
           emp_totals_ptr->total_hours / theSize,
           emp_totals_ptr->total_overtimeHrs / theSize,
           emp_totals_ptr->total_grossPay / theSize,
           emp_totals_ptr->total_stateTax / theSize,
           emp_totals_ptr->total_fedTax / theSize,
           emp_totals_ptr->total_netPay / theSize);

    printf("\nMinimums:\n");
    printf("Wage: %.2f  Hours: %.1f  OT: %.1f  Gross: %.2f  State: %.2f  Fed: %.2f  Net: %.2f\n",
           emp_MinMax_ptr->min_wageRate, emp_MinMax_ptr->min_hours, emp_MinMax_ptr->min_overtimeHrs,
           emp_MinMax_ptr->min_grossPay, emp_MinMax_ptr->min_stateTax, emp_MinMax_ptr->min_fedTax,
           emp_MinMax_ptr->min_netPay);

    printf("\nMaximums:\n");
    printf("Wage: %.2f  Hours: %.1f  OT: %.1f  Gross: %.2f  State: %.2f  Fed: %.2f  Net: %.2f\n",
           emp_MinMax_ptr->max_wageRate, emp_MinMax_ptr->max_hours, emp_MinMax_ptr->max_overtimeHrs,
           emp_MinMax_ptr->max_grossPay, emp_MinMax_ptr->max_stateTax, emp_MinMax_ptr->max_fedTax,
           emp_MinMax_ptr->max_netPay);
}