//********************************************************
// Assignment 7 - Structures and Strings
// Name: Robert Hyde
// Class: C Programming, Fall 2026
// Date: 10/30/2026
//
// 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.
//
// Call by Reference design
//********************************************************

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

#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 FED_TAX_RATE 0.25
#define FIRST_NAME_SIZE 10
#define LAST_NAME_SIZE 10
#define TAX_STATE_SIZE 3

struct name {
    char firstName[FIRST_NAME_SIZE];
    char lastName[LAST_NAME_SIZE];
};

struct employee {
    struct name empName;
    char taxState[TAX_STATE_SIZE];
    long int clockNumber;
    float wageRate, hours, overtimeHrs;
    float grossPay, stateTax, fedTax, netPay;
};

struct totals {
    float total_wageRate, total_hours, total_overtimeHrs;
    float total_grossPay, total_stateTax, total_fedTax, total_netPay;
};

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

void getHours(struct employee data[], int size);
void calcOvertimeHrs(struct employee data[], int size);
void calcGrossPay(struct employee data[], int size);
void calcStateTax(struct employee data[], int size);
void calcFedTax(struct employee data[], int size);
void calcNetPay(struct employee data[], int size);
void printHeader(void);
void printEmp(struct employee data[], int size);

struct totals calcEmployeeTotals(
    struct employee data[], struct totals totals, int size);

struct min_max calcEmployeeMinMax(
    struct employee data[], struct min_max mm, int size);

void printEmpStatistics(struct totals totals, struct min_max mm, int size);

int main(void)
{
    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}
    };

    struct totals employeeTotals = {0};
    struct min_max employeeMinMax = {0};

    getHours(employeeData, SIZE);
    calcOvertimeHrs(employeeData, SIZE);
    calcGrossPay(employeeData, SIZE);
    calcStateTax(employeeData, SIZE);
    calcFedTax(employeeData, SIZE);
    calcNetPay(employeeData, SIZE);

    employeeTotals = calcEmployeeTotals(employeeData, employeeTotals, SIZE);
    employeeMinMax = calcEmployeeMinMax(employeeData, employeeMinMax, SIZE);

    printHeader();
    printEmp(employeeData, SIZE);
    printEmpStatistics(employeeTotals, employeeMinMax, SIZE);

    printf("\n");
    return 0;
}

void getHours(struct employee data[], int size)
{
    int i;
    for (i = 0; i < size; i++) {
        printf("\nEnter hours worked by emp # %06li: ", data[i].clockNumber);
        scanf("%f", &data[i].hours);
    }
}

void calcOvertimeHrs(struct employee data[], int size)
{
    int i;
    for (i = 0; i < size; i++) {
        data[i].overtimeHrs =
            data[i].hours > STD_HOURS ? data[i].hours - STD_HOURS : 0.0;
    }
}

void calcGrossPay(struct employee data[], int size)
{
    int i;
    float normalPay, overtimePay;

    for (i = 0; i < size; i++) {
        normalPay = data[i].wageRate *
                    (data[i].hours - data[i].overtimeHrs);
        overtimePay = data[i].overtimeHrs * OT_RATE * data[i].wageRate;
        data[i].grossPay = normalPay + overtimePay;
    }
}

void calcStateTax(struct employee data[], int size)
{
    int i;
    float rate;

    for (i = 0; i < size; i++) {
        data[i].taxState[0] =
            (char)toupper((unsigned char)data[i].taxState[0]);
        data[i].taxState[1] =
            (char)toupper((unsigned char)data[i].taxState[1]);

        if (strcmp(data[i].taxState, "MA") == 0)
            rate = MA_TAX_RATE;
        else if (strcmp(data[i].taxState, "NH") == 0)
            rate = NH_TAX_RATE;
        else if (strcmp(data[i].taxState, "VT") == 0)
            rate = VT_TAX_RATE;
        else if (strcmp(data[i].taxState, "CA") == 0)
            rate = CA_TAX_RATE;
        else
            rate = DEFAULT_TAX_RATE;

        data[i].stateTax = data[i].grossPay * rate;
    }
}

void calcFedTax(struct employee data[], int size)
{
    int i;
    for (i = 0; i < size; i++)
        data[i].fedTax = data[i].grossPay * FED_TAX_RATE;
}

void calcNetPay(struct employee data[], int size)
{
    int i;
    for (i = 0; i < size; i++)
        data[i].netPay = data[i].grossPay - data[i].stateTax - data[i].fedTax;
}

void printHeader(void)
{
    printf("\n\n*** Pay Calculator ***\n");
    printf("\n--------------------------------------------------------------");
    printf("-------------------");
    printf("\nName                Tax  Clock# Wage   Hours  OT   Gross ");
    printf("  State  Fed      Net");
    printf("\n                    State                           Pay     ");
    printf("Tax    Tax      Pay");
    printf("\n--------------------------------------------------------------");
    printf("-------------------");
}

