// GLOBAL DEFINES ==============================================================

#define NULL  ((void *)0)
#define FALSE 0
#define TRUE  1
#define LOW   0
#define HIGH  1
#define __STATIC static
#define HAL_INLINE __inline
#define ALWAYS_INLINE_ATTRIBUTE
#define ISO_OP_MEMORY_CLASS

#define wsd_ISOAgLibSE                       0
#define alm_gps                           2000
#define alm_wheel                         2001
#define alm_fert                          2002
#define alm_level_fert                    2003
#define alm_level_seed                    2004
#define skm_start                         4000
#define msk_start                         1000

// GLOBAL TYPE DEFINITIONS =====================================================

typedef void TVoid;
typedef void THandle;
typedef char TChar;
typedef signed char TInt8;
typedef signed short TInt16;
typedef signed int TInt32;
typedef unsigned char TBoolean;
typedef unsigned char TUint8;
typedef unsigned short TUint16;
typedef unsigned int TUint32;
typedef float TFloat32;
typedef double TFloat64;

#define GET_SEED_LEVEL_ENABLED_PIN 4
#define GET_FERT_LEVEL_ENABLED_PIN 2
#define GET_SEED_LEVEL_STATUS_PIN 32
#define GET_FERT_LEVEL_STATUS_PIN 33
#define SET_SEED_LEVEL_STATUS_PIN 19
#define SET_FERT_LEVEL_STATUS_PIN 21

#define GPS_SPEED 1
#define TIME_CHECK_ALARM 1000
#define TIME_RETURN_TO_CHECK_ALARM 10000

typedef enum
{
    E_LOCAL_CF_1,
    E_LOCAL_CF_2,
    E_LOCAL_CF_3,
    E_LOCAL_CF_4
} ELocalCf;

// Global variables
static TBoolean m_boGpsSpeedModule = FALSE;
static TBoolean m_boLastStatusGps = FALSE;
static TBoolean m_boAlarmMaskChanged = FALSE;
static TBoolean m_boAlarmConditionsStillPresent = FALSE;
static TBoolean m_boSeedAndFertNormal = FALSE;
static TBoolean m_boAudioRepeat = FALSE;

static TUint32 m_u32CurrentTime = 0;
static TUint32 m_u32TimeOfAlarmMaskChange = 0;
static TUint32 m_u32TimeOfLastCheckAlarm = 0;

static TUint32 m_u32LevelSeedCount = 0;
static TUint32 m_u32LevelFertCount = 0;
static TUint32 m_u32AlarmMaskChangedCount = 0;
static TUint32 m_u32AlarmMaskResetCount = 0;

static TUint16 m_u16LastDisplayedAlarm = 0;
static TUint32 m_u32TimeOfLastAlarmDisplay = 0;

TUint32 u32IsoStackCoreGetTime()
{
    return millis();
}

TBoolean vEcuAppCheckCrashingFert()
{
    return FALSE;
}

TBoolean vEcuAppCheckStatusGps()
{
    return TRUE;
}

TBoolean bEcuAppSeedLevelEnabled()
{
    return digitalRead(GET_SEED_LEVEL_ENABLED_PIN);
}

TBoolean bEcuAppFertLevelEnabled()
{
    return digitalRead(GET_FERT_LEVEL_ENABLED_PIN);
}

TBoolean bEcuAppGetSeedLevelStatus()
{
    return !digitalRead(GET_SEED_LEVEL_STATUS_PIN);
}

TBoolean bEcuAppGetFertLevelStatus()
{
    return !digitalRead(GET_FERT_LEVEL_STATUS_PIN);
}

void vTriggerAlarm(TUint16 alarmType)
{
    // Auxiliary function to trigger an alarm
    // Change the active mask to the specified alarm type
    eIsoStackVtQChangeActiveMask(E_LOCAL_CF_1, wsd_ISOAgLibSE, alarmType, 0);

    // Set the alarm mask changed flag to TRUE
    m_boAlarmMaskChanged = TRUE;

    // Update the time of the alarm mask change to the current time
    m_u32TimeOfAlarmMaskChange = m_u32CurrentTime;

    // Reset the audio repeat flag
    m_boAudioRepeat = FALSE;

    // Reset alarm count
    m_u32AlarmMaskChangedCount = 0;

    // Switch case to handle different alarm types
    switch (alarmType) {
        case alm_fert:
            // Fertilizer crash detected
            Serial.println("Alert: Fertilizer crash detected!");
            break;
        case alm_gps:
            // GPS status issue detected
            Serial.println("Alert: GPS status is not OK!");
            break;
        case alm_level_seed:
            // Seed level status issue detected
            Serial.printf("Alert (%d): Seed level status is not OK\n", ++m_u32LevelSeedCount);
            break;
        case alm_level_fert:
            // Fertilizer level status issue detected
            Serial.printf("Alert (%d): Fertilizer level status is not OK\n", ++m_u32LevelSeedCount);
            break;
        default:
            // Unknown alarm type detected
            Serial.println("Alert: Unknown alarm type!");
            break;
    }
}

