/**
 * @file lubeapp.cpp
 *
 * @brief Implementation file for the ChainLube application logic.
 *
 * This file contains the implementation of the ChainLube application logic, including functions
 * for running the main application, initiating lubrication pulses, and maintaining system status.
 * Global variables related to lubrication pulses are also defined in this file.
 *
 * @author Marcel Peterkau
 * @date   09.01.2024
 */

#include "lubeapp.h"

uint32_t lubePulseTimestamp = 0;

/**
 * @brief Runs the main logic of the ChainLube application based on the current system status.
 *
 * This function is responsible for executing the main logic of the ChainLube application. It calculates
 * the tank percentage, maintains Diagnostic Trouble Codes (DTCs) related to the tank level, updates travel
 * distances, triggers lubrication pulses, and handles the behavior based on the current system status
 * (Startup, Normal, Rain, Purge, Error, Shutdown). It also manages the pin state of the lube pump.
 *
 * @param add_milimeters The additional distance traveled in millimeters to be processed by the application.
 */
void RunLubeApp(uint32_t add_milimeters)
{
    // Calculate and update tank percentage
    globals.TankPercentage = PersistenceData.tankRemain_microL / (LubeConfig.tankCapacity_ml * 10);

    // Maintain DTCs related to tank level
    MaintainDTC(DTC_TANK_EMPTY, (PersistenceData.tankRemain_microL < LubeConfig.amountPerDose_microL));
    MaintainDTC(DTC_TANK_LOW, (globals.TankPercentage < LubeConfig.TankRemindAtPercentage));

    // Add traveled distance in millimeters
    PersistenceData.TravelDistance_highRes_mm += add_milimeters;
    PersistenceData.odometer_mm += add_milimeters;

    // Update odometer if necessary
    if (PersistenceData.odometer_mm >= 1000000)
    {
        PersistenceData.odometer++;
        PersistenceData.odometer_mm = 0;
    }

    // Handle different system statuses
    switch (globals.systemStatus)
    {
    case sysStat_Startup:
        strcpy_P(globals.systemStatustxt, PSTR("Startup"));
        // Transition to Normal status after startup delay
        if (millis() > STARTUP_DELAY)
        {
            globals.systemStatus = sysStat_Normal;
            globals.resumeStatus = sysStat_Normal;
        }
        break;

    case sysStat_Normal:
        strcpy_P(globals.systemStatustxt, PSTR("Normal"));
        // Trigger lube pulse if traveled distance exceeds the configured limit
        if (PersistenceData.TravelDistance_highRes_mm / 1000 > LubeConfig.DistancePerLube_Default)
        {
            LubePulse();
            PersistenceData.TravelDistance_highRes_mm = 0;
        }
        break;

    case sysStat_Rain:
        strcpy_P(globals.systemStatustxt, PSTR("Rain"));
        // Trigger lube pulse if traveled distance exceeds the configured limit in Rain mode
        if (PersistenceData.TravelDistance_highRes_mm / 1000 > LubeConfig.DistancePerLube_Rain)
        {
            LubePulse();
            PersistenceData.TravelDistance_highRes_mm = 0;
        }
        break;

    case sysStat_Purge:
        strcpy_P(globals.systemStatustxt, PSTR("Purge"));
        // Execute lube pulses during the Purge status
        if (globals.purgePulses > 0)
        {
            // Check if enough time has passed since the last lube pulse
            if (lubePulseTimestamp + LUBE_PULSE_PAUSE_MS < millis())
            {
                LubePulse();
                globals.purgePulses--;
                Debug_pushMessage("Purge remain: %d\n", globals.purgePulses);
            }
        }
        else
        {
            // Transition back to the previous status after completing purge pulses
            globals.systemStatus = globals.resumeStatus;
        }
        break;

    case sysStat_Error:
        strcpy_P(globals.systemStatustxt, PSTR("Error"));
        break;

    case sysStat_Shutdown:
        strcpy_P(globals.systemStatustxt, PSTR("Shutdown"));
        break;

    default:
        break;
    }

    // Maintain Pin-State of Lube-Pump
    if (lubePulseTimestamp > millis())
        digitalWrite(GPIO_PUMP, HIGH);
    else
        digitalWrite(GPIO_PUMP, LOW);
}

/**
 * @brief Initiates a lubrication pulse if there is sufficient oil remaining in the tank.
 *
 * This function checks if there is enough oil remaining in the tank to perform a lubrication pulse.
 * If there is sufficient oil, it updates the lubePulseTimestamp to trigger the pulse and decreases
 * the tank oil level by the configured amount per dose (LubeConfig.amountPerDose_microL).
 */
void LubePulse()
{
    // Only initiate a lubrication pulse if there is oil remaining in the tank
    if (PersistenceData.tankRemain_microL > 0)
    {
        lubePulseTimestamp = millis() + LUBE_PULSE_LENGHT_MS;

        // Prevent underrun and shift over by adjusting the tank oil level
        if (PersistenceData.tankRemain_microL < LubeConfig.amountPerDose_microL)
            PersistenceData.tankRemain_microL = 0;
        else
            PersistenceData.tankRemain_microL -= LubeConfig.amountPerDose_microL;
    }
}