void printEmp(struct employee data[], int size)
{
    int i;
    char name[FIRST_NAME_SIZE + LAST_NAME_SIZE + 1];

    for (i = 0; i < size; i++) {
        strcpy(name, data[i].empName.firstName);
        strcat(name, " ");
        strcat(name, data[i].empName.lastName);

        printf("\n%-20.20s %-2.2s  %06li %5.2f  %4.1f  %4.1f "
               "%7.2f %6.2f %7.2f %8.2f",
               name, data[i].taxState, data[i].clockNumber,
               data[i].wageRate, data[i].hours, data[i].overtimeHrs,
               data[i].grossPay, data[i].stateTax, data[i].fedTax,
               data[i].netPay);
    }
}

struct totals calcEmployeeTotals(
    struct employee data[], struct totals t, int size)
{
    int i;
    for (i = 0; i < size; i++) {
        t.total_wageRate += data[i].wageRate;
        t.total_hours += data[i].hours;
        t.total_overtimeHrs += data[i].overtimeHrs;
        t.total_grossPay += data[i].grossPay;
        t.total_stateTax += data[i].stateTax;
        t.total_fedTax += data[i].fedTax;
        t.total_netPay += data[i].netPay;
    }
    return t;
}

struct min_max calcEmployeeMinMax(
    struct employee data[], struct min_max m, int size)
{
    int i;

    if (size <= 0)
        return m;

    m.min_wageRate = m.max_wageRate = data[0].wageRate;
    m.min_hours = m.max_hours = data[0].hours;
    m.min_overtimeHrs = m.max_overtimeHrs = data[0].overtimeHrs;
    m.min_grossPay = m.max_grossPay = data[0].grossPay;
    m.min_stateTax = m.max_stateTax = data[0].stateTax;
    m.min_fedTax = m.max_fedTax = data[0].fedTax;
    m.min_netPay = m.max_netPay = data[0].netPay;

    for (i = 1; i < size; i++) {
        if (data[i].wageRate < m.min_wageRate) m.min_wageRate = data[i].wageRate;
        if (data[i].wageRate > m.max_wageRate) m.max_wageRate = data[i].wageRate;

        if (data[i].hours < m.min_hours) m.min_hours = data[i].hours;
        if (data[i].hours > m.max_hours) m.max_hours = data[i].hours;

        if (data[i].overtimeHrs < m.min_overtimeHrs) m.min_overtimeHrs = data[i].overtimeHrs;
        if (data[i].overtimeHrs > m.max_overtimeHrs) m.max_overtimeHrs = data[i].overtimeHrs;

        if (data[i].grossPay < m.min_grossPay) m.min_grossPay = data[i].grossPay;
        if (data[i].grossPay > m.max_grossPay) m.max_grossPay = data[i].grossPay;

        if (data[i].stateTax < m.min_stateTax) m.min_stateTax = data[i].stateTax;
        if (data[i].stateTax > m.max_stateTax) m.max_stateTax = data[i].stateTax;

        if (data[i].fedTax < m.min_fedTax) m.min_fedTax = data[i].fedTax;
        if (data[i].fedTax > m.max_fedTax) m.max_fedTax = data[i].fedTax;

        if (data[i].netPay < m.min_netPay) m.min_netPay = data[i].netPay;
        if (data[i].netPay > m.max_netPay) m.max_netPay = data[i].netPay;
    }
    return m;
}

void printEmpStatistics(struct totals t, struct min_max m, int size)
{
    printf("\n--------------------------------------------------------------");
    printf("-------------------");

    printf("\nTotals:                          %5.2f %5.1f %5.1f %7.2f %6.2f %7.2f %8.2f",
           t.total_wageRate, t.total_hours, t.total_overtimeHrs,
           t.total_grossPay, t.total_stateTax, t.total_fedTax, t.total_netPay);

    if (size > 0) {
        printf("\nAverages:                        %5.2f %5.1f %5.1f %7.2f %6.2f %7.2f %8.2f",
               t.total_wageRate / size, t.total_hours / size,
               t.total_overtimeHrs / size, t.total_grossPay / size,
               t.total_stateTax / size, t.total_fedTax / size,
               t.total_netPay / size);
    }

    printf("\nMinimum:                         %5.2f %5.1f %5.1f %7.2f %6.2f %7.2f %8.2f",
           m.min_wageRate, m.min_hours, m.min_overtimeHrs,
           m.min_grossPay, m.min_stateTax, m.min_fedTax, m.min_netPay);

    printf("\nMaximum:                         %5.2f %5.1f %5.1f %7.2f %6.2f %7.2f %8.2f",
           m.max_wageRate, m.max_hours, m.max_overtimeHrs,
           m.max_grossPay, m.max_stateTax, m.max_fedTax, m.max_netPay);
}