#include "tee_internal_api.h"

//#include <stdint.h>

#include "qsee_log.h"
#include "qsee_core.h"

#ifdef KG_CHECK_PROCA_SUPPORTED
#include "pa_tz_api.h"
#endif

#include "process_cmd.h"

char TZ_APP_NAME[] = { "tz_kg" };

/**
 * Global In/Out messages
 */
static tci_message_t rcvMsgCopy;
static tci_message_t rspMsgCopy;

void tz_app_init(void) {
    /*  App specific initialization code*/
    qsee_log(QSEE_LOG_MSG_DEBUG, "TA_KG_Init\n");
}

#ifndef RSP_ID
#define RSP_ID_MASK (1U << 31)
#define RSP_ID(cmdId) (((uint32_t)(cmdId)) | RSP_ID_MASK)
#endif

void tz_app_cmd_handler(void *cmd, uint32_t cmdlen, 
                        void *resp, uint32_t resplen) {
    qsee_log(QSEE_LOG_MSG_DEBUG, "TA_KG_cmd\n");

    uint32_t commandId = 0;
    uint32_t ret = KG_SUCCESS;

    if (qsee_is_ns_range(cmd, cmdlen)) {
        qsee_log(QSEE_LOG_MSG_ERROR, "cmd data is not in secure memory.\n");
        ret = KG_ERR_PERMISSION_DENIED;
        goto out;
    }

    if (qsee_is_ns_range(resp, resplen)) {
        qsee_log(QSEE_LOG_MSG_ERROR, "rsp data is not in secure memory.\n");
        ret = KG_ERR_PERMISSION_DENIED;
        goto out;
    }

    
    if ((NULL == cmd) || (NULL == resp)) {
        qsee_log(QSEE_LOG_MSG_ERROR,
        "tz_app_cmd_handler invalid input");
        ret = KG_ERR_INVALID_BUFFER;
        goto out;
    }

    
    if (cmdlen < sizeof(tci_message_t)) {
        qsee_log(QSEE_LOG_MSG_ERROR,
                "cmdlen = %u < %u = sizeof(tciMessage_t)",      
                cmdlen, sizeof(tci_message_t));
        ret = KG_ERR_INVALID_BUFFER;
        goto out;
    }

    if (resplen < sizeof(tci_message_t)) {
        qsee_log(QSEE_LOG_MSG_ERROR,    
            "rsplen = %u < %u = sizeof(tciMessage_t)",
            resplen, sizeof(tci_message_t));
        ret = KG_ERR_INVALID_BUFFER;
        goto out;
    }

    tci_message_t* cmdmsg = NULL;
    tci_message_t* respmsg = NULL;

    /* Create local copy of WSM buffer to prevent Race Condition */
    TEE_MemFill(&rcvMsgCopy, 0, sizeof(tci_message_t));
    TEE_MemFill(&rspMsgCopy, 0, sizeof(tci_message_t));
    TEE_MemMove(&rcvMsgCopy, cmd, sizeof(tci_message_t));

    cmdmsg = &rcvMsgCopy;
    respmsg = &rspMsgCopy;

    commandId = cmdmsg->header.id;
    qsee_log(QSEE_LOG_MSG_DEBUG, "KG processing cmd_id: %08X)\n", commandId);

    if (cmdmsg->header.content_id == KG_CMD_PROTOCOL_BL) {
        ret = process_blcmd(commandId, cmdmsg, respmsg);
    } else {
#ifdef KG_CHECK_PROCA_SUPPORTED
/* PROCA START : check caller process has permission */
        PaTzResult auth_result;
        PaHandler handler;
        PaTzResult result;
        const char* allowed_process = "/vendor/bin/hw/vendor.samsung.hardware.tlc.kg@1.0-service";
    
        // Create PaHandler
        result = PaTzHandlerCreateFromProcessName(allowed_process, &handler);
        if (PA_TZ_SUCCESS != result) {
            qsee_log(QSEE_LOG_MSG_ERROR, "KG PROCA PaTzHandlerCreateFromProcessName Error : %x\n", result);
            ret = KG_ERR_PERMISSION_DENIED;
            goto out;
        }
    
        auth_result = PaTzAuthenticateWithCommandBuffer(handler, cmd, sizeof(tci_message_t), NULL);
        PaTzHandlerDestroy(&handler);
    
        if (PA_TZ_AF_DEVICE_IS_COMPROMISED == auth_result) {
            qsee_log(QSEE_LOG_MSG_ERROR, "KG PROCA does not work on custom kernel.\n");
        } else if (PA_TZ_PROCA_NOT_SUPPORTED == auth_result) {
            qsee_log(QSEE_LOG_MSG_ERROR, "KG PROCA This device does not support PROCA.\n");
        } else if (PA_TZ_SUCCESS != auth_result) {
            qsee_log(QSEE_LOG_MSG_ERROR, "KG PROCA Authentication is failed. : %x\n", auth_result);
            ret =  KG_ERR_PERMISSION_DENIED;
            goto out;
        }
        qsee_log(QSEE_LOG_MSG_ERROR, "KG PROCA Authentication success\n");
/* PROCA END */
#endif    
        ret = process_cmd(commandId, cmdmsg, respmsg);
    }

out: 
    respmsg->header.id = RSP_ID(commandId);
    respmsg->header.status = ret;


    qsee_log(QSEE_LOG_MSG_DEBUG, "respmsg->header.id = 0x%08X", respmsg->header.id);
    qsee_log(QSEE_LOG_MSG_DEBUG, "respmsg->header.status = 0x%08X", respmsg->header.status);
    qsee_log(QSEE_LOG_MSG_DEBUG, "Returning = 0x%08X", ret);

    TEE_MemMove(resp, &rspMsgCopy, sizeof(tci_message_t));

    TEE_MemFill(&rcvMsgCopy, 0, sizeof(tci_message_t));
    TEE_MemFill(&rspMsgCopy, 0, sizeof(tci_message_t));

    return;
}

void tz_app_shutdown(void) {
    /* App specific shutdown code*/
    qsee_log(QSEE_LOG_MSG_DEBUG, "App shutdown\n");
    return;
}
