/**
 * Callback handler of default ACL provisioning.
 *
 * @param[in] ctx             ctx value passed to callback from calling function.
 * @param[in] UNUSED          handle to an invocation
 * @param[in] clientResponse  Response from queries to remote servers.
 * @return  OC_STACK_DELETE_TRANSACTION to delete the transaction
 *          and OC_STACK_KEEP_TRANSACTION to keep it.
 */
static OCStackApplicationResult ProvisionDefaultACLCB(void *ctx, OCDoHandle UNUSED,
                                                       OCClientResponse *clientResponse)
{
    OC_LOG_V(INFO, TAG, "IN ProvisionDefaultACLCB.");

    VERIFY_NON_NULL(TAG, clientResponse, ERROR);
    VERIFY_NON_NULL(TAG, ctx, ERROR);

    OTMContext_t* otmCtx = (OTMContext_t*) ctx;
    (void)UNUSED;

    if (OC_STACK_RESOURCE_CREATED == clientResponse->result)
    {
        OC_LOG_V(INFO, TAG, "Staring commit hash task.");
        // TODO hash currently have fixed value 0.
        uint16_t aclHash = 0;
        otmCtx->selectedDeviceInfo->pstat->commitHash = aclHash;
        otmCtx->selectedDeviceInfo->pstat->tm = NORMAL;
        OCSecurityPayload* secPayload = (OCSecurityPayload*)OICCalloc(1, sizeof(OCSecurityPayload));
        if(!secPayload)
        {
            OC_LOG(ERROR, TAG, "Failed to memory allocation");
            return OC_STACK_NO_MEMORY;
        }
        secPayload->base.type = PAYLOAD_TYPE_SECURITY;
        secPayload->securityData = BinToPstatJSON(otmCtx->selectedDeviceInfo->pstat);
        if (NULL == secPayload->securityData)
        {
            OICFree(secPayload);
            SetResult(otmCtx, OC_STACK_INVALID_JSON);
            return OC_STACK_DELETE_TRANSACTION;
        }
        OC_LOG_V(INFO, TAG, "Created payload for commit hash: %s",secPayload->securityData);

        char query[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};
        if(!PMGenerateQuery(true,
                            otmCtx->selectedDeviceInfo->endpoint.addr,
                            otmCtx->selectedDeviceInfo->securePort,
                            otmCtx->selectedDeviceInfo->connType,
                            query, sizeof(query), OIC_RSRC_PSTAT_URI))
        {
            OC_LOG(ERROR, TAG, "ProvisionDefaultACLCB : Failed to generate query");
            return OC_STACK_ERROR;
        }
        OC_LOG_V(DEBUG, TAG, "Query=%s", query);

        OCCallbackData cbData = {.context=NULL, .cb=NULL, .cd=NULL};
        cbData.cb = &FinalizeProvisioningCB;
        cbData.context = (void*)otmCtx;
        cbData.cd = NULL;
        OCStackResult ret = OCDoResource(NULL, OC_REST_PUT, query, 0, (OCPayload*)secPayload,
                otmCtx->selectedDeviceInfo->connType, OC_HIGH_QOS, &cbData, NULL, 0);
        OC_LOG_V(INFO, TAG, "OCDoResource returned: %d",ret);
        if (ret != OC_STACK_OK)
        {
            OC_LOG(ERROR, TAG, "OCStack resource error");
            SetResult(otmCtx, ret);
        }
    }
static OCStackResult PutOwnerTransferModeToResource(OTMContext_t* otmCtx)
{
    OC_LOG(DEBUG, TAG, "IN PutOwnerTransferModeToResource");

    if(!otmCtx || !otmCtx->selectedDeviceInfo)
    {
        OC_LOG(ERROR, TAG, "Invalid parameters");
        return OC_STACK_INVALID_PARAM;
    }

    OCProvisionDev_t* deviceInfo = otmCtx->selectedDeviceInfo;
    OicSecOxm_t selectedOxm = deviceInfo->doxm->oxmSel;
    char query[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};

    if(!PMGenerateQuery(false,
                        deviceInfo->endpoint.addr, deviceInfo->endpoint.port,
                        deviceInfo->connType,
                        query, sizeof(query), OIC_RSRC_DOXM_URI))
    {
        OC_LOG(ERROR, TAG, "PutOwnerTransferModeToResource : Failed to generate query");
        return OC_STACK_ERROR;
    }
    OC_LOG_V(DEBUG, TAG, "Query=%s", query);
    OCSecurityPayload* secPayload = (OCSecurityPayload*)OICCalloc(1, sizeof(OCSecurityPayload));
    if(!secPayload)
    {
        OC_LOG(ERROR, TAG, "Failed to memory allocation");
        return OC_STACK_NO_MEMORY;
    }
    secPayload->base.type = PAYLOAD_TYPE_SECURITY;
    secPayload->securityData = g_OTMDatas[selectedOxm].createSelectOxmPayloadCB(otmCtx);
    if (NULL == secPayload->securityData)
    {
        OICFree(secPayload);
        OC_LOG(ERROR, TAG, "Error while converting bin to json");
        return OC_STACK_ERROR;
    }
    OC_LOG_V(DEBUG, TAG, "Payload : %s", secPayload->securityData);

    OCCallbackData cbData;
    cbData.cb = &OwnerTransferModeHandler;
    cbData.context = (void *)otmCtx;
    cbData.cd = NULL;
    OCStackResult res = OCDoResource(NULL, OC_REST_PUT, query,
                                     &deviceInfo->endpoint, (OCPayload*)secPayload,
                                     deviceInfo->connType, OC_LOW_QOS, &cbData, NULL, 0);
    if (res != OC_STACK_OK)
    {
        OC_LOG(ERROR, TAG, "OCStack resource error");
    }

    OC_LOG(DEBUG, TAG, "OUT PutOwnerTransferModeToResource");

    return res;
}
static OCStackResult PutUpdateOperationMode(OTMContext_t* otmCtx,
                                    OicSecDpom_t selectedOperationMode)
{
    OC_LOG(DEBUG, TAG, "IN PutUpdateOperationMode");

    if(!otmCtx || !otmCtx->selectedDeviceInfo)
    {
        return OC_STACK_INVALID_PARAM;
    }

    OCProvisionDev_t* deviceInfo = otmCtx->selectedDeviceInfo;
    char query[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};
    if(!PMGenerateQuery(false,
                        deviceInfo->endpoint.addr, deviceInfo->endpoint.port,
                        deviceInfo->connType,
                        query, sizeof(query), OIC_RSRC_PSTAT_URI))
    {
        OC_LOG(ERROR, TAG, "PutUpdateOperationMode : Failed to generate query");
        return OC_STACK_ERROR;
    }
    OC_LOG_V(DEBUG, TAG, "Query=%s", query);

    deviceInfo->pstat->om = selectedOperationMode;

    OCSecurityPayload* secPayload = (OCSecurityPayload*)OICCalloc(1, sizeof(OCSecurityPayload));
    if(!secPayload)
    {
        OC_LOG(ERROR, TAG, "Failed to memory allocation");
        return OC_STACK_NO_MEMORY;
    }
    secPayload->base.type = PAYLOAD_TYPE_SECURITY;
    secPayload->securityData = BinToPstatJSON(deviceInfo->pstat);
    if (NULL == secPayload->securityData)
    {
        OICFree(secPayload);
        OC_LOG(ERROR, TAG, "Error while converting pstat bin to json");
        return OC_STACK_INVALID_PARAM;
    }

    OCCallbackData cbData;
    cbData.cb = &OperationModeUpdateHandler;
    cbData.context = (void *)otmCtx;
    cbData.cd = NULL;
    OCStackResult res = OCDoResource(NULL, OC_REST_PUT, query, 0, (OCPayload*)secPayload,
                                     deviceInfo->connType, OC_LOW_QOS, &cbData, NULL, 0);
    if (res != OC_STACK_OK)
    {
        OC_LOG(ERROR, TAG, "OCStack resource error");
    }

    OC_LOG(DEBUG, TAG, "OUT PutUpdateOperationMode");

    return res;
}
/**
 * Internal function for handling credential generation and sending credential to resource server.
 *
 * @param[in] cred Instance of cred resource.
 * @param[in] deviceInfo information about device to which credential is to be provisioned.
 * @param[in] responseHandler callbak called by OC stack when request API receives response.
 * @return  OC_STACK_OK in case of success and other value otherwise.
 */
static OCStackResult provisionCredentials(const OicSecCred_t *cred,
        const OCProvisionDev_t *deviceInfo, CredentialData_t *credData,
        OCClientResponseHandler responseHandler)
{
    OCSecurityPayload* secPayload = (OCSecurityPayload*)OICCalloc(1, sizeof(OCSecurityPayload));
    if(!secPayload)
    {
        OC_LOG(ERROR, TAG, "Failed to memory allocation");
        return OC_STACK_NO_MEMORY;
    }
    secPayload->base.type = PAYLOAD_TYPE_SECURITY;
    secPayload->securityData = BinToCredJSON(cred);
    if(NULL == secPayload->securityData)
    {
        OICFree(secPayload);
        OC_LOG(ERROR, TAG, "Failed to BinToCredJSON");
        return OC_STACK_NO_MEMORY;
    }

    OC_LOG_V(INFO, TAG, "Credential for provisioning : %s",secPayload->securityData);
    char query[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};
    if(!PMGenerateQuery(true,
                        deviceInfo->endpoint.addr,
                        deviceInfo->securePort,
                        deviceInfo->connType,
                        query, sizeof(query), OIC_RSRC_CRED_URI))
    {
        OC_LOG(ERROR, TAG, "DeviceDiscoveryHandler : Failed to generate query");
        return OC_STACK_ERROR;
    }
    OC_LOG_V(DEBUG, TAG, "Query=%s", query);

    OCCallbackData cbData = {.context=NULL, .cb=NULL, .cd=NULL};
    cbData.cb = responseHandler;
    cbData.context = (void *) credData;
    cbData.cd = NULL;

    OCDoHandle handle = NULL;
    OCMethod method = OC_REST_POST;
    OCStackResult ret = OCDoResource(&handle, method, query, 0, (OCPayload*)secPayload,
            deviceInfo->connType, OC_HIGH_QOS, &cbData, NULL, 0);
    OC_LOG_V(INFO, TAG, "OCDoResource::Credential provisioning returned : %d",ret);
    if (ret != OC_STACK_OK)
    {
        OC_LOG(ERROR, TAG, "OCStack resource error");
        return ret;
    }
    return OC_STACK_OK;
}
static OCStackResult GetProvisioningStatusResource(OTMContext_t* otmCtx)
{
    OC_LOG(DEBUG, TAG, "IN GetProvisioningStatusResource");

    if(!otmCtx || !otmCtx->selectedDeviceInfo)
    {
        OC_LOG(ERROR, TAG, "Invailed parameters");
        return OC_STACK_INVALID_PARAM;
    }

    OCProvisionDev_t* deviceInfo = otmCtx->selectedDeviceInfo;
    char query[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};
    if(!PMGenerateQuery(false,
                        deviceInfo->endpoint.addr, deviceInfo->endpoint.port,
                        deviceInfo->connType,
                        query, sizeof(query), OIC_RSRC_PSTAT_URI))
    {
        OC_LOG(ERROR, TAG, "GetProvisioningStatusResource : Failed to generate query");
        return OC_STACK_ERROR;
    }
    OC_LOG_V(DEBUG, TAG, "Query=%s", query);

    OCCallbackData cbData;
    cbData.cb = &ListMethodsHandler;
    cbData.context = (void *)otmCtx;
    cbData.cd = NULL;
    OCStackResult res = OCDoResource(NULL, OC_REST_GET, query, NULL, NULL,
                                     deviceInfo->connType, OC_LOW_QOS, &cbData, NULL, 0);
    if (res != OC_STACK_OK)
    {
        OC_LOG(ERROR, TAG, "OCStack resource error");
    }

    OC_LOG(DEBUG, TAG, "OUT GetProvisioningStatusResource");

    return res;
}
OCStackResult SRPProvisionACL(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
        OicSecAcl_t *acl, OCProvisionResultCB resultCallback)
{
    VERIFY_NON_NULL(TAG, selectedDeviceInfo, ERROR,  OC_STACK_INVALID_PARAM);
    VERIFY_NON_NULL(TAG, acl, ERROR,  OC_STACK_INVALID_PARAM);
    VERIFY_NON_NULL(TAG, resultCallback, ERROR,  OC_STACK_INVALID_CALLBACK);

    OCSecurityPayload* secPayload = (OCSecurityPayload*)OICCalloc(1, sizeof(OCSecurityPayload));
    if(!secPayload)
    {
        OC_LOG(ERROR, TAG, "Failed to memory allocation");
        return OC_STACK_NO_MEMORY;
    }
    secPayload->base.type = PAYLOAD_TYPE_SECURITY;
    secPayload->securityData = BinToAclJSON(acl);
    if(NULL == secPayload->securityData)
    {
        OICFree(secPayload);
        OC_LOG(ERROR, TAG, "Failed to BinToAclJSON");
        return OC_STACK_NO_MEMORY;
    }
    OC_LOG_V(INFO, TAG, "ACL : %s", secPayload->securityData);

    char query[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};
    if(!PMGenerateQuery(true,
                        selectedDeviceInfo->endpoint.addr,
                        selectedDeviceInfo->securePort,
                        selectedDeviceInfo->connType,
                        query, sizeof(query), OIC_RSRC_ACL_URI))
    {
        OC_LOG(ERROR, TAG, "DeviceDiscoveryHandler : Failed to generate query");
        return OC_STACK_ERROR;
    }
    OC_LOG_V(DEBUG, TAG, "Query=%s", query);

    OCCallbackData cbData =  {.context=NULL, .cb=NULL, .cd=NULL};
    cbData.cb = &SRPProvisionACLCB;
    ACLData_t *aclData = (ACLData_t *) OICMalloc(sizeof(ACLData_t));
    if (aclData == NULL)
    {
        OICFree(secPayload);
        OC_LOG(ERROR, TAG, "Unable to allocate memory");
        return OC_STACK_NO_MEMORY;
    }
    memset(aclData, 0x00, sizeof(ACLData_t));
    aclData->deviceInfo = selectedDeviceInfo;
    aclData->resultCallback = resultCallback;
    aclData->numOfResults=0;
    aclData->ctx = ctx;
    // call to provision ACL to device1.
    int noOfRiCalls = 1;
    aclData->resArr = (OCProvisionResult_t*)OICMalloc(sizeof(OCProvisionResult_t)*noOfRiCalls);
    if (aclData->resArr == NULL)
    {
        OICFree(secPayload);
        OC_LOG(ERROR, TAG, "Unable to allocate memory");
        return OC_STACK_NO_MEMORY;
    }
    memset(aclData->resArr, 0x00, sizeof(sizeof(OCProvisionResult_t)*noOfRiCalls));
    cbData.context = (void *)aclData;
    cbData.cd = NULL;
    OCMethod method = OC_REST_POST;
    OCDoHandle handle = NULL;
    OC_LOG(DEBUG, TAG, "Sending ACL info to resource server");
    OCStackResult ret = OCDoResource(&handle, method, query,
            &selectedDeviceInfo->endpoint, (OCPayload*)secPayload,
            selectedDeviceInfo->connType, OC_HIGH_QOS, &cbData, NULL, 0);
    if (ret != OC_STACK_OK)
    {
        OICFree(aclData->resArr);
        OICFree(aclData);
    }
    VERIFY_SUCCESS(TAG, (OC_STACK_OK == ret), ERROR, OC_STACK_ERROR);
    return OC_STACK_OK;
}