#include "bf_spay_tui_entry.h"

#include "process_cmd.h"
#include "tee_internal_api.h"
#include "tees_tui.h"
#include "tui.h"

#include "tl_spay_tui_msg.h"
#include "tlPinpadLayout.h"
#include "spay_tui_common.h"
#include "bf_spay_tui.h"
#include "bf_pthread_create_common.h"

#include "tci.h"
#include "spay_init_tl.h"
#include "spay_msk_tl.h"
#include "spay_pin_random_util_tl.h"
#include "spay_authenticate_transaction_tl.h"
#include "TZ_Vendor_tl.h"
#include "TZ_Vendor_debug_tl.h"
#include "ta_log.h"
#include "tl_tui_bc_error_msg.h"

#define MSG_ALIGN_SIZE  0x40
#define MSG_ALIGN_MASK  (MSG_ALIGN_SIZE -1)
#define MSG_ALIGN(x)    ((x + MSG_ALIGN_SIZE) &(~MSG_ALIGN_MASK))

#ifdef ENABLE_TA_ERR_MSG
    char g_err_descrp[TA_MAX_ERROR_STR_LEN];
    int g_err_recorded = 0;
#endif

static bool initialized;
static uint8_t g_msg[MSG_ALIGN(sizeof(tciMessage_t))];

TEE_Result TA_CreateEntryPoint(void)
{
#ifdef TA_VERSION
    TTY_LOG("%s TA VERSION is: %s", LOG_TAG, TA_VERSION);
#else
    TTY_LOG("%s TA VERSION is UNKNOWN", LOG_TAG);
#endif

     init();

    return TEE_SUCCESS;
}

void TA_DestroyEntryPoint(void)
{
    TTY_LOG("%s TA_DestroyEntryPoint is success", LOG_TAG);
}

TEE_Result TA_OpenSessionEntryPoint(uint32_t paramTypes, TEE_Param params[4], void **sessionContext)
{
    (void)paramTypes;
    (void)params;
    (void)sessionContext;
    TTY_LOG("%s TA_OpenSessionEntryPoint is success", LOG_TAG);
    return TEE_SUCCESS;
}

void TA_CloseSessionEntryPoint(void *sessionContext)
{
    (void)sessionContext;
    TTY_LOG("%s TA_CloseSessionEntryPoint is success", LOG_TAG);
    return;
}

