#include <ecm.h>
#include <stdio.h>
#include <string.h>

#define ECM2_EVENT_MASK     (ECM_EVENT_LOCAL | ECM_EVENT_CFG          |   \
                            ECM_EVENT_WCNT   | ECM_EVENT_STATE_CHANGE |   \
                            ECM_EVENT_SLV)

/* Reference to process variables */
static uint8_t * pucDo = NULL;
static uint8_t ucData[8] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
static uint8_t i = 0;
static uint8_t j = 0;
static uint8_t inop = 0;

static int ecmEventHandler(ECM_EVENT *pEvent)
{
    switch(pEvent->ulEvent)
    {
        case ECM_EVENT_STATE_CHANGE:
            printf("ECM_EVENT_STATE_CHANGE: Master state change to ");
            switch(pEvent->ulArg1)
            {
            case ECM_DEVICE_STATE_UNKNOWN:
                printf("UNKNOWN\n");
                break;
            case ECM_DEVICE_STATE_INIT:
                printf("INIT\n");
                break;
            case ECM_DEVICE_STATE_PREOP:
                printf("PREOP\n");
                break;
            case ECM_DEVICE_STATE_SAFEOP:
                printf("SAFEOP\n");
                break;
            case ECM_DEVICE_STATE_OP:
                printf("OP\n");
                inop = 1;
                break;
            default:
                printf("???\n");
                break;
            }
            break;
        
        case ECM_EVENT_SLV:
            printf("ECM_EVENT_SLV: Slave address %d state change to ", pEvent->ulArg2 & 0x0000FFFF);
            switch(pEvent->ulArg1)
            {
            case ECM_ESC_AL_STATUS_INIT:
                printf("INIT\n");
                break;
            case ECM_ESC_AL_STATUS_PREOP:
                printf("PREOP\n");
                break;
            case ECM_ESC_AL_STATUS_BOOTSTRAP:
                printf("BOOTSTRAP\n");
                break;
            case ECM_ESC_AL_STATUS_SAFEOP:
                printf("SAFEOP\n");
                break;
            case ECM_ESC_AL_STATUS_OP:
                printf("OP\n");
                break;
            default:
                printf("???\n");
                break;
            }
            break;
    }
    return(0);
}

static int CycleHandler(ECM_HANDLE hndDevice, int error){
    if(!inop) return 0;
    if (i==8) 
    {
        *pucDo = 0;
        i=0;
        j++;
        if(j<3)
            pucDo++;
        else{
            pucDo-=2;
            j=0;
        }
    }
    *pucDo = ucData[i];
    i++;
    return 0;
}

