/**
 * @file dtc.cpp
 * @brief Implementation of functions related to Diagnostic Trouble Codes (DTCs).
 *
 * This file contains the implementation of functions that manage the status
 * and registration of Diagnostic Trouble Codes in the system.
 *
 * @author Marcel Peterkau
 * @date 09.01.2024
 */
#include "dtc.h"
#include "debugger.h"

DTCEntry_t DTCStorage[MAX_DTC_STORAGE];

// Function implementations...

/**
 * @brief Maintains the status of Diagnostic Trouble Codes (DTCs) in the DTCStorage array.
 *        Updates the status of existing DTCs or adds new ones based on their activity.
 *
 * @param DTC_no      The number of the Diagnostic Trouble Code.
 * @param active      Indicates whether the DTC is active (true) or inactive (false).
 * @param DebugValue  Additional debugging information associated with the DTC.
 */
void MaintainDTC(DTCNum_t DTC_no, boolean active, uint32_t DebugValue)
{
    // Iterate through the existing DTCs in the storage
    for (int i = 0; i < MAX_DTC_STORAGE; i++)
    {
        // Check if the DTC with the specified number exists
        if (DTCStorage[i].Number == DTC_no)
        {
            // If the DTC is active and was not active before, update its status
            if (active && DTCStorage[i].active != DTC_ACTIVE)
            {
                Debug_pushMessage("DTC gone active: %d, DebugVal: %d\n", DTC_no, DebugValue);
                DTCStorage[i].timestamp = millis();
                DTCStorage[i].active = DTC_ACTIVE;
                DTCStorage[i].debugVal = DebugValue;
            }
            // If the DTC is not active anymore, update its status to previous
            if (!active && DTCStorage[i].active == DTC_ACTIVE)
            {
                Debug_pushMessage("DTC gone previous: %d\n", DTC_no);
                DTCStorage[i].active = DTC_PREVIOUS;
            }
            return; // DTC found and processed, exit the function
        }
    }

    // DTC was not found in the existing storage, but it is active,
    // so look for free space to store the new DTC
    if (active == true)
    {
        for (int i = 0; i < MAX_DTC_STORAGE; i++)
        {
            // Check for an empty slot in the storage
            if (DTCStorage[i].Number == DTC_LAST_DTC)
            {
                Debug_pushMessage("new DTC registered: %d, DebugVal: %d\n", DTC_no, DebugValue);
                DTCStorage[i].Number = DTC_no;
                DTCStorage[i].timestamp = millis();
                DTCStorage[i].active = DTC_ACTIVE;
                DTCStorage[i].debugVal = DebugValue;
                return; // New DTC registered, exit the function
            }
        }
    }
}

/**
 * @brief Clears a specific Diagnostic Trouble Code (DTC) entry.
 *
 * This function clears the information related to a specific DTC entry,
 * setting its status to inactive and timestamp to zero.
 *
 * @param DTC_no The Diagnostic Trouble Code number to be cleared.
 */
void ClearDTC(DTCNum_t DTC_no)
{
    for (int i = 0; i < MAX_DTC_STORAGE; i++)
    {
        if (DTCStorage[i].Number == DTC_no)
        {
            DTCStorage[i].Number = DTC_LAST_DTC;
            DTCStorage[i].active = DTC_INACTIVE;
            DTCStorage[i].timestamp = 0;
        }
    }
}

/**
 * @brief Clears all Diagnostic Trouble Code (DTC) entries.
 *
 * This function clears all DTC entries, setting their status to inactive and
 * timestamps to zero.
 */
void ClearAllDTC()
{
    for (int i = 0; i < MAX_DTC_STORAGE; i++)
    {
        DTCStorage[i].Number = DTC_LAST_DTC;
        DTCStorage[i].active = DTC_INACTIVE;
        DTCStorage[i].timestamp = 0;
    }
}

/**
 * @brief Gets the last recorded Diagnostic Trouble Code (DTC) number.
 *
 * This function retrieves the DTC number of the last recorded DTC based on the
 * timestamp. Optionally, it can filter only active DTCs.
 *
 * @param only_active If true, considers only active DTCs; otherwise, considers all.
 * @return The DTC number of the last recorded DTC or DTC_LAST_DTC if none found.
 */
DTCNum_t getlastDTC(boolean only_active)
{
    int8_t pointer = -1;
    uint32_t lasttimestamp = 0;

    for (int i = 0; i < MAX_DTC_STORAGE; i++)
    {
        if (DTCStorage[i].Number > 0 && DTCStorage[i].timestamp > lasttimestamp)
        {
            if (only_active == false || DTCStorage[i].active == DTC_ACTIVE)
            {
                pointer = i;
                lasttimestamp = DTCStorage[i].timestamp;
            }
        }
    }

    return pointer >= 0 ? DTCStorage[pointer].Number : DTC_LAST_DTC;
}

/**
 * @brief Gets the severity level for a specific Diagnostic Trouble Code (DTC).
 *
 * This function looks up the severity level associated with the provided DTC code
 * from the predefined list of DTC definitions.
 *
 * @param targetCode The DTC code for which to retrieve the severity.
 * @return The severity level of the specified DTC or DTC_NONE if not found.
 */
DTCSeverity_t getSeverityForDTC(DTCNum_t targetCode)
{
    for (int i = 0; i < DTC_LAST_DTC; i++)
    {
        if (dtc_definitions[i].code == targetCode)
        {
            return dtc_definitions[i].severity;
        }
    }
    return DTC_NONE;
}

/**
 * @brief Processes Diagnostic Trouble Codes (DTCs) and updates system status accordingly.
 *
 * This function checks for the presence of active DTCs and adjusts the system status
 * based on the severity of the most critical DTC. If a critical DTC is detected,
 * the system status is set to sysStat_Error, potentially triggering a system shutdown.
 *
 * @note The function also preserves the original system status when transitioning to an error state
 *       and restores it when all DTCs are cleared.
 */
void DTC_Process()
{
    static tSystem_Status preserverSysStatusError;
    DTCNum_t lastDTC = getlastDTC(true);

    if (lastDTC < DTC_LAST_DTC)
    {
        globals.hasDTC = true;

        if (getSeverityForDTC(lastDTC) == DTC_CRITICAL && globals.systemStatus != sysStat_Shutdown)
        {
            if (globals.systemStatus != sysStat_Error)
            {
                preserverSysStatusError = globals.systemStatus;
            }
            globals.systemStatus = sysStat_Error;
        }
    }
    else
    {
        globals.hasDTC = false;

        if (globals.systemStatus == sysStat_Error)
        {
            globals.systemStatus = preserverSysStatusError;
        }
    }
}
