/**
*   @copyright       © 2025 Microchip Technology Inc.
    Microchip Technology Inc. and its subsidiaries. You may use this
    software and any derivatives exclusively with Microchip products.

    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
    WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
    PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION
    WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.

    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
    BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
    FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
    ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
    THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.

    MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE
    TERMS. 
*
*************************************************************************/

#include "test.h"
#include "diag_interrupt.h"
#include <stddef.h>
#include <stdint.h>
#include <xc.h>

static uint32_t testResult;
static uint32_t status;

#if DEMO_INTERRUPT_AVAILABLE

#define TEST_INTERRUPT_PASS 0x12
#define TEST_INTERRUPT_FREQ_TEST_FAILED 0x13

#define TEST_FREQ_COMPLETED 0xAAU
#define TEST_FREQ_CONTINUE 0xCCU

#define MAX_MONITORED_MODULES 4U

void triggerInt0InterruptOnPosEdge(void);
void triggerInt0InterruptOnNegEdge(void);
void triggerInt1InterruptOnPosEdge(void);
void triggerInt1InterruptOnNegEdge(void);
void triggerInt2InterruptOnPosEdge(void);
void triggerInt2InterruptOnNegEdge(void);
void triggerInt3InterruptOnPosEdge(void);
void triggerInt3InterruptOnNegEdge(void);
void triggerOtherInterruptsOnPosEdge(void);
void triggerOtherInterruptsOnNegEdge(void);
void TEST_INTERRUPT_TriggerDiffIFSSet(void);

DIAG_INTERRUPT_IRQ_MODEL hwModules[MAX_MONITORED_MODULES];

DIAG_INTERRUPT_IRQ_MODEL *hwModPtr = hwModules;

DIAG_INTERRUPT_MODULE_STATUS IrqDataTable[MAX_MONITORED_MODULES];

/*Initialize timer for 5ms*/


volatile uint8_t triggerCounter = 0U;

volatile uint8_t performFrequencyCheck = 0U;

volatile uint8_t executeTriggerSource1 = 0;
volatile uint8_t executeTriggerSource2 = 0;
volatile uint8_t executeTriggerSource3 = 0;
volatile uint8_t executeTriggerSource4 = 0;
volatile uint8_t executeTriggerSource5 = 0;

void __attribute__ ( ( interrupt, no_auto_psv ) ) _PWMEVTAInterrupt(void)
{
   DIAG_INTERRUPT_UpdateISRNotifications(DIAG_INTERRUPT_PEVTAIF,hwModPtr,MAX_MONITORED_MODULES); 
   _PEVTAIF = 0;
}

/*Use this as Trigger Source 2 for Interrupt Frequency Test*/
void __attribute__ ( ( interrupt, no_auto_psv ) ) _PWMEVTBInterrupt(void)
{
   DIAG_INTERRUPT_UpdateISRNotifications(DIAG_INTERRUPT_PEVTBIF,hwModPtr,MAX_MONITORED_MODULES); 
    _PEVTBIF = 0;
}

/*Use this as Trigger Source 3 for Interrupt Frequency Test*/
void __attribute__ ( ( interrupt, no_auto_psv ) ) _PWMEVTCInterrupt(void)
{
   DIAG_INTERRUPT_UpdateISRNotifications(DIAG_INTERRUPT_PEVTCIF,hwModPtr,MAX_MONITORED_MODULES); 
    _PEVTCIF = 0;
}

/*Use this as Trigger Source 4 for Interrupt Frequency Test*/
void __attribute__ ( ( interrupt, no_auto_psv ) ) _PWMEVTDInterrupt(void)
{
    DIAG_INTERRUPT_UpdateISRNotifications(DIAG_INTERRUPT_PEVTDIF,hwModPtr,MAX_MONITORED_MODULES); 
    _PEVTDIF = 0;
}

void testInitTimer(void)
{
     // Stop the timer
    T1CONbits.ON = 0;

    // Set the Timer1 period register
    PR1 = 0x7A120; //This is for 5 ms

    // Clear the Timer1 count register
    TMR1 = 0;

    // Set the Timer1 prescaler to 1:8
    T1CONbits.TCKPS = 0; // 1:8 prescaler

    // Select the internal clock (FOSC/2)
    T1CONbits.TCS = 0;

    // Clear the Timer1 interrupt flag
    IFS1bits.T1IF = 0;

    // Enable the Timer1 interrupt
    IEC1bits.T1IE = 0;

    // Set the Timer1 interrupt priority (optional)
    IPC6bits.T1IP = 4;
}

