#include "ifaa_ta_vendor.h"
#include "ifaa_ta_vendor.h"
#include "ifaa_ta_common.h"
#include "ifaa_tee_common.h"

#include "tzWrappers/TzwMemory.h"
#include "tzWrappers/TzwString.h"
#include "tzWrappers/TzwStorage.h"
#include "tzWrappers/TzwCommon.h"

#include "ifaa_fingerprint_id_table.h"
#include "ifaa_km.h"
#include "ifaa_config.h"

#define SKPM_KEY_HANDLE_LENGTH    10
const char *skpm_key_handle_sfs = "skpmhandle";

#define CMD_PAIR(id) \
    {id, #id}

static ifaa_ta_cmd_pair_t IFAA_ta_cmd_pairs[] = {
    CMD_PAIR(IFAA_TA_CMD_GET_DEVICE_ID),
    CMD_PAIR(IFAA_TA_CMD_REGISTER),
    CMD_PAIR(IFAA_TA_CMD_AUTHENTICATE),
    CMD_PAIR(IFAA_TA_CMD_DEREGISTER),
    CMD_PAIR(IFAA_TA_CMD_QUERY_STATUS),
    CMD_PAIR(IFAA_TA_CMD_GEN_ASYMMETRIC_KEY),
    CMD_PAIR(IFAA_TA_CMD_GET_PROTOCOL_VERSION),

#ifndef IFBIO_RELEASE
    CMD_PAIR(IFAA_TA_CMD_GET_CERT_ALG_ENCODE),
    CMD_PAIR(IFAA_TA_CMD_READ_FILE),
    CMD_PAIR(IFAA_TA_CMD_WRITE_FILE),
    CMD_PAIR(IFAA_TA_CMD_DELETE_FILE),

    CMD_PAIR(IFAA_INTERNAL_GET_LAST_IDENTIFIED_RESULT),
    CMD_PAIR(IFAA_INTERNAL_GET_AUTHENTICATOR_VERSION),
    CMD_PAIR(IFAA_INTERNAL_GET_ID_LIST),
    CMD_PAIR(IFAA_INTERNAL_BIO_ID_COMPARE),

    CMD_PAIR(IFAA_INTERNAL_SHA256),
    CMD_PAIR(IFAA_INTERNAL_SIGN),
    CMD_PAIR(IFAA_INTERNAL_VERIFY),
    CMD_PAIR(IFAA_INTERNAL_KEY_GENERATE),
    CMD_PAIR(IFAA_INTERNAL_HMAC_SHA1),
    CMD_PAIR(IFAA_INTERNAL_AUTHENTICATOR_SIGN),
    CMD_PAIR(IFAA_INTERNAL_AUTHENTICATOR_VERIFY),

    CMD_PAIR(IFAA_TA_CMD_DELETE_SKPM_INJECT_STATE),
    CMD_PAIR(IFAA_TA_CMD_CLEAR_SFS_FILE_LIST),
#endif
    CMD_PAIR(IFAA_TA_CMD_GET_SEC_DEVICE_ID),

    CMD_PAIR(IFAA_TA_CMD_GET_SKPM_INJECT_STATE),
    CMD_PAIR(IFAA_TA_CMD_PROVISIONING_KEY_HANDLE),
    CMD_PAIR(IFAA_TA_CMD_GET_ID_LIST),
    CMD_PAIR(IFAA_TA_CMD_SET_ID_LIST),
};

const char *IFAA_getCmdString(ifaa_ta_cmd cmd){
    uint8_t len = sizeof(IFAA_ta_cmd_pairs)/sizeof(ifaa_ta_cmd_pair_t);
    const char *cmdString = NULL;
    for( uint8_t i = 0; i < len; i++){
        if(cmd == IFAA_ta_cmd_pairs[i].cmd_id){
            cmdString = IFAA_ta_cmd_pairs[i].cmd_string;
            break;
        }
    }

    return cmdString;
}

MACRO_SAMSUNG_IMPL_MARKER
IFAA_Result IFAA_GetFpEnrolledIdList(uint8_t* buf_id_list, uint32_t* id_list_len)
{
    LOG_PERF_BEGIN
    if (buf_id_list == NULL || id_list_len == NULL) {
		LOG_E("get enrolled id list: bad params!");
		return IFAA_ERR_BAD_PARAM;
    }
    if(TEE_GetFpList(buf_id_list, id_list_len) == TEE_SUCCESS) {
		LOG_D("get fp list len = %d", *id_list_len);
#ifdef __DEV_DEBUG__
		logByteArrayHex(buf_id_list, *id_list_len, "enrolled bio id list");
#endif
		return IFAA_ERR_SUCCESS;
    } else {
		return IFAA_ERR_GET_FILE_LIST_FAILED;
    }
    LOG_PERF_END
}

IFAABoolean IFAA_CheckRoot(){
    LOG_E("not implemented yet!");
    return IFAA_NO;
}

MACRO_SAMSUNG_IMPL_MARKER
IFAA_Result IFAA_AuthenticatorSignDigest(const uint8_t *digest, uint32_t digest_len,
                                         uint8_t *signature, uint32_t *sig_len) {
    LOG_FUNC_BEGIN

    IFAA_Result ret = IFAA_ERR_SUCCESS;
    if (!digest || !signature || digest_len <= 0) {
        LOG_E("IFAA_AuthenticatorSignDigest,Invalid parameters");
        return IFAA_ERR_BAD_PARAM;
    }

    LOG_D("digest_len before sign:%d", *sig_len);

    ifaa_km_result_t result = ifaa_km_sign(digest, digest_len, signature, sig_len);
    if (result != IFAA_KM_SUCCESS) {
        LOG_E("IFAA_AuthenticatorSignDigest, Ifaa km sign failed : 0x%08x", result);
        return IFAA_ERR_SKPM_SIGN_FAILED;
    }
    LOG_D("digest_len after sign:%d", *sig_len);
    LOG_D("signature success");

    LOG_FUNC_END

    return ret;
}

IFAA_Result ifaa_tz_get_skpm_inject_state() {
    LOG_FUNC_BEGIN

    uint8_t buff[SKPM_KEY_HANDLE_LENGTH];
    uint32_t buff_len = SKPM_KEY_HANDLE_LENGTH;

    IFAA_Result ret = IFAA_ReadFile(skpm_key_handle_sfs, SKPM_KEY_HANDLE_LENGTH, buff, &buff_len);
    if (ret != IFAA_ERR_SUCCESS) {
        LOG_E("Check skpm inject state failed, error code : %x", ret);
    }

    LOG_FUNC_END

    return ret;
}

IFAA_Result ifaa_tz_delete_skpm_inject_state() {

    IFAA_Result ret = IFAA_DeleteFile(skpm_key_handle_sfs, SKPM_KEY_HANDLE_LENGTH);
    if (ret != IFAA_ERR_SUCCESS) {
        LOG_E("Remove skpm inject state failed, error code : %x", ret);
    }
    return ret;
}

IFAA_Result ifaa_tz_skpm_provision_operation(uint32_t skpm_key_handle_length, uint8_t *skpm_key_handle) {
    //provision skpm key handle
    uint32_t skpm_key_handle_real_length = skpm_key_handle_length - sizeof(uint32_t);
    LOG_D("skpm_key_handle_real_length:%d", skpm_key_handle_real_length);
    uint8_t *real_skpm_key_handle = skpm_key_handle + sizeof(uint32_t);

#if defined(TZ_MODEL_BLOWFISH)
    TEE_UUID uuid = TA_IFBIO_Teegris_UUID;
    char uuidStr[UUID_STRING_LEN]= {0,};
    uuid_unparse(&uuid, uuidStr);
    LOG_D("get current TA uuid: %s", uuidStr);
#elif defined(TZ_MODEL_Kinibi)
    LOG_D("TA UUID: ffffffffd00000000000000000000091");
#elif defined(TZ_MODEL_QCOM)
    LOG_D("get current TA Name: ifbio");
#endif
    ifaa_km_result_t result = ifaa_km_provision_key(real_skpm_key_handle,
                                                    skpm_key_handle_real_length);
    if (result != IFAA_KM_SUCCESS) {
        LOG_E("provision SKPM key(ifaa_km_provision_key) failed : 0x%08x", result);
        return IFAA_ERR_PROVISION_FAILED;
    } else {
        LOG_D("Provision SKPM key handle success");
    }
    //To save the skpm key handle data,skpm_key_handle is  length(4byte)+real data.
    LOG_D("Write skpm handle to storage");

    IFAA_Result ret = IFAA_WriteFile(skpm_key_handle_sfs, SKPM_KEY_HANDLE_LENGTH,
                                     (const uint8_t *)skpm_key_handle_sfs,
                                     SKPM_KEY_HANDLE_LENGTH);
    if (ret != IFAA_ERR_SUCCESS) {
        LOG_E("ifaa_tz_skpm_provision_operation, Save skpm blob data failed, error code : 0x%08x", ret);
        return ret;
    }

    return ret;
}

#define MAX_PATH_LEN 256

MACRO_SAMSUNG_IMPL_MARKER
IFAA_Result IFAA_WriteFile(const char *path, uint32_t path_len, const uint8_t *data, uint32_t len) {

    LOG_FUNC_BEGIN

    CHECK_BOOL_RET_VAL(path && path_len > 0 && data && len > 0, IFAA_ERR_BAD_PARAM);

    char local_path[MAX_PATH_LEN] =  {0,};
    tzwMemMove(local_path, (char *)path, path_len);
    local_path[path_len] = 0;

#ifndef IFBIO_RELEASE
    LOG_D("write filename: %s(%d), len of data: %d", local_path, path_len, len);
#endif

    TEE_Result ret = IFAA_ERR_SUCCESS;
    TEE_Result status = TEE_SUCCESS;
    TzwSfsObject_t handle = TEE_HANDLE_NULL;
    do{
        status = tzwOpenSfsObject((void*)path, path_len,
                                    TEE_DATA_FLAG_ACCESS_WRITE | TEE_DATA_FLAG_ACCESS_WRITE_META, &handle);
        LOG_D("tzwOpenSfsObject, status: %x", status);
        if (TEE_SUCCESS == status) {
            LOG_D("Object existed. Deleting.");
            tzwCloseAndDeleteSfsObject(handle);
            handle = TEE_HANDLE_NULL;
            LOG_D("Object '%s' successfully deleted!", local_path);
        }

        status = tzwCreateSfsObject((void*)path, path_len,
                                    TEE_DATA_FLAG_ACCESS_WRITE | TEE_DATA_FLAG_ACCESS_WRITE_META, &handle);
        CHECK_TEE_STATUS_SUCCESS_BREAK("tzwCreateSfsObject");

#ifdef __DEV_DEBUG__
        logByteArrayHex(data, len, "write data into storage");
#endif

        status = tzwWriteToSfsObject(handle, (void*)data, len);
        CHECK_TEE_STATUS_SUCCESS_BREAK("tzwWriteToSfsObject");

        if (TEE_SUCCESS == ret) {
            ret = IFAA_ERR_SUCCESS;
        }
    }while(0);

    tzwCloseSfsObject(handle);

    if (TEE_SUCCESS != status) {
        LOG_E("%s error no: 0x%08x", __func__, status);
        ret = status;;
    }

    LOG_FUNC_END

    return ret;
}

MACRO_SAMSUNG_IMPL_MARKER
IFAA_Result IFAA_ReadFile(const char *path, uint32_t path_len, uint8_t *buff, uint32_t *len) {

    LOG_FUNC_BEGIN

#ifndef IFBIO_RELEASE
    logByteArrayHex((uint8_t*)path, path_len, "path name for read");
    char local_path[MAX_PATH_LEN] =  {0,};
    tzwMemMove(local_path, (char *)path, path_len);
    local_path[path_len] = 0;
    LOG_D("read filename: %s(%d), read len: %d", local_path, path_len, *len);
#endif
    CHECK_BOOL_RET_VAL(path && path_len > 0
                       && buff && *len > 0, IFAA_ERR_BAD_PARAM);
    LOG_I_AM_HERE
    IFAA_Result ret = IFAA_ERR_SUCCESS;
    TEE_Result status = TEE_SUCCESS;
    TzwSfsObject_t handle = TEE_HANDLE_NULL;
    do{
        LOG_I_AM_HERE
        status = tzwOpenSfsObject((void*)path, path_len,
                                TEE_DATA_FLAG_ACCESS_READ, &handle);
        CHECK_TEE_STATUS_SUCCESS_BREAK("tzwOpenSfsObject");

        tzwReadFromSfsObject(handle, buff, *len, len);
        CHECK_TEE_STATUS_SUCCESS_BREAK("tzwReadFromSfsObject");

#ifdef __DEV_DEBUG__
        logByteArrayHex(buff, *len, "read data from storage");
#endif

    }while(0);

    tzwCloseSfsObject(handle);
    if (TEE_SUCCESS != status) {
        LOG_E("%s error no: 0x%08x", __func__, status);
        if(status == TEE_ERROR_ITEM_NOT_FOUND){
            ret = IFAA_ERR_NO_FILE;
        }
    }

    LOG_FUNC_END

    return ret;
}

MACRO_SAMSUNG_IMPL_MARKER
IFAA_Result IFAA_DeleteFile(const char *path, uint32_t path_len)
{

    CHECK_BOOL_RET_VAL(path && path_len > 0, IFAA_ERR_BAD_PARAM);

#ifndef IFBIO_RELEASE
    char local_path[MAX_PATH_LEN] =  {0,};
    tzwMemMove(local_path, (char *)path, path_len);
    local_path[path_len] = 0;
    LOG_D("delete_file: %s", local_path);
#endif
    TEE_Result status = TEE_SUCCESS;
    TzwSfsObject_t handle = TEE_HANDLE_NULL;
    do {
        status = tzwOpenSfsObject((void*)path, path_len,
                                TEE_DATA_FLAG_ACCESS_WRITE | TEE_DATA_FLAG_ACCESS_WRITE_META, &handle);
        if ((TEE_SUCCESS != status) && (TEE_ERROR_ITEM_NOT_FOUND != status)) {
            LOG_E("tzwOpenSfsObject error: %08x", status);
            break;
        }

        tzwCloseAndDeleteSfsObject(handle);
    }while(0);

    if (TEE_SUCCESS != status) {
        return IFAA_ERR_ERASE;
    }

    return IFAA_ERR_SUCCESS;
}

MACRO_SAMSUNG_IMPL_MARKER
IFAA_Result IFAA_ClearFiles()
{
    LOG_E("not implemented yet");

    return TEE_ERROR_NOT_IMPLEMENTED;
}
