
#include <stdio.h>
#include <inttypes.h>
#include <kernel/dpl/ClockP.h>
#include <kernel/dpl/SemaphoreP.h>
#include <drivers/ipc_notify.h>
#include "kernel/dpl/DebugP.h"
#include "kernel/dpl/SystemP.h"
#include "ti_drivers_open_close.h"
#include "ti_board_open_close.h"
#include <drivers/ipc_notify/v0/ipc_notify_v0.h>
#include <drivers/ipc_notify/v0/ipc_notify_v0_mailbox.h>

// #include "emmc.h"
// #include "ethData.h"

/* client ID that is used to send and receive messages */
uint32_t gClientId_low_tx = 4u;        // mailbox WKUP to MCU
uint32_t gClientId_low_rx = 5u;       // mailbox MCU to WKUP

#if defined(SOC_AM62PX)
/* main core that starts the message exchange */
uint32_t gMainCoreId = CSL_CORE_ID_WKUP_R5FSS0_0;
/* remote cores that echo messages from main core, make sure to NOT list main core in this list */
uint32_t gRemoteCoreId[] = {
    CSL_CORE_ID_MCU_R5FSS0_0,
    CSL_CORE_ID_MAX /* this value indicates the end of the array */
};
#endif

/* semaphore's used to indicate a main core has finished all message exchanges */
SemaphoreP_Object sLowAddrDone;
SemaphoreP_Object sHighAddrDone;

uint8_t EmmcRxBuf1[4]__attribute__((aligned(128U))) = {0x1B, 0x2C, 0x2A, 0x3B};
uint8_t EmmcRxBuf2[4]__attribute__((aligned(128U))) = {0x0A, 0x0C, 0x0A, 0x0B};

typedef struct {
    uint32_t buf1_addr;
    uint32_t buf2_addr;
}SharedBuffAddr;

SharedBuffAddr gSharedAddrs __attribute__((section(".eth_data_addr"),aligned(4)));

uint32_t rx_arr_mcu[2];
uint32_t rx_cnt_mcu = 0;

void ipc_notify_msg_handler_wkup_lowAddr(uint16_t remoteCoreId, uint16_t localClientId, uint32_t msgValue, void *args)
{
    rx_arr_mcu[rx_cnt_mcu] = msgValue;
    rx_cnt_mcu ++;
    if (rx_cnt_mcu == 2)
    {
        SemaphoreP_post(&sLowAddrDone);
    
    }

}

void ipc_notify_echo_main_core_start()
{
    int32_t status = SystemP_SUCCESS;
    uint64_t curTime;

    SemaphoreP_constructBinary(&sLowAddrDone, 0);
    /* register a handler to receive messages */
    DebugP_log("[WKUP] Registering handler for client ID %d\r\n", gClientId_low_rx);
    status = IpcNotify_registerClient(gClientId_low_rx, ipc_notify_msg_handler_wkup_lowAddr, NULL);
    DebugP_log("[WKUP] Register status: %d\r\n", status);
    /* wait for all cores to be ready */
    IpcNotify_syncAll(SystemP_WAIT_FOREVER);

    gSharedAddrs.buf1_addr = (uint32_t) EmmcRxBuf1;
    gSharedAddrs.buf2_addr = (uint32_t) EmmcRxBuf2;
    DebugP_log("[WKUP] gSharedAddrs=0x%X: buf1_addr=0x%08X, buf2_addr=0x%08X\r\n", 
               (uint32_t)&gSharedAddrs, gSharedAddrs.buf1_addr, gSharedAddrs.buf2_addr); //PC-- WKUP writing to shared memory

    // __asm__ __volatile__ ( "dsb sy"  "\n\t": : : "memory");
    
    uint32_t eth_flag = 0x01;
    status = IpcNotify_sendMsg(gRemoteCoreId[0], gClientId_low_tx, eth_flag, 1);
    DebugP_log("[WKUP] Send status: %d\r\n", status);
    
    curTime = ClockP_getTimeUsec();
    SemaphoreP_pend(&sLowAddrDone, SystemP_WAIT_FOREVER);
    curTime = ClockP_getTimeUsec() - curTime;
    DebugP_log("[IPC Notify] The total echo time is %u. \r\n", curTime);

    DebugP_log("[WKUP] buf1_addr=0x%08X, buf2_addr=0x%08X\r\n", rx_arr_mcu[0], rx_arr_mcu[1]);

}

void ipc_notify_msg_handler_mcu_lowAddr(uint16_t remoteCoreId, uint16_t localClientId, uint32_t msgValue, void *args)
{
    DebugP_log("[MCU HANDLER] inside handler core=%d, client=%d, msg=0x%X\r\n",
               remoteCoreId, localClientId, msgValue);
    if (msgValue == 0x01)
    {
        __asm__ volatile("dsb sy" ::: "memory");
        
        uint32_t eth_data_addr_recov1 = gSharedAddrs.buf1_addr;
        uint32_t eth_data_addr_recov2 = gSharedAddrs.buf2_addr;
        
        DebugP_log("[MCU] gSharedAddrs 0x%X: buf1_addr=0x%08X, buf2_addr=0x%08X\r\n",
                   (uint32_t)&gSharedAddrs, eth_data_addr_recov1, eth_data_addr_recov2);
        
        IpcNotify_sendMsg(remoteCoreId, gClientId_low_rx, eth_data_addr_recov1, 1);
        IpcNotify_sendMsg(remoteCoreId, gClientId_low_rx, eth_data_addr_recov2, 1);
    
    }
        
}

void ipc_notify_msg_handler_mcu_highAddr(uint16_t remoteCoreId, uint16_t localClientId, uint32_t msgValue, void *args)
{
    uint32_t data_h = msgValue;
    DebugP_log("The received data in MCU is %u. \r\n", data_h);

    IpcNotify_sendMsg(remoteCoreId, localClientId, data_h, 1);
}

void ipc_notify_echo_remote_core_start()
{
    
    int32_t status;
    /* register a handler to receive messages */
    DebugP_log("[MCU] Registering handler for client ID %d\r\n", gClientId_low_tx);
    status = IpcNotify_registerClient(gClientId_low_tx, ipc_notify_msg_handler_mcu_lowAddr, NULL);
    DebugP_log("[MCU] Register status: %d\r\n", status);

    /* wait for all cores to be ready */
    DebugP_log("[MCU] Waiting for sync\r\n");
    IpcNotify_syncAll(SystemP_WAIT_FOREVER);
    DebugP_log("[MCU] Sync completed, and waiting for messages\r\n");

    // __asm__ volatile("dsb sy" ::: "memory");
    
    DebugP_log("[MCU] MCU Core ID: %d\r\n", IpcNotify_getSelfCoreId());
    DebugP_log("[MCU] gSharedAddrs at 0x%X\r\n", (uint32_t)&gSharedAddrs);
    uint32_t eth_data_addr_recov1 = gSharedAddrs.buf1_addr;
    uint32_t eth_data_addr_recov2 = gSharedAddrs.buf2_addr;
    DebugP_log("[MCU] gSharedAddrs 0x%X: buf1_addr=0x%08X, buf2_addr=0x%08X\r\n",
                   (uint32_t)&gSharedAddrs, eth_data_addr_recov1, eth_data_addr_recov2);
                   
}

void dloop()
{
    volatile uint32_t loop = 1;
    while(loop)
        ;
}

void ipc_notify_echo_main(void *args)
{
    // dloop();
    if(IpcNotify_getSelfCoreId()==gMainCoreId)
    {
        ipc_notify_echo_main_core_start();
    }
    else
    {
        ipc_notify_echo_remote_core_start();
    }

}