void testTimerStart( void )
{
    //Clear interrupt flag
    _T1IF = 0;
    //Enable the interrupt
    _T1IE = 1;
    
    // Start the Timer 
    T1CONbits.ON = 1;
    
    triggerCounter = 0;
}

void testTimerStop( void )
{
    //Clear interrupt flag
    _T1IF = 0;
    //Enable the interrupt
    _T1IE = 0;
    
    // Start the Timer 
    T1CONbits.ON = 0;
    
    triggerCounter = 0;
}

void TriggerPWMEVTAInterrupt(void)
{
    _PEVTAIE = 1;
    _PEVTAIF = 1;
}

void TriggerPWMEVTBInterrupt(void)
{
    _PEVTBIE = 1;
    _PEVTBIF = 1;
}

void TriggerPWMEVTCInterrupt(void)
{
    _PEVTCIE = 1;
    _PEVTCIF = 1;
}

void TriggerPWMEVTDInterrupt(void)
{
    _PEVTDIE = 1;
    _PEVTDIF = 1;
}

void Test_ExecuteFunctions(void)
{
    uint8_t numTasks = 0;
    while(numTasks < (MAX_MONITORED_MODULES+1))
    {
        if(executeTriggerSource1 == 1)
        {
            TriggerPWMEVTAInterrupt();
            //Reset the flag
            executeTriggerSource1 = 0;
        }
        if(executeTriggerSource2 == 1)
        {
            TriggerPWMEVTBInterrupt();
            //Reset the flag
            executeTriggerSource2 = 0;
        }
        if(executeTriggerSource3 == 1)
        {
            TriggerPWMEVTCInterrupt();
            //Reset the flag
            executeTriggerSource3 = 0;
        }
        if(executeTriggerSource4 == 1)
        {
            TriggerPWMEVTDInterrupt();
            //Reset the flag
            executeTriggerSource4 = 0;
        }
        if(performFrequencyCheck == 1)
        {
            DIAG_INTERRUPT_FrequencyCheck(hwModPtr,MAX_MONITORED_MODULES);
            //Reset the flag
            performFrequencyCheck = 0;
        }
        numTasks += 1;
    }
}

void __attribute__((__interrupt__, auto_psv)) _T1Interrupt(void) 
{
    IOIM2CONbits.ON = 0;
    triggerCounter += 1U;
    //Initialize 4 triggers 
    
    //One trigger for 15 ms
    if((triggerCounter % 3) == 0)
    {
        executeTriggerSource1 = 1;
    }
    
    //One trigger for 20 ms
    if((triggerCounter % 5) == 0)
    {
        //Trigger the interruptFlag for the Source2
        executeTriggerSource2 = 1;
    }
    
    //One trigger for 30 ms
    if((triggerCounter % 6) == 0)
    {
        //Trigger the interrupt Flag for the Source 3
        executeTriggerSource3 = 1;
    }
    
    //One trigger for 40 ms
    if((triggerCounter % 8) == 0)
    {
        //Trigger the interruptFlag for the Source 4
        executeTriggerSource4 = 1;
    }
    
    //Trigger the DIAG_INTERRUPT_Frequency after 100 ms
    if((triggerCounter % 20) == 0)
    {
        //This flag is waited in the main loop to perform the DIAG_Frequency Check
        performFrequencyCheck = 1;
        //Reset the triggerCounter for the next cycle
        triggerCounter = 0;
    }
        
    // Clear the Timer1 interrupt flag
    
    IFS1bits.T1IF = 0;    
}

void TEST_INTERRUPT_FreqCheck_InitializeModuleParamsStruct(void)
{
    uint16_t index = 0U;
    while(index < MAX_MONITORED_MODULES)
    {
        hwModPtr[index].moduleIrq = (uint16_t)DIAG_INVALID_MODULE_ID;
        hwModPtr[index].moduleInterruptCounter = (int16_t)0;
        hwModPtr[index].moduleExpectedInterruptCount = (int16_t)0;
        hwModPtr[index].moduleInterruptToleranceBand = (int16_t)0;
        hwModPtr[index].moduleInterruptFTTI = (uint16_t)0U;
        hwModPtr[index].moduleInterruptFTTICounter = (uint16_t)0U;
        hwModPtr[index].moduleIntrStatus = MODULE_FREQ_TEST_IN_PROGRESS ;
        index += 1U;
    }
}

