/**
 * @file	   monitoring.c
 *
 * @copyright  Nehemis SARL reserves all rights even in the event of industrial
 *             property rights. We reserve all rights of disposal such as
 *             copying and passing on to third parties.
 *
 * @brief
 *
 */

/******************************************************************************
 * Include Header Files
 ******************************************************************************/
#include "enduranceTestBench.h"
#include "stdlib.h"

/* CANopen includes */
#include <gen_define.h>
#include <co_canopen.h>
#include "hal_system.h"
#include "nms_can.h"

/******************************************************************************
 * Macro constant declarations
 ******************************************************************************/
#define ENDURANCE_TEST_BENCH_RUN_TWO_ROWS_ONLY
#define ENDURANCE_TEST_BENCH_MAX_RETRY_CNT					3u
#define ENDURANCE_TEST_BENCH_TIMEOUT						1000u
#define ENDURANCE_TEST_BENCH_POSITION_SETPOINT_INDEX		0x2002
#define ENDURANCE_TEST_BENCH_POSITION_SETPOINT_SUB_INDEX	0x0
#define ENDURANCE_TEST_BENCH_POSITION_FEEDBACK_INDEX		0x2004
#define ENDURANCE_TEST_BENCH_POSITION_FEEDBACK_SUB_INDEX	0x0
#define ENDURANCE_TEST_BENCH_TEST_PATTERN_2_ROWS			3
#define ENDURANCE_TEST_BENCH_TEST_PATTERN_COUNT 			3


#ifdef ENDURANCE_TEST_BENCH_RUN_TWO_ROWS_ONLY
#define ENDURANCE_TEST_BENCH_LSS_NODE_COUNT					10u
#define ENDURANCE_TEST_BENCH_VALVE_COUNT 					10
#else
#define ENDURANCE_TEST_BENCH_LSS_NODE_COUNT					20u
#define ENDURANCE_TEST_BENCH_VALVE_COUNT 					20
#endif

static const uint8 testPatterns[ENDURANCE_TEST_BENCH_TEST_PATTERN_COUNT][ENDURANCE_TEST_BENCH_VALVE_COUNT] = {
    /* Pattern 0: Columns alternately closed or open */
    {
        0, 0, 0, 0, 255,     		// Row 0
        255, 252, 252, 252, 252,   	// Row 1
        0, 0, 0, 0, 252,     		// Row 2
        252, 252, 252, 252, 252     // Row 3
    },
    /* Pattern 1: Random placeholder (to be filled dynamically at runtime) */
    {
        0, 0, 0, 0, 0,       // Row 0
        0, 0, 0, 0, 0,       // Row 1
        0, 0, 0, 0, 0,       // Row 2
        0, 0, 0, 0, 0        // Row 3
    },
    /* Pattern 2: Inverted version of Pattern 0 */
    {
        252, 252, 252, 252, 252,   // Row 0
        0, 0, 0, 0, 252,           // Row 1
        252, 252, 252, 252, 252,   // Row 2
        0, 0, 0, 0, 252            // Row 3
    },
    /* Pattern 4: Random placeholder for only two rows */
    {
        0, 0, 0, 0, 0,       // Row 0
        0, 0, 0, 0, 0,       // Row 1
    },
};

/******************************************************************************
 * Type Declarations
 ******************************************************************************/
typedef enum
{
    TEST_BENCH_STARTUP,
    TEST_BENCH_IDLE,
    TEST_BENCH_WRITE,
    TEST_BENCH_WRITE_WAIT,
    TEST_BENCH_CYCLE_COMPLETE
} SdlTestBenchState_en;

typedef enum
{
    TEST_BENCH_DATA_VERIF_DEFAULT,
	TEST_BENCH_DATA_NODE_SKIPPED,
    TEST_BENCH_DATA_VERIF_SUCCESS,
    TEST_BENCH_DATA_VERIF_FAILURE,
} TestBenchStatus_en;

typedef struct
{
    uint16 targetPositions_gau8[ENDURANCE_TEST_BENCH_LSS_NODE_COUNT];
    uint8 readPosition_gau8[ENDURANCE_TEST_BENCH_LSS_NODE_COUNT];
    TestBenchStatus_en status_en[ENDURANCE_TEST_BENCH_LSS_NODE_COUNT];
} TestBenchData_en;


