/**
 * API to register for particular OxM.
 *
 * @param[in] Ownership transfer method.
 * @param[in] Implementation of callback functions for owership transfer.
 * @return  OC_STACK_OK in case of success and other value otherwise.
 */
OCStackResult OCSetOwnerTransferCallbackData(OicSecOxm_t oxm, OTMCallbackData_t* callbackData)
{
    if(NULL == callbackData)
    {
        return OC_STACK_INVALID_CALLBACK ;
    }

    return OTMSetOwnershipTransferCallbackData(oxm, callbackData);
}
/**
 * Provisioning client sample using ProvisioningAPI
 */
int main()
{
    OCStackResult res = OC_STACK_OK;
    int unused;
    (void)unused;

    // Initialize Persistent Storage for SVR database
    OCPersistentStorage ps = { .open = NULL,
                               .read = NULL,
                               .write = NULL,
                               .close = NULL,
                               .unlink = NULL };
    ps.open = client_fopen;
    ps.read = fread;
    ps.write = fwrite;
    ps.close = fclose;
    ps.unlink = unlink;
    OCRegisterPersistentStorageHandler(&ps);

    if (OC_STACK_OK != OCInit(NULL, 0, OC_CLIENT_SERVER))
    {
        OC_LOG(ERROR, TAG, "OCStack init error");
        goto error;
    }

    OCProvisionDev_t* pDeviceList = NULL;
    res = OCDiscoverUnownedDevices(PREDEFINED_TIMEOUT, &pDeviceList);
    if(OC_STACK_OK != res)
    {
        OC_LOG_V(ERROR, TAG, "Failed to PMDeviceDiscovery : %d", res);
        goto error;
    }

    OCProvisionDev_t* pCurDev = pDeviceList;
    int i;
    while(pCurDev !=NULL)
    {
        for(i = 0; i < UUID_LENGTH; i++)
            printf("%c", pCurDev->doxm->deviceID.id[i]);
        printf("\n");
        pCurDev = pCurDev->next;
    }

    //Register callback function to each OxM
    OTMCallbackData_t justWorksCBData = {.loadSecretCB=NULL,
                                         .createSecureSessionCB=NULL,
                                         .createSelectOxmPayloadCB=NULL,
                                         .createOwnerTransferPayloadCB=NULL};
    justWorksCBData.loadSecretCB = LoadSecretJustWorksCallback;
    justWorksCBData.createSecureSessionCB = CreateSecureSessionJustWorksCallback;
    justWorksCBData.createSelectOxmPayloadCB = CreateJustWorksSelectOxmPayload;
    justWorksCBData.createOwnerTransferPayloadCB = CreateJustWorksOwnerTransferPayload;
    OTMSetOwnershipTransferCallbackData(OIC_JUST_WORKS, &justWorksCBData);

    OTMCallbackData_t pinBasedCBData = {.loadSecretCB=NULL,
                                         .createSecureSessionCB=NULL,
                                         .createSelectOxmPayloadCB=NULL,
                                         .createOwnerTransferPayloadCB=NULL};
    pinBasedCBData.loadSecretCB = InputPinCodeCallback;
    pinBasedCBData.createSecureSessionCB = CreateSecureSessionRandomPinCallbak;
    pinBasedCBData.createSelectOxmPayloadCB = CreatePinBasedSelectOxmPayload;
    pinBasedCBData.createOwnerTransferPayloadCB = CreatePinBasedOwnerTransferPayload;
    OTMSetOwnershipTransferCallbackData(OIC_RANDOM_DEVICE_PIN, &pinBasedCBData);

    SetInputPinCB(&InputPinCB);

    char* myContext = "OTM Context";
    //Perform ownership transfer
    res = OCDoOwnershipTransfer((void*)myContext, pDeviceList, OwnershipTransferCB);
    if(OC_STACK_OK == res)
    {
        OC_LOG(INFO, TAG, "Request for ownership transfer is sent successfully.");
    }
    else
    {
        OC_LOG_V(ERROR, TAG, "Failed to OCDoOwnershipTransfer : %d", res);
    }

    gOwnershipState = 0;
    while ( gOwnershipState == 0 )
    {
        if (OCProcess() != OC_STACK_OK)
        {
            OC_LOG(ERROR, TAG, "OCStack process error");
            goto error;
        }
        sleep(1);
    }

// Credential & ACL provisioning between two devices.

    OCProvisionDev_t *pOwnedList = NULL;
    OCProvisionDev_t *pOwnedDevices [MAX_OWNED_DEVICE] = {0,};
    int nOwnedDevice = 0;

    res = OCDiscoverOwnedDevices(PREDEFINED_TIMEOUT, &pOwnedList);
    if (OC_STACK_OK == res)
    {
        printf("################## Owned Device List #######################\n");
        while (pOwnedList != NULL)
        {
            nOwnedDevice ++;
            printf(" %d : ", nOwnedDevice);
            for (int i = 0; i < UUID_LENGTH; i++)
            {
                printf("%c", pOwnedList->doxm->deviceID.id[i]);
            }
            printf("\n");
            pOwnedDevices[nOwnedDevice] = pOwnedList;
            pOwnedList = pOwnedList->next;
        }
    }
    else
    {
        OC_LOG(ERROR, TAG, "Error while Owned Device Discovery");
    }

    int Device1 = 0;
    int Device2 = 0;

    printf("Select 2 devices for Credential & ACL provisioning\n");
    printf("Device 1: ");
    unused = scanf("%d", &Device1);
    printf("Device 2: ");
    unused = scanf("%d", &Device2);


    gAcl1 = (OicSecAcl_t *)OICCalloc(1,sizeof(OicSecAcl_t));
    if (NULL == gAcl1)
    {
        OC_LOG(ERROR, TAG, "Error while memory allocation");
        goto error;
    }

    gAcl2 = (OicSecAcl_t *)OICCalloc(1,sizeof(OicSecAcl_t));
    if (NULL == gAcl2)
    {
        OC_LOG(ERROR, TAG, "Error while memory allocation");
        goto error;
    }

    printf("Input ACL for Device1\n");
    if ( 0 == InputACL(gAcl1))
    {
        printf("Success Input ACL\n");
    }
    else
    {
        OC_LOG(ERROR, TAG, "InputACL error");
        goto error;
    }

    printf("Input ACL for Device2\n");
    if (0 == InputACL(gAcl2))
    {
        printf("Success Input ACL\n");
    }
    else
    {
        OC_LOG(ERROR, TAG, "InputACL error");
        goto error;
    }
    char *ctx = "DUMMY";
    OCProvisionPairwiseDevices(ctx,SYMMETRIC_PAIR_WISE_KEY, OWNER_PSK_LENGTH_128, pOwnedDevices[Device1],
                           gAcl1, pOwnedDevices[Device2], gAcl2, ProvisionPairwiseCB);

    gOwnershipState = 0;
    while ( gOwnershipState == 0 )
    {
        if (OCProcess() != OC_STACK_OK)
        {
            OC_LOG(ERROR, TAG, "OCStack process error");
            goto error;
        }
        sleep(1);
    }

    if (OCStop() != OC_STACK_OK)
    {
        OC_LOG(ERROR, TAG, "OCStack process error");
        goto error;
    }

error:
    deleteACL(gAcl1);
    deleteACL(gAcl2);
    OCDeleteDiscoveredDevices(&pDeviceList);
    OCDeleteDiscoveredDevices(&pOwnedList);

    return 0;
}