int main(int argc, char *argv[])
{
    int result;
    ECM_LIB_INIT libInitData;
    ECM_CFG_INIT cfgInitData;
    ECM_HANDLE hndDevice =NULL;
    ECM_HANDLE hndMaster =NULL;
    ECM_PROC_CTRL ProcCtrl;
    ECM_NIC *pNicList = NULL;
    uint32_t numAdapter;
    ECM_ETHERNET_ADDRESS macPrimary, macRedundant;

    ECM_INIT(libInitData);
    libInitData.ulEventMask = ECM2_EVENT_MASK;
    libInitData.pfnEventHandler = ecmEventHandler;
    result = ecmInitLibrary(&libInitData);
    if (result != 0) {
      printf("ecmInitLibrary() returned with %d\n", result);
      return(-1);
    }

    ECM_INIT(cfgInitData);
    cfgInitData.Config.File.pszEniFile = "./cable_redundancy_three_ecxdio8.xml";
    cfgInitData.ulFlags |= ECM_FLAG_CFG_IGNORE_SRC_MAC;
    cfgInitData.cfgDataDevice.ulFlags |= ECM_FLAG_DEVICE_REDUNDANCY;
    pNicList = calloc(2, sizeof(ECM_NIC));
    //Intel ethernet adapter as primary adapter
    pNicList[0].macAddr.b[0] = 0x74;
    pNicList[0].macAddr.b[1] = 0x46;
    pNicList[0].macAddr.b[2] = 0xa0;
    pNicList[0].macAddr.b[3] = 0xab;
    pNicList[0].macAddr.b[4] = 0xd2;
    pNicList[0].macAddr.b[5] = 0x30;
    ECM_INIT_MAC(macPrimary, pNicList[0].macAddr);
    //Realtek ethernet adapter as redundant adapter
    pNicList[1].macAddr.b[0] = 0x08;
    pNicList[1].macAddr.b[1] = 0x57;
    pNicList[1].macAddr.b[2] = 0x00;
    pNicList[1].macAddr.b[3] = 0xe9;
    pNicList[1].macAddr.b[4] = 0x5b;
    pNicList[1].macAddr.b[5] = 0xc9;
    ECM_INIT_MAC(macRedundant,pNicList[1].macAddr);
    cfgInitData.cfgDataDevice.macAddr[0]=macPrimary;
    cfgInitData.cfgDataDevice.macAddr[1]=macRedundant;
    result = ecmReadConfiguration(&cfgInitData, &hndDevice, &hndMaster);
    if (result != ECM_SUCCESS) {
        printf("Reading configuration failed with %s\n", ecmResultToString(result));
        ecmInitLibrary(NULL);
        return(result);
    }

    result = ecmGetDataReference(hndMaster, ECM_OUTPUT_DATA, 26, 1, (void **)&pucDo);
    if (result != ECM_SUCCESS) {
        printf("Failed to get reference to pucDO\n");
        ecmDeleteMaster(hndMaster);
        ecmDeleteDevice(hndDevice);
        ecmInitLibrary(NULL);
        return(-1);
    }    

    result = ecmAttachMaster(hndMaster);
    if (result != ECM_SUCCESS) {
        printf("Attaching master failed with %s\n", ecmResultToString(result));
        ecmDeleteMaster(hndMaster);
        ecmDeleteDevice(hndDevice);
        ecmInitLibrary(NULL);
        return(-1);
    }

    ecmSleep(1000);

    ECM_INIT(ProcCtrl);
    ProcCtrl.ulAcyclicPeriod = 1000;
    ProcCtrl.ulAcyclicPrio   = ECM_PRIO_DEFAULT;
    ProcCtrl.ulCyclicPeriod = 80000;
    ProcCtrl.ulCyclicPrio   = ECM_PRIO_DEFAULT;
    ProcCtrl.pfnHandler     = CycleHandler;
    result = ecmProcessControl(hndDevice, &ProcCtrl);
    if (result != ECM_SUCCESS) {
        printf("Creating background worker threads failed with %s\n", ecmResultToString(result));
        ecmDetachMaster(hndMaster);
        ecmDeleteMaster(hndMaster);
        ecmDeleteDevice(hndDevice);
        ecmInitLibrary(NULL);
        return(-1);
    } 
    
    result = ecmRequestState(hndMaster, ECM_DEVICE_STATE_OP, 60000);
    if (result != ECM_SUCCESS) {
        printf("Failed with %s to change network state into state %d !\n", ecmResultToString(result), ECM_DEVICE_STATE_OP);
        ecmDetachMaster(hndMaster);
        ecmDeleteMaster(hndMaster);
        ecmDeleteDevice(hndDevice);
        ecmInitLibrary(NULL);
        return(-1);
    }

    ecmSleep(100000);

    printf("Detaching master...\n");
    result = ecmDetachMaster(hndMaster);
    if (result != ECM_SUCCESS) {
        printf("Failed with %s\n", ecmResultToString(result));
    }

    printf("Deleting master...\n");
    result = ecmDeleteMaster(hndMaster);
    if (result != ECM_SUCCESS) {
        printf("Failed with %s\n", ecmResultToString(result));
    }

    printf("Deleting device...\n");
    result = ecmDeleteDevice(hndDevice);
    if (result != ECM_SUCCESS) {
        printf("Failed with %s\n", ecmResultToString(result));
    }

    ecmInitLibrary(NULL);

    return 0;

}



    // result = ecmGetNicList(NULL, &numAdapter);
    // if (result != ECM_SUCCESS) {
    //     printf("Failed to ecmGetNicList()\n");
    //     ecmInitLibrary(NULL);
    //     return(-1);
    // } else {
    //     pNicList = calloc(numAdapter, sizeof(ECM_NIC));
    //     if (pNicList != NULL) {
    //         ecmGetNicList(pNicList, &numAdapter);
    //         printf("\nAvailable NICs for EtherCAT\n");
    //         printf("---------------------------\n");
    //         for (i = 0; i < (int)numAdapter; i++) {
    //             printf("%d. %s", i, pNicList[i].szName);
    //             if(strstr(pNicList[i].szName, "lld") != NULL) {
    //                 printf(" (V %d.%d.%d)",
    //                     (pNicList[i].usReserved >> 12) & 0x0F,
    //                     (pNicList[i].usReserved >>  8) & 0x0F,
    //                     (pNicList[i].usReserved) & 0xFF);
    //             }
    //             printf("\n   MAC: %02x.%02x.%02x.%02x.%02x.%02x\n",
    //                 pNicList[i].macAddr.b[0], pNicList[i].macAddr.b[1],
    //                 pNicList[i].macAddr.b[2], pNicList[i].macAddr.b[3],
    //                 pNicList[i].macAddr.b[4], pNicList[i].macAddr.b[5]);
                
    //                     // if (iPrimaryNic == i) {
    //                     //     ECM_INIT_MAC(macPrimary, pNicList[i].macAddr);
    //                     // }
    //                     // if (iRedundantNic == i) {
    //                     //     ECM_INIT_MAC(macRedundant, pNicList[i].macAddr);
    //                     // }
    //         }
    //         free(pNicList);
    //     }
    // }