/******************************************************************************
 * Global variable declarations
 ******************************************************************************/
static uint8 currentNode_gu8 	= 0u;

static TestBenchData_en testBenchData_en;
static SdlTestBenchState_en testBenchState_en = TEST_BENCH_IDLE;

static uint64 readExecutedTime_u64;
static uint64 writeTime_u64;
/******************************************************************************
 * Public Function Definitions
 ******************************************************************************/
void EnduranceTestBenchRun(bool *testBenchStarted_pb)
{
    static uint64 startTime_u64 	= 0uLL;
    static uint8 alternate_u8 		= 0u;
    static uint8 retries_u8 		= 0u;
    uint64 currentTime_u64;

    HalSystemGetRunTimeMs(&currentTime_u64);

    if (startTime_u64 == 0uLL)
    {
        HalSystemGetRunTimeMs(&startTime_u64);
        testBenchState_en = TEST_BENCH_IDLE;
    }

    switch (testBenchState_en)
    {
	case TEST_BENCH_STARTUP:
	{
		uint16 max_u8 = 1000u; /* Fully open (255) */
		uint16 min_u8 = 0u;            /* Fully closed (0) */

		if ((currentTime_u64 - startTime_u64) < 50uLL)
		{
		    /* First 5 seconds: First 10 open, rest closed */
		    for (uint8 i_u8 = 0u; i_u8 < ENDURANCE_TEST_BENCH_LSS_NODE_COUNT; i_u8++)
		    {
		    	testBenchData_en.targetPositions_gau8[i_u8] = (i_u8 < 10u) ? max_u8 : min_u8;
		    }
		}
		else if ((currentTime_u64 - startTime_u64) < 10uLL)
		{
		    /* Next 5 seconds: First 10 closed, rest open */
		    for (uint8 i_u8 = 0u; i_u8 < ENDURANCE_TEST_BENCH_LSS_NODE_COUNT; i_u8++)
		    {
		    	testBenchData_en.targetPositions_gau8[i_u8] = (i_u8 < 10) ? min_u8 : max_u8;
		    }
		}
		else
		{
		    /* After 10 seconds, move to endurance test */
		    testBenchState_en 	= TEST_BENCH_IDLE;
		}

		currentNode_gu8 		= 0u;
		testBenchState_en 		= TEST_BENCH_WRITE;
	}
	break;

	case TEST_BENCH_IDLE:
	{
	    *testBenchStarted_pb = true;
	    uint8 randIndex_u8 = (uint8)(rand() % ENDURANCE_TEST_BENCH_VALVE_COUNT);

	    /* --- TEST MODE: force single-random pattern --- */
	    uint8 patternIndex_u8 = ENDURANCE_TEST_BENCH_TEST_PATTERN_2_ROWS;

	    if (patternIndex_u8 == 1u)
	    {
	        /* Randomize all valves */
	        for (uint8 i_u8 = 0u; i_u8 < ENDURANCE_TEST_BENCH_VALVE_COUNT; i_u8++)
	        {
	            testBenchData_en.targetPositions_gau8[i_u8] = (uint16)(rand() % 1000);
	        }
	    }
	    else if (patternIndex_u8 == ENDURANCE_TEST_BENCH_TEST_PATTERN_2_ROWS)
	    {
	        /* All zero */
	        for (uint8 i_u8 = 0u; i_u8 < ENDURANCE_TEST_BENCH_VALVE_COUNT; i_u8++)
	        {
	            testBenchData_en.targetPositions_gau8[i_u8] = 25u;
	        }

	        /* Pick one random valve and give it a random setpoint */
	        testBenchData_en.targetPositions_gau8[randIndex_u8] = (uint16)(rand() % 1000);
	    }
	    else
	    {
	        /* Copy from predefined pattern (0 or 2) */
	        for (uint8 i_u8 = 0u; i_u8 < ENDURANCE_TEST_BENCH_VALVE_COUNT; i_u8++)
	        {
	            testBenchData_en.targetPositions_gau8[i_u8] = testPatterns[patternIndex_u8][i_u8];
	        }
	    }

	    currentNode_gu8 = randIndex_u8;
	    testBenchState_en = TEST_BENCH_WRITE;
	}
	break;


	/* -------------------- Write -------------------- */
    case TEST_BENCH_WRITE:
    {
        if (currentNode_gu8 < ENDURANCE_TEST_BENCH_LSS_NODE_COUNT)
        {
			RET_T retVal_en = coSdoWrite(
				(currentNode_gu8 + 1),
				ENDURANCE_TEST_BENCH_POSITION_SETPOINT_INDEX,
				ENDURANCE_TEST_BENCH_POSITION_SETPOINT_SUB_INDEX,
				&testBenchData_en.targetPositions_gau8[currentNode_gu8],
				sizeof(uint16),
				CO_FALSE,
				ENDURANCE_TEST_BENCH_TIMEOUT
			);


			if (retVal_en == RET_OK)
			{
				uint8 targetPos = (uint8)(testBenchData_en.targetPositions_gau8[currentNode_gu8] / 10u);
				NmsCanPutObj_u8(0x2004, currentNode_gu8 + 1, targetPos);
				HalSystemGetRunTimeMs(&writeTime_u64);
				retries_u8 = 0u;
				testBenchState_en = TEST_BENCH_WRITE_WAIT;

			}
			else
			{
				retries_u8++;
				if (retries_u8 >= ENDURANCE_TEST_BENCH_MAX_RETRY_CNT)
				{
					testBenchData_en.status_en[currentNode_gu8] = TEST_BENCH_DATA_NODE_SKIPPED;

					/* Mark node as skipped and move on */
					//currentNode_gu8++;
					retries_u8 = 0u;
					testBenchState_en = TEST_BENCH_WRITE;
				}
			}
		}

        else
        {
            /* Finished writing to all nodes */
            //currentNode_gu8 = 0u;   /* Reset index for read phase */
            testBenchState_en = TEST_BENCH_CYCLE_COMPLETE;
        }
        break;
    }

    case TEST_BENCH_WRITE_WAIT:
    {
        /* Wait 100ms between writes */
        if ((currentTime_u64 - writeTime_u64) >= 30000uLL)
        {
            /* Move to next node write */
            //currentNode_gu8++;
			RET_T retVal_en = coSdoWrite(
				(currentNode_gu8 + 1),
				ENDURANCE_TEST_BENCH_POSITION_SETPOINT_INDEX,
				ENDURANCE_TEST_BENCH_POSITION_SETPOINT_SUB_INDEX,
				25u,
				sizeof(uint16),
				CO_FALSE,
				ENDURANCE_TEST_BENCH_TIMEOUT
			);
			if(retVal_en == RET_OK)
			{
				NmsCanPutObj_u8(0x2004, currentNode_gu8 + 1, 2u);
			}


            testBenchState_en = TEST_BENCH_IDLE;
        }
        break;
    }


    /* -------------------- Cycle Complete -------------------- */
    case TEST_BENCH_CYCLE_COMPLETE:
    {
        /* Optionally, can log or process the batch results here */
        /* Reset all node data for the next cycle */
        for (uint8 i_u8 = 0u; i_u8 < ENDURANCE_TEST_BENCH_LSS_NODE_COUNT; i_u8++)
        {
            testBenchData_en.targetPositions_gau8[i_u8] = 0u;
            testBenchData_en.readPosition_gau8[i_u8]    = 0u;
            testBenchData_en.status_en[i_u8]            = TEST_BENCH_DATA_VERIF_DEFAULT;
        }

        /* Reset everything and Start next cycle */
        currentNode_gu8 	= 0u;
        retries_u8			= 0u;
        testBenchState_en 	= TEST_BENCH_IDLE;
        break;
    }

    default:
        break;
    }
}

/******************************************************************************
 * Callback Functions
 ******************************************************************************/
void EnduranceTestBenchWriteInd(uint8 sdoNr_u8, uint16 index_u16, uint8 subIndex_u8, uint32 errorVal_u32)
{
	HalSystemGetRunTimeMs(&writeTime_u64);
}

void EnduranceTestBenchReadInd(uint8 sdoNr_u8, uint16 index_u16, uint8 subIndex_u8, uint32 size, uint32 errorVal_u32)
{
	HalSystemGetRunTimeMs(&readExecutedTime_u64);
}
