/*
 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All rights reserved.
 *
 */
#include <tees_extension.h>

#include "TzwMacros.h"
#include "TzwMemory.h"
#include "TzwString.h"
#include "ifaa_ta_common.h"
#include "ifaa_ta_vendor.h"
#include "ifaa_wsbuff.h"
#include "ifaa_config.h"
#include "ta_banner_print.h"

#include <inttypes.h>

#include <sys/resource.h>
// Few defines below describes TA prorperties.
#define TA_PROP_UUID                   TA_IFBIO_Teegris_UUID
#define TA_PROP_SINGLE_INSTANCE        FALSE
#define TA_PROP_MULTISESSION           FALSE
#define TA_PROP_INSTANCE_KEEPALIVE     FALSE
#define TA_PROP_DATASIZE               RLIM_INFINITY
#define TA_PROP_STACKSIZE              0x8000
#define TA_PROP_THREAD_COUNT           1
#define TA_PROP_NUMINSTANCES           0
#define TA_PROP_INITIAL_PRIORITY       RLIM_DEF_PRIORITY
#define TA_PROP_MAX_PRIORITY           RLIM_MAX_PRIORITY
#define TA_PROP_GROUP_ID               "samsung_ta"
#define TA_PROP_VERSION                "slsi"
#define TA_PROP_DESCRIPTION            "IFAA"
#define TA_PROP_DBG_DLM_DATA_AVAILABLE TA_DBG_DLM_BLOCKED
#define TA_PROP_DBG_PMR_DATA_AVAILABLE TA_DBG_PMR_BLOCKED

#include <ta_property.h>

uint64_t perf_time_begin;
uint64_t perf_time_end;

#ifdef __DEV_DEBUG__
static uint64_t start_time_of_entry_point = 0;
static uint64_t end_time_of_entry_point = 0;

static uint64_t start_time_of_session_entry = 0;
static uint64_t end_time_of_session_entry = 0;
#endif /* __DEV_DEBUG__ */

/**
 *    The function is the service constructor, which the system calls when it creates a new instance of the service.
 **/
TEE_Result TA_EXPORT TA_CreateEntryPoint(void) {
    LOG_I("VERSION: %s", taAppVersion);

#ifdef __DEV_DEBUG__
    start_time_of_entry_point = systemTime();
#endif /* __DEV_DEBUG__ */
    return TEE_SUCCESS;
}

/**
 *    The function is the service destructor, which the system calls when the instance is being destroyed.
 **/
void TA_EXPORT TA_DestroyEntryPoint(void) {
    LOG_I("TA is unloaded");

#ifdef __DEV_DEBUG__
    end_time_of_entry_point = systemTime();
    LOG_DEV("elapsed time(entry point): %"PRIu64"ms", end_time_of_entry_point - start_time_of_entry_point);
#endif /* __DEV_DEBUG__ */
}

/**
 *    The system calls this function when a new client connects to the service instance.
 **/
TEE_Result TA_EXPORT TA_OpenSessionEntryPoint(uint32_t nParamTypes, IN OUT TEE_Param pParams[4], OUT void** ppSessionContext) {
    S_VAR_NOT_USED(pParams);
    LOG_D("enter func: %s", __func__);

#ifdef __DEV_DEBUG__
    start_time_of_session_entry = systemTime();
#endif /* __DEV_DEBUG__ */

    if (!ppSessionContext) {
        return TEE_ERROR_GENERIC;
    }

    if (TEE_PARAM_TYPE_GET(nParamTypes, 0) != TEE_PARAM_TYPE_VALUE_INPUT) {
        LOG_E("Bad parameter at index 0: expected value input");
        return TEE_ERROR_BAD_PARAMETERS;
    }

	print_banner(TA_PROP_DESCRIPTION, TA_PROP_VERSION);

	IFAA_TaAddEntry(IFAA_BIO_FINGERPRINT, IFAA_ENTRY_LAST_IDENTIFIED_RESULT_GETTER, IFAA_GetFpLastIdentifiedResult);
	IFAA_TaAddEntry(IFAA_BIO_FINGERPRINT, IFAA_ENTRY_AUTHENTICATOR_VERSION_GETTER, IFAA_GetFpAuthenticatorVersion);
	IFAA_TaAddEntry(IFAA_BIO_FINGERPRINT, IFAA_ENTRY_ID_LIST_GETTER, IFAA_GetFpEnrolledIdList);
	IFAA_TaAddEntry(IFAA_BIO_FINGERPRINT, IFAA_ENTRY_EQUATOR, IFAA_FpIdCompare);
	IFAA_TaAddEntry(IFAA_BIO_FINGERPRINT, IFAA_ENTRY_IS_ROOT, IFAA_CheckRoot);

    return TEE_SUCCESS;
}

/**
 *  The system calls this function to indicate that a session is being closed.
 **/
