#include "hdmOperations.h"
#include <stdio.h>

#define TAG "hdmOperations: "

uint32_t hdm_tata_communication(hdmMessage_t *sendmsg, hdmMessage_t *respmsg, uint32_t commandID) {
    TEE_Result ret = HDM_TATA_SUCCESS;
    TEE_UUID selected = HDM_UUID;
    TEE_TASessionHandle session;
    TEE_Param params[4];
    uint32_t returnOrigin = 0;
    uint32_t requestTimeout = 300;
    uint32_t paramTypes = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT, TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE);
    
    printf(TAG "Start communication TA-TA");

    ret = TEE_OpenTASession(&selected, requestTimeout, 0, NULL, &session, &returnOrigin);
    printf(TAG "TEE_OpenTASession - returnOrigin = %x \n", returnOrigin);
    if (ret != TEE_SUCCESS) {
        printf(TAG "TEE_OpenTASession - Error to open Session ret=%x \n", ret);
        ret = HDM_ERROR_SESSION_FAILED;
    } else {
        params[0].memref.buffer = sendmsg;
        params[0].memref.size = sizeof(hdmMessage_t);
        params[1].memref.buffer = respmsg;
        params[1].memref.size = sizeof(hdmMessage_t);

        ret = TEE_InvokeTACommand(session, requestTimeout, commandID, paramTypes, params, &returnOrigin);
        respmsg = params[1].memref.buffer;
        if (ret != TEE_SUCCESS) {
            printf(TAG "TEE_InvokeTACommand - Error to send TA Command ret=%x \n", ret);
            ret = HDM_ERROR_COMMAND_FAILED;
        } else {
            ret = respmsg->header.status;
        }

        TEE_CloseTASession(session);
        printf(TAG "TEE_CloseTASession \n");
    }

    return ret;
}

/**
 * Method to release (unblock) an HDM policy configured by KG.
 * Release flow consists in the following steps:
 * 1. Call TA_Communication_kg_release in Secure World
 * 
 * @return HDM_TATA_SUCCESS or HDM_TATA_ERROR indicating if the operation was success or not
 */
uint32_t TA_Communication_kg_release() {
    TEE_Result ret = HDM_TATA_ERROR;
    hdmMessage_t* sendmsg = NULL;
    hdmMessage_t* respmsg = NULL;

    sendmsg = TEE_Malloc(sizeof(hdmMessage_t), 0);
    if (sendmsg == NULL) {
        printf(TAG "TA_Communication_kg_release : failed to allocate memory");
        return HDM_TATA_ERROR;
    }

    respmsg = TEE_Malloc(sizeof(hdmMessage_t), 0);
    if (respmsg == NULL) {
        printf(TAG "TA_Communication_kg_release : failed to allocate memory");
        TEE_Free(sendmsg);
        return HDM_TATA_ERROR;
    }

    ret = hdm_tata_communication(sendmsg, respmsg, KG_UNBLOCK_CMD);
    if (ret != HDM_TATA_SUCCESS) {
        ret = HDM_TATA_ERROR;
    }
    printf(TAG "TA_Communication_kg_release : ret - (%d) \n", ret);

    TEE_Free(sendmsg);
    TEE_Free(respmsg);

    return ret;
}

/**
 * Method to apply (block) an HDM policy configured by KG.
 * Apply flow consists in the following steps:
 * 1. Call TA_Communication_kg_apply in Secure World
 * 
 * @return HDM_TATA_SUCCESS or HDM_TATA_ERROR indicating if the operation was success or not
 */
uint32_t TA_Communication_kg_apply() {
    TEE_Result ret = HDM_TATA_ERROR;
    hdmMessage_t* sendmsg = NULL;
    hdmMessage_t* respmsg = NULL;

    sendmsg = TEE_Malloc(sizeof(hdmMessage_t), 0);
    if (sendmsg == NULL) {
        printf(TAG "TA_Communication_kg_apply : failed to allocate memory");
        return HDM_TATA_ERROR;
    }

    respmsg = TEE_Malloc(sizeof(hdmMessage_t), 0);
    if (respmsg == NULL) {
        printf(TAG "TA_Communication_kg_apply : failed to allocate memory");
        TEE_Free(sendmsg);
        return HDM_TATA_ERROR;
    }

    ret = hdm_tata_communication(sendmsg, respmsg, KG_BLOCK_CMD);
    if (ret != HDM_TATA_SUCCESS) {
        ret = HDM_TATA_ERROR;
    }

    printf(TAG "TA_Communication_kg_apply : ret - (%d) \n", ret);  

    TEE_Free(sendmsg);
    TEE_Free(respmsg);

    return ret;
}

/**
 * Method to get status of HDM policy configured by KG.
 * @param *kg_status[out] KG Status struct
 * 
 * @return
 * Return and kg_status will be HDM_TATA_ERROR if error occurs
 * For success case HDM_TATA_SUCCESS will be returned and kg_status will be filled with KG policy info
 */
uint32_t TA_Communication_kg_get_status(tz_hdm_kg_status_t *kg_status) {
    TEE_Result ret = HDM_TATA_ERROR;
    hdmMessage_t* sendmsg = NULL;
    hdmMessage_t* respmsg = NULL;

    sendmsg = TEE_Malloc(sizeof(hdmMessage_t), 0);
    if (sendmsg == NULL) {
        printf(TAG "TA_Communication_kg_get_status : failed to allocate memory");
        return HDM_TATA_ERROR;
    }

    respmsg = TEE_Malloc(sizeof(hdmMessage_t), 0);
    if (respmsg == NULL) {
        printf(TAG "TA_Communication_kg_get_status : failed to allocate memory");
        TEE_Free(sendmsg);
        return HDM_TATA_ERROR;
    }

    ret = hdm_tata_communication(sendmsg, respmsg, KG_GET_STATUS_CMD);
    kg_status->device_block = respmsg->tata_message.kg_status.device_block;
    kg_status->compromise_block = respmsg->tata_message.kg_status.compromise_block;

    printf(TAG "TA_Communication_kg_get_status : ret - (%d) \n", ret);

    TEE_Free(sendmsg);
    TEE_Free(respmsg);

    return ret;
}