void eIsoStackVtQChangeActiveMask(
    ELocalCf eLocalCf,
    TUint16 u16WorkingSetObjId,
    TUint16 u16NewActiveMaskObjId,
    TUint32 u32UserId)
{
    // Check if the new active mask object ID is equal to the start mask
    if (u16NewActiveMaskObjId == msk_start) {
        // If it is, set the seed level status pin and fertilizer level status pin to LOW
        digitalWrite(SET_SEED_LEVEL_STATUS_PIN, LOW);
        digitalWrite(SET_FERT_LEVEL_STATUS_PIN, LOW);
    } 
    // Check if the new active mask object ID is equal to the seed level alarm mask
    else if (u16NewActiveMaskObjId == alm_level_seed) {
        // If it is, set the seed level status pin to HIGH and fertilizer level status pin to LOW
        digitalWrite(SET_SEED_LEVEL_STATUS_PIN, HIGH);
        digitalWrite(SET_FERT_LEVEL_STATUS_PIN, LOW);
    } 
    // Check if the new active mask object ID is equal to another condition (it seems like there might be a mistake here because the condition is the same as the previous one)
    else if (u16NewActiveMaskObjId == alm_level_fert) { // Possibly, this condition should be different
        // If it is, set both the seed level status pin and fertilizer level status pin to LOW
        digitalWrite(SET_SEED_LEVEL_STATUS_PIN, LOW);
        digitalWrite(SET_FERT_LEVEL_STATUS_PIN, HIGH);
    }
}

TUint8 u8EcuAppSpeedModule()
{
  return GPS_SPEED;
}

TVoid vResetAlarmMask()
{
    // Auxiliary function to reset the alarm mask
    if (m_boAlarmMaskChanged) {
        Serial.printf("Alarm mask has changed (%d)\n", ++m_u32AlarmMaskChangedCount); // Indicate that the alarm mask has changed
        
        // Check if any of the alarm conditions are still present
        m_boAlarmConditionsStillPresent = vEcuAppCheckCrashingFert() ||
                                        (m_boGpsSpeedModule && !vEcuAppCheckStatusGps()) ||
                                        (bEcuAppSeedLevelEnabled() && !bEcuAppGetSeedLevelStatus()) ||
                                        (bEcuAppFertLevelEnabled() && !bEcuAppGetFertLevelStatus());

        // Check if seed and fert levels are back to normal
        m_boSeedAndFertNormal = bEcuAppGetSeedLevelStatus() && bEcuAppGetFertLevelStatus();

        // Reset the alarm mask if no alarm conditions are present or if seed and fert levels are back to normal
        if (!m_boAlarmConditionsStillPresent || m_boSeedAndFertNormal ||
            // or if the predefined time to check the alarm has passed
            (m_u32CurrentTime - m_u32TimeOfAlarmMaskChange) >= TIME_RETURN_TO_CHECK_ALARM) {
            m_u32TimeOfAlarmMaskChange = u32IsoStackCoreGetTime();
            m_boAlarmMaskChanged = FALSE; // Reset the alarm mask flag
            m_u32AlarmMaskChangedCount = 0;
            Serial.printf("Resetting alarm mask (%d)\n", ++m_u32AlarmMaskResetCount); // Indicate that the alarm mask is being reset
            
            // Set the active mask back to the start mask, indicating that the alarm has been addressed
            eIsoStackVtQChangeActiveMask(E_LOCAL_CF_1, wsd_ISOAgLibSE, msk_start, 0);
        }
    }
}

TVoid vVtAppCheckAlarm(TVoid)
{
    // Get current time
    m_u32CurrentTime = u32IsoStackCoreGetTime();

    // Exit early if the alarm check interval hasn't passed
    if ((m_u32CurrentTime - m_u32TimeOfLastCheckAlarm) < TIME_CHECK_ALARM) {
        return;
    }

    // Update the last check time
    m_u32TimeOfLastCheckAlarm = m_u32CurrentTime;

    // Determine the type of speed module being used
    m_boGpsSpeedModule = u8EcuAppSpeedModule() == GPS_SPEED;

    // Check and trigger alarms if conditions are met and no alarm is currently active
    if (!m_boAlarmMaskChanged || 
        (m_u32CurrentTime - m_u32TimeOfLastAlarmDisplay) >= TIME_RETURN_TO_CHECK_ALARM) {
        
        // Reset the last alarm display time
        m_u32TimeOfLastAlarmDisplay = m_u32CurrentTime;
        
        if (vEcuAppCheckCrashingFert()) {
            vTriggerAlarm(alm_fert);
        }
        else if (m_boGpsSpeedModule && !vEcuAppCheckStatusGps()) {
            vTriggerAlarm(alm_gps);
        }
        else if (bEcuAppSeedLevelEnabled() && !bEcuAppGetSeedLevelStatus() && 
                 (m_u16LastDisplayedAlarm != alm_level_seed || !bEcuAppFertLevelEnabled() || bEcuAppGetFertLevelStatus())) {
            vTriggerAlarm(alm_level_seed);
            m_u16LastDisplayedAlarm = alm_level_seed;
        }
        else if (bEcuAppFertLevelEnabled() && !bEcuAppGetFertLevelStatus() && 
                 (m_u16LastDisplayedAlarm != alm_level_fert || !bEcuAppSeedLevelEnabled() || bEcuAppGetSeedLevelStatus())) {
            vTriggerAlarm(alm_level_fert);
            m_u16LastDisplayedAlarm = alm_level_fert;
        }
    }

    // Reset the alarm mask based on conditions
    vResetAlarmMask();
}

void setup()
{
    Serial.begin(115200);
    pinMode(GET_SEED_LEVEL_ENABLED_PIN, INPUT_PULLUP);
    pinMode(GET_FERT_LEVEL_ENABLED_PIN, INPUT_PULLUP);
    pinMode(GET_SEED_LEVEL_STATUS_PIN, INPUT_PULLUP);
    pinMode(GET_FERT_LEVEL_STATUS_PIN, INPUT_PULLUP);
    pinMode(SET_SEED_LEVEL_STATUS_PIN, OUTPUT);
    pinMode(SET_FERT_LEVEL_STATUS_PIN, OUTPUT);
}

void loop()
{
    vVtAppCheckAlarm();
}