TEE_Result TA_InvokeCommandEntryPoint(void *sessionContext, uint32_t commandID, uint32_t paramTypes, TEE_Param params[4])
{
    (void)sessionContext;
    (void)paramTypes;
    TTY_LOG("%s TA_InvokeCommandEntryPoint", LOG_TAG);

    uint32_t ret = TEE_RESULT_NOT_READY;
    uint32_t sendmsg_len = 0;
    uint32_t respmsg_len = 0;
    uint32_t g_msg_len = sizeof(g_msg);

    tciMessage_t *sendmsg = NULL;
    tciMessage_t *respmsg = NULL;

    if (paramTypes != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT, TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE)) {
        TTY_LOG("%s Bad Parameters in invoke command", LOG_TAG);
        return TEE_ERROR_BAD_PARAMETERS;
    }

    /* param[0]: INOUT */
    if (TEES_IsREESharedMemory (TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_WRITE, 
        params[0].memref.buffer, params[0].memref.size) != TEE_SUCCESS) {
        TTY_LOG("Memory access check error\n");
        return TEE_ERROR_ACCESS_DENIED;
    }

    /* param[1]: OUTPUT */
    if (TEES_IsREESharedMemory (TEE_MEMORY_ACCESS_WRITE, 
        params[1].memref.buffer, params[1].memref.size) != TEE_SUCCESS) {
        TTY_LOG("Memory access check error\n");
        return TEE_ERROR_ACCESS_DENIED;
    }

    sendmsg_len = params[0].memref.size;
    respmsg_len = params[1].memref.size;

    if (sendmsg_len <= 0 || respmsg_len <= 0) {
        TTY_LOG("%s Bad Parameters in invoke command", LOG_TAG);
        return TEE_ERROR_BAD_PARAMETERS;
    }

    if (sendmsg_len > g_msg_len) {
        TTY_LOG("%s: Invalid cmd len %d > max %d ", LOG_TAG, sendmsg_len, g_msg_len);
        return TEE_ERROR_BAD_PARAMETERS;
    }

    TZ_bzero(&g_msg, g_msg_len);
    memcpy(&g_msg, params[0].memref.buffer, sendmsg_len);
    sendmsg = (tciMessage_t *) g_msg;

    respmsg = (tciMessage_t *) params[1].memref.buffer;
    if (respmsg_len > g_msg_len) {
        TTY_LOG("%s: Invalid respmsg len %d vs %d ", LOG_TAG, respmsg_len, g_msg_len);
        return TEE_ERROR_BAD_PARAMETERS;
    }
    TZ_bzero(respmsg, respmsg_len);

    TTY_LOG("%s: Command id - 0x%08X", LOG_TAG, commandID);
    if (!IS_CMD(commandID)) {
        TTY_LOG("%s: Invalid command id, exiting!", LOG_TAG);
        TZ_bzero(&g_msg, g_msg_len);
        return TEE_ERROR_BAD_PARAMETERS;
    }

   if (CMD_SPAY_INIT == commandID) {
        ret = TZ_SPAY_INIT_OK;
        if(!initialized) {
            IS_BUFFER_VALID(sendmsg_len, tz_spay_init_msg_cmd_t, respmsg_len, tz_spay_init_msg_resp_t) {
#ifdef SKIP_COMPILE_ERROR
                respmsg->payload.init.payload.resp.return_code = TZ_SPAY_INIT_OK;
                ret = TZ_SPAY_INIT_OK;
#else
                ret = process_spay_init(&sendmsg->payload.init.payload.cmd, &respmsg->payload.init.payload.resp);
#endif

                respmsg->spayhdr.header.len = sizeof(tz_spay_init_msg_resp_t);
#ifdef ENABLE_TA_ERR_MSG
                if (g_err_recorded) {
                    // we have error code, return it back to NW
                    memcpy(respmsg->payload.init.payload.resp.error_msg.data, g_err_descrp, TA_MAX_ERROR_STR_LEN);
                    respmsg->payload.init.payload.resp.error_msg.len = strlen(g_err_descrp);
                    memset(g_err_descrp, 0, TA_MAX_ERROR_STR_LEN);
                    g_err_recorded = 0;
                }
#endif
            }
        }

        if (ret != TZ_SPAY_INIT_OK) {
            TTY_LOG("%s: trusted boot initialization failed", LOG_TAG);
            initialized = false;
        } else {
            if (!initialized) {
                TTY_LOG("%s: trusted boot initialization completed successfully", LOG_TAG);
            } else {
                TTY_LOG("%s: already initialized", LOG_TAG);
            }
            initialized = true;
/*
#ifdef CNCC_TA
            TTY_LOG("%s: update whitelist for tz_driver");
            update_whitelist_tz_driver();
            TTY_LOG("%s: update whitelist for fchandler");
            update_whitelist_fchandler();
#endif
*/
        }
    } else {
#ifdef SKIP_COMPILE_ERROR
        TTY_LOG("verify_trusted_boot will be skipped. This block should be removed..");
        ret = TZ_SPAY_INIT_OK;
#else
        ret = ((initialized) ? TZ_SPAY_INIT_OK : verify_trusted_boot());
#endif
        if (ret == TZ_SPAY_INIT_OK) {
            if (!initialized) {
                DBG_LOG("%s: trusted boot verification completed successfully", LOG_TAG);
            } else {
                DBG_LOG("%s: trusted boot already verified", LOG_TAG);
            }
#ifndef SPAY_TUI
            if (CMD_SPAY_LOAD_PIN_RANDOM == commandID) {
                IS_BUFFER_VALID(sendmsg_len, tz_spay_load_pin_random_cmd_t, respmsg_len, tz_spay_load_pin_random_resp_t) {
                    memset((void*)respmsg->payload.load_pin_random.payload.resp.error_msg.data, 0, TZ_SPAY_MAX_ERROR_STR_LEN);
                    ret = unwrap_pin_random_so(sendmsg->payload.load_pin_random.payload.cmd.pin_random_so.len, sendmsg->payload.load_pin_random.payload.cmd.pin_random_so.so);
                    DBG_LOG("%s: pin random so len = %d", sendmsg->payload.load_pin_random.payload.cmd.pin_random_so.len, LOG_TAG);
                    respmsg->payload.load_pin_random.payload.resp.return_code = ret;
                    respmsg->spayhdr.header.len = sizeof(tz_spay_load_pin_random_resp_t);
#ifdef ENABLE_TA_ERR_MSG
                    if (g_err_recorded) {
                        // we have error code, return it back to NW
                        memcpy(respmsg->payload.load_pin_random.payload.resp.error_msg.data, g_err_descrp, TA_MAX_ERROR_STR_LEN);
                        respmsg->payload.load_pin_random.payload.resp.error_msg.len = strlen(g_err_descrp);
                        memset(g_err_descrp, 0, TA_MAX_ERROR_STR_LEN);
                        g_err_recorded = 0;
                    }
#endif
                }
            } else if (CMD_SPAY_MOVE_SERVICE_KEY == commandID) {
                IS_BUFFER_VALID(sendmsg_len, tz_spay_msk_msg_cmd_t, respmsg_len, tz_spay_msk_msg_resp_t) {
                    memset((void*)respmsg->payload.msk_cmd.payload.resp.error_msg.data, 0, TZ_SPAY_MAX_SK_LEN);
                    ret = process_spay_msk(&sendmsg->payload.msk_cmd.payload.cmd, &respmsg->payload.msk_cmd.payload.resp);
                    respmsg->payload.msk_cmd.payload.resp.return_code = ret;
                    respmsg->spayhdr.header.len = sizeof(tz_spay_msk_msg_payload_t);
#ifdef ENABLE_TA_ERR_MSG
                    if (g_err_recorded) {
                        // we have error code, return it back to NW
                        memcpy(respmsg->payload.msk_cmd.payload.resp.error_msg.data, g_err_descrp, TA_MAX_ERROR_STR_LEN);
                        respmsg->payload.msk_cmd.payload.resp.error_msg.len = strlen(g_err_descrp);
                        memset(g_err_descrp, 0, TA_MAX_ERROR_STR_LEN);
                        g_err_recorded = 0;
                    }
#endif
                }
            } else {
                /* Process Command. */
                ret = process_cmd(commandID, sendmsg, sendmsg_len, respmsg, respmsg_len);
                if (TEES_SUCCESS == ret && (SPAY_TUI_CMD_SETUP == commandID || SPAY_TUI_CMD_RESUME == commandID || SPAY_TUI_CMD_VERIFY == commandID)) {
                	if(SPAY_TUI_CMD_RESUME == commandID && 1 == sendmsg->payload.cmd.data.resume.update_display_only){
                	    DBG_LOG("%s Resume called: no call touch thread, update only %d", LOG_TAG,
                		                			sendmsg->payload.cmd.data.resume.update_display_only);
                	} else {
                		ret = pthread_create_common(commandID, 0);
                	}
                }
            }
#else // SPAY_TUI
                /* Process Command. */
                ret = process_cmd(commandID, sendmsg, sendmsg_len, respmsg, respmsg_len);
                if (TEES_SUCCESS == ret && (SPAY_TUI_CMD_SETUP == commandID || SPAY_TUI_CMD_RESUME == commandID || SPAY_TUI_CMD_VERIFY == commandID)) {
                    DBG_LOG("%s create thread called: ", LOG_TAG);
                    if(SPAY_TUI_CMD_RESUME == commandID && 1 == sendmsg->payload.cmd.data.resume.update_display_only) {
                        DBG_LOG("%s resume, update only %d", LOG_TAG,sendmsg->payload.cmd.data.resume.update_display_only);
                        ret = pthread_create_common(commandID, sendmsg->payload.cmd.data.resume.update_display_only);
                    } else if(SPAY_TUI_CMD_VERIFY == commandID && 1 == sendmsg->payload.cmd.data.verify.update_display_only) {
                        DBG_LOG("%s verify, update only %d", LOG_TAG,sendmsg->payload.cmd.data.verify.update_display_only);
                        ret = pthread_create_common(commandID, sendmsg->payload.cmd.data.verify.update_display_only);
                    } else {
                        ret = pthread_create_common(commandID, 0);
                    }
                }
#endif
            initialized = true;

        } else {
            TTY_LOG("%s: trusted boot verification failed", LOG_TAG);
            initialized = false;
        }
    }
	
    TTY_LOG("%s: ret = 0x%08X, ret&spay_tui_mask: 0x%08X, ^: 0x%08X", LOG_TAG, ret, ret&SPAY_TUI_MASK, ret^SPAY_TUI_MASK);

    if((ret & SPAY_TUI_MASK) == SPAY_TUI_MASK) {
        ret = ret ^ SPAY_TUI_MASK;
        TTY_LOG("%s: ret = 0x%08X", LOG_TAG, ret);
        // all response has retCode
        respmsg->payload.rsp.data.commonRsp.retCode = ret;
    } else if (ret != TZ_SPAY_INIT_OK) {
        TTY_LOG("%s: Command 0x%08X failed with error code: 0x%08X", LOG_TAG, commandID, ret);
    }
	
   if (ret != TZ_SPAY_INIT_OK) {
	   TTY_LOG("%s: Command 0x%08X failed with error code: 0x%08X", LOG_TAG, commandID, ret);
   }

    respmsg->spayhdr.header.id = RSP_ID(commandID);
    respmsg->spayhdr.header.status = ret;

    TTY_LOG("%s: Trustlet respmsg->spayhdr.header.id = 0x%08X", LOG_TAG, respmsg->spayhdr.header.id);
    TTY_LOG("%s: Trustlet respmsg->spayhdr.header.status = 0x%08X", LOG_TAG, respmsg->spayhdr.header.status);
    TTY_LOG("%s: Returning = 0x%08X", LOG_TAG, ret);

    TZ_bzero(&g_msg, g_msg_len);
    return TEE_SUCCESS;
}