void TA_EXPORT TA_CloseSessionEntryPoint(IN OUT void* pSessionContext) {
    LOG_D("enter func: %s", __func__);
    if (pSessionContext) {
        tzwFree(pSessionContext);
        pSessionContext = NULL;
    }

#ifdef __DEV_DEBUG__
    end_time_of_session_entry = systemTime();
    LOG_DEV("elapsed time(session entry): %"PRIu64"ms", end_time_of_session_entry- start_time_of_session_entry);
#endif /* __DEV_DEBUG__ */
}

/**
 *  This function calls when the client invokes a command within the given session.
 **/
TEE_Result TA_EXPORT TA_InvokeCommandEntryPoint(void* sessionContext, uint32_t commandID, uint32_t paramTypes,
        TEE_Param params[4]) {
    S_VAR_NOT_USED(sessionContext);
    S_VAR_NOT_USED(commandID);

    LOG_I_PERF_BEGIN

    LOG_D("enter func: %s, paramTypes: 0x%08x", __func__, paramTypes);

    if(paramTypes != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
                                    TEE_PARAM_TYPE_MEMREF_OUTPUT,
                                    TEE_PARAM_TYPE_NONE,
                                    TEE_PARAM_TYPE_NONE)){
        LOG_E("%s: Bad parameters", __func__);
        return TEE_ERROR_BAD_PARAMETERS;
    }

	/* param[0]: INPUT ONLY*/
	if (TEES_IsREESharedMemory(TEE_MEMORY_ACCESS_READ,
		params[0].memref.buffer, params[0].memref.size) != TEE_SUCCESS) {
		LOG_E("Memory access check error\n");
		return TEE_ERROR_ACCESS_DENIED;
	}

	/* param[1]: OUTPUT ONLY*/
	if (TEES_IsREESharedMemory(TEE_MEMORY_ACCESS_WRITE,
		params[1].memref.buffer, params[1].memref.size) != TEE_SUCCESS) {
		LOG_E("Memory access check error\n");
		return TEE_ERROR_ACCESS_DENIED;
	}

    if(sizeof(cmd_req_t) < (uint32_t)params[0].memref.size
            && sizeof(cmd_rsp_t) == (uint32_t)params[1].memref.size){
        LOG_E("%s:OOM, Too many bytes from NWD or Shared Memory Size does not match", __func__);
        return TEE_ERROR_OUT_OF_MEMORY;
    }

    uint32_t total_log_size = 0;
    cmd_req_t request = {0,};
    cmd_rsp_p response = (cmd_rsp_p)params[1].memref.buffer;

    memset(&request, 0, sizeof(cmd_req_t));
    memset(response, 0, sizeof(cmd_rsp_t));

    uint8_t *req = (uint8_t*)&request;
    uint8_t *rsp = (uint8_t*)response;

    uint32_t ret_size = (IFBIO_OUTPUT_LEN + 8);
    uint32_t orig_size = (IFBIO_OUTPUT_LEN + 8);
    uint32_t req_size = (uint32_t)params[0].memref.size;

    LOG_D("cmd_req_t: %08x", sizeof(cmd_req_t));
    LOG_D("input: %08x", (uint32_t)params[0].memref.size);

    tzwMemMove(req, (uint8_t*)params[0].memref.buffer, (uint32_t)params[0].memref.size);

    IFAA_Result status = IFAA_TaInvokeCmd(req, req_size, rsp, &ret_size);

    LOG_I_PERF_END

    if (status != IFAA_ERR_SUCCESS) {
        LOG_I("Error occured during IFAA_TaInvokeCmd");
    }

    if(ret_size != orig_size) {
        LOG_DEV("**response size has been changed**");
    }

#ifdef __DEV_DEBUG__
    /*logByteArrayHex(rsp, ret_size, "response bytes from TA");*/
#endif

#if defined(TRANSMIT_LOG_TO_CA)
#ifdef TA_RELEASE
    if (IFAA_ERR_SUCCESS != status)
#endif /*TA_RELEASE*/
    do{
        int len = 0;
        qsc_send_cmd_rsp_t *logOut = (qsc_send_cmd_rsp_t*)(rsp);
        char *ptr = (char*)logOut->log_buf;
        logOut->log_len= 0;
        total_log_size = 0;
        while(!isStackLogEmpty()) {
            char *str = popStackLog(&len);
            total_log_size += (len + 4);
            if(LOGGER_BUF_LEN < total_log_size ){
                /*LOG_Raw("missing log:%s", str);*/
                tzwFree(str);
                continue;
            }

            *((uint32_t*)ptr) = len;
            ptr += 4;

            strncpy(ptr, str, len);
            ptr += len;
            logOut->log_len += (len + 4);
            tzwFree(str);
        }
    }while(0);
#ifdef __DEV_DEBUG__
    LOG_Raw("len of transmit Ta log: %u", response->log_len);
    LOG_Raw("len of all Ta log: %d", total_log_size);
#endif /* __DEV_DEBUG__ */
#endif /*TRANSMIT_LOG_TO_CA*/

    return TEE_SUCCESS;
}