void TEST_INTERRUPT_AddModuleToParamsStruct(uint16_t Irq, int16_t expCntVal, int16_t tolBand,uint16_t fttiRate)
{
    uint8_t index = 0;
    /*Look for a slot in the hwModules array to update it*/
    while((hwModules[index].moduleIrq != (uint16_t)DIAG_INVALID_MODULE_ID) && (index < (uint8_t)MAX_MONITORED_MODULES))
        {
            index += 1U;
        }
         /*We found a slot in the hwModules array, fill it with the arguments passed*/
        hwModules[index].moduleIrq = Irq;
        hwModules[index].moduleInterruptCounter = expCntVal;
        hwModules[index].moduleExpectedInterruptCount = expCntVal;
        hwModules[index].moduleInterruptToleranceBand = tolBand;
        hwModules[index].moduleInterruptFTTI = fttiRate;
        hwModules[index].moduleInterruptFTTICounter = fttiRate;
        hwModules[index].moduleIntrStatus = MODULE_FREQ_TEST_IN_PROGRESS;
}
    
uint16_t TEST_INTERRUPT_FrequencyTest(void)
{
    volatile uint16_t retVal = TEST_FREQ_CONTINUE;
    uint32_t failCnt = 0U;
    uint8_t testIndex = 0;
       
    testTimerStart();
    while(retVal == TEST_FREQ_CONTINUE)
    {
        //Execute the tasks
        testIndex = 0;
        Test_ExecuteFunctions();
        /*Collect the results of the tests in the results array*/
        DIAG_INTERRUPT_GetFreqTestResults(&failCnt,IrqDataTable,hwModPtr,MAX_MONITORED_MODULES);
        while(testIndex < MAX_MONITORED_MODULES)
        {
            if(IrqDataTable[testIndex].interruptStatus != MODULE_FREQ_TEST_IN_PROGRESS) 
                 
            {
                retVal = TEST_FREQ_COMPLETED;
            }
            else
            {
                retVal = TEST_FREQ_CONTINUE;
            }
            testIndex += 1;
        }
        //Now go idle and wait for the interrupt
        Idle();
    }
    return retVal;    
}

uint32_t DEMO_INTERRUPT_TestTask()
{
    uint32_t failCnt = 0U;
    
    status = DEMO_TEST_PASS;
    
    testInitTimer();
    //Set the number of modules to be tested
    //DIAG_INTERRUPT_SetNumModulesForFreqCheck(MAX_MONITORED_MODULES);
    TEST_INTERRUPT_FreqCheck_InitializeModuleParamsStruct();
    /*Initialize the interrupt module parameters*/
    (void)TEST_INTERRUPT_AddModuleToParamsStruct(DIAG_INTERRUPT_PEVTAIF,6,1,1);
    (void)TEST_INTERRUPT_AddModuleToParamsStruct(DIAG_INTERRUPT_PEVTBIF,5,1,1);
    (void)TEST_INTERRUPT_AddModuleToParamsStruct(DIAG_INTERRUPT_PEVTCIF,3,1,1);
    (void)TEST_INTERRUPT_AddModuleToParamsStruct(DIAG_INTERRUPT_PEVTDIF,2,1,1);
    /*Start the interrupt frequency test*/
    DIAG_INTERRUPT_StartFrequencyTest();
    testResult = TEST_INTERRUPT_FrequencyTest();
    if(testResult == TEST_FREQ_COMPLETED)
    {
        DIAG_INTERRUPT_GetFreqTestResults(&failCnt,IrqDataTable,hwModPtr,MAX_MONITORED_MODULES);
        if(failCnt == 0)
        {
            testResult = TEST_INTERRUPT_PASS;
        }
        else
        {
            testResult = TEST_INTERRUPT_FREQ_TEST_FAILED;
        }
    }
    DIAG_INTERRUPT_StopFrequencyTest();
    testTimerStop();
    if(testResult == TEST_INTERRUPT_FREQ_TEST_FAILED)
    {
        status = DEMO_TEST_FAIL;
    }
    return status;
}
#endif
