#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 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");
                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(){
    //LED light sequence
    // 1.4(0x08), 1.3(0x04), 1.2(0x02), 1.1(0x01), 
    // 2.4(0x08), 2.3(0x04), 2.2(0x02), 2.1(0x01),  
    // 2.5(0x10), 2.6(0x20), 2.7(0x40), 2.8(0x80),
    // 1.5(0x10), 1.6(0x20), 1.7(0x40), 1.8(0x80),   
    static uint8_t ucData[16]={0x08,0x04,0x02,0x01,0x08,0x04,0x02,0x01,0x10,0x20,0x40,0x80,0x10,0x20,0x40,0x80};
    static int8_t i=0;
    
    if (i == 4) {
        *pucDo = 0;
        pucDo = pucDo + 3;
    } 
    if (i == 12) {
        *pucDo = 0;
        pucDo = pucDo - 3;
    } 
    if (i == 16) {
        i=0;
    } 
    *pucDo = ucData[i];
    i++;
}


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_VAR_DESC VarDesc;
    ECM_PROC_CTRL ProcCtrl;
    ECM_COPY_VECTOR *pCopyVector;
    uint32_t ulEntries;
    ECM_COPY_VECTOR *pCopyVectorInput;
    uint32_t ulEntriesInput;
    int j;
    int i;

    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 = "./eni_dual_packed_ecx-dio8.xml";    
    cfgInitData.Config.File.pszEniFile = "./eni_dual_ecx-dio8.xml";
    cfgInitData.ulFlags = ECM_FLAG_CFG_KEEP_PROCVARS | ECM_FLAG_CFG_VIRTUAL_VARS;
    result = ecmReadConfiguration(&cfgInitData, &hndDevice, &hndMaster);
    if (result != ECM_SUCCESS) {
        printf("Reading configuration failed with %s\n", ecmResultToString(result));
        ecmInitLibrary(NULL);
        return(result);
    }

    result = ecmGetCopyVector(hndMaster, NULL, &ulEntries, ECM_OUTPUT_DATA);
    printf("ulEntries is %d \n", ulEntries);
    if (ECM_SUCCESS == result) {
        pCopyVector = calloc((size_t)ulEntries, sizeof(ECM_COPY_VECTOR));
        if (pCopyVector != NULL) {
            result = ecmGetCopyVector(hndMaster, pCopyVector, &ulEntries, ECM_OUTPUT_DATA);
            if (ECM_SUCCESS == result) {
                printf("Output process image:\n");
                printf("---------------------\n");
                for (i = 0; i < ulEntries; i++) {
                    printf("Offset = %08lu Size: %08lu\n",
                            (unsigned long)pCopyVector[i].ulOffset,
                            (unsigned long)pCopyVector[i].ulSize);
                }
            }
        }
    }

    result = ecmGetDataReference(hndMaster, ECM_OUTPUT_DATA, pCopyVector->ulOffset, pCopyVector->ulSize, (void **)&pucDo);
    if (result != ECM_SUCCESS) {
        printf("Failed to get reference to output process data\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 = cfgInitData.cfgDataDevice.ulCycleTime;
    ProcCtrl.ulCyclicPeriod = 100000;
    ProcCtrl.ulCyclicPrio   = ECM_PRIO_DEFAULT;
    ProcCtrl.pfnHandler     = CycleHandler;
    // ProcCtrl.pfnBeginCycle  = CycleStartHandler;
    // ProcCtrl.pfnEndCycle    = CycleEndHandler;
    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);
    } else {
        printf("Worker thread cycle times (microseconds) (Cyclic: %d)\n", ProcCtrl.ulCyclicPeriod);
    }


    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(5000);

do
{
    ecmSleep(100);
    if (ProcCtrl.ulCyclicPeriod > 10000) {
        if (ProcCtrl.ulCyclicPeriod == 40000)
            ecmSleep(5000);
        if (ProcCtrl.ulCyclicPeriod == 20000)
            ecmSleep(5000);
        ProcCtrl.ulCyclicPeriod -= 500;
        }
    else
        break;
    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);
    } else {
        printf("Worker thread cycle times (microseconds) (Cyclic: %d)\n", ProcCtrl.ulCyclicPeriod);
    }
}while (1);


ecmSleep(5000);

    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);

    free(pCopyVector);

    return 0;

}


