#include <stdio.h>
#include <stddef.h>
#include <string.h>

#include "tc_api.h"
#include "tc_type.h"
#include "tc_errcode.h"
#include "tc_private/tc_private.h"
#include "tc_private/tc_handle.h"

#define ENGINE_CTX_RELEASE(engine_ctx)                \
engine_ctx->api_ctx->release(engine_ctx->api_ctx);  \
engine_ctx->release();                              \

TC_RC TC_Start (
    const uint8_t           *device,
    TC_HANDLE               *handle)
{
    TC_RC   rc = TC_SUCCESS;

    TC_HANDLE_CTX*  tc_handle_ctx = malloc(sizeof(TC_HANDLE_CTX));
    *handle = (TC_HANDLE)tc_handle_ctx;
    tc_handle_ctx->handle.tc_object = malloc(sizeof(TC_OBJECT));

    if (device) {
        int count  = sizeof(engine_ctx_table) / sizeof(ENGINE_CTX_TABLE);
        ENGINE_CTX_TABLE* p = NULL;
        for (int i = 0; i < count; i++) {
            p = &engine_ctx_table[i];
            if ((strstr(device, p->engine_name)) || (strstr(device, "tabrmd"))){
                tc_handle_ctx->engine = p->engine_ctx;
                break;
            } 
        }
    }

    if (NULL == tc_handle_ctx->engine
        || NULL == tc_handle_ctx->engine->ops 
        || NULL == tc_handle_ctx->engine->ops->start) {
        return TC_ERR_NULL;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_START);
    if (rc != TC_SUCCESS)
        goto start_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                2,
                                handle,
                                device);
    if (rc != TC_SUCCESS)
        goto start_end;

    rc = tc_handle_ctx->engine->ops->start(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto start_end;

start_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_End (
    TC_HANDLE               *handle)
{
    TC_RC   rc = TC_SUCCESS;

    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(*handle);

    if (tc_handle_ctx == NULL) {
        return TC_ERR_NULL;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_END);
    if (rc != TC_SUCCESS)
        goto end_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                1,
                                handle);
    if (rc != TC_SUCCESS)
        goto end_end;

    rc = tc_handle_ctx->engine->ops->end(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS) {
        goto end_end;
    }


end_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    free(tc_handle_ctx);
    return rc;
}

TC_RC TC_CreatePrimary (
    TC_HANDLE              handle,
    const TC_ALG           alg_hash,
    const uint32_t         hierarchy,
    const TC_BUFFER       *hierarchy_auth_msg,
    const TC_ALG           alg_primary,
    const TC_BUFFER       *primary_auth_msg,
    uint32_t              *primary_index)
{
    TC_RC   rc = TC_SUCCESS;

    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx) {
        rc = TC_ERR_NULL;
        goto primary_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_CREATEPRIMARY);
    if (rc != TC_SUCCESS)
        goto primary_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                7,
                                handle,
                                alg_hash,
                                hierarchy,
                                hierarchy_auth_msg,
                                alg_primary,
                                primary_auth_msg,
                                primary_index);
    if (rc != TC_SUCCESS)
        goto primary_end;

    rc = tc_handle_ctx->engine->ops->createprimary(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS) {
        goto primary_end;
    }

    *primary_index = tc_handle_ctx->handle.tc_object->count -1;
  
primary_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_Create (
    TC_HANDLE              handle,
    const TC_ALG           alg_hash,
    const uint32_t         primary_index,
    const TC_BUFFER       *primary_auth_msg,
    const TC_ALG           alg_key,
    const TC_BUFFER       *key_auth_msg,
    uint32_t              *key_index)
{
    TC_RC   rc = TC_SUCCESS;
    
    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx) {
        rc = TC_ERR_NULL;
        goto create_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_CREATE);
    if (rc != TC_SUCCESS)
        goto create_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                7,
                                handle,
                                alg_hash,
                                primary_index,
                                primary_auth_msg,
                                alg_key,
                                key_auth_msg,
                                key_index);
    if (rc != TC_SUCCESS)
        goto create_end;

    rc = tc_handle_ctx->engine->ops->create(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto create_end;

    *key_index = tc_handle_ctx->handle.tc_object->count -1;
create_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_Load (
    TC_HANDLE              handle,
    const uint32_t         key_index,
    const TC_BUFFER       *key_auth_msg)
{
    TC_RC   rc = TC_SUCCESS;
    
    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx) {
        rc = TC_ERR_NULL;
        goto load_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_LOAD);
    if (rc != TC_SUCCESS)
        goto load_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                3,
                                handle,
                                key_index,
                                key_auth_msg);
    if (rc != TC_SUCCESS)
        goto load_end;

    rc = tc_handle_ctx->engine->ops->load(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto load_end;
load_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_EvictControl (
    TC_HANDLE              handle,
    const bool             enable,
    const uint32_t         persist_index,
    const uint32_t         key_index,
    const uint32_t         hierarchy,
    const TC_BUFFER       *hierarchy_auth_msg)
{
    TC_RC   rc = TC_SUCCESS;
    
    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx) {
        rc = TC_ERR_NULL;
        goto evict_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_EVICTCONTROL);
    if (rc != TC_SUCCESS)
        goto evict_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                6,
                                handle,
                                enable,
                                persist_index,
                                key_index,
                                hierarchy,
                                hierarchy_auth_msg);
    if (rc != TC_SUCCESS)
        goto evict_end;

    rc = tc_handle_ctx->engine->ops->evictcontrol(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto evict_end;

evict_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_Encrypt (
    TC_HANDLE              handle,
    const uint32_t         key_index,
    const TC_BUFFER       *key_auth_msg,
    const TC_ALG           alEncrypt,
    const TC_BUFFER       *plain_text,
    TC_BUFFER             *ciphter_text)
{
    TC_RC   rc = TC_SUCCESS;
    
    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx->handle.tc_context) {
        rc = TC_ERR_NULL;
        goto encrypt_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_ENCRYPT);
    if (rc != TC_SUCCESS)
        goto encrypt_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                6,
                                handle,
                                key_index,
                                key_auth_msg,
                                alEncrypt,
                                plain_text,
                                ciphter_text);
    if (rc != TC_SUCCESS)
        goto encrypt_end;

    rc = tc_handle_ctx->engine->ops->encrypt(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto encrypt_end;

encrypt_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_Decrypt (
    TC_HANDLE              handle,
    const uint32_t         key_index,
    const TC_BUFFER       *key_auth_msg,
    const TC_ALG           alDecrypt,
    const TC_BUFFER       *ciphter_text,
    TC_BUFFER             *plain_text)
{
    TC_RC   rc = TC_SUCCESS;
    
    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx) {
        rc = TC_ERR_NULL;
        goto decrypt_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_DECRYPT);
    if (rc != TC_SUCCESS)
        goto decrypt_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                6,
                                handle,
                                key_index,
                                key_auth_msg,
                                alDecrypt,
                                ciphter_text,
                                plain_text);
    if (rc != TC_SUCCESS)
        goto decrypt_end;

    rc = tc_handle_ctx->engine->ops->decrypt(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto decrypt_end;

decrypt_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_Hash (
    TC_HANDLE              handle,
    const TC_ALG           alg_hash,
    const TC_BUFFER       *plain_text,
    TC_BUFFER             *digest_msg)
{
    TC_RC   rc = TC_SUCCESS;
    
    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx) {
        rc = TC_ERR_NULL;
        goto hash_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_HASH);
    if (rc != TC_SUCCESS)
        goto hash_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                4,
                                handle,
                                alg_hash,
                                plain_text,
                                digest_msg);
    if (rc != TC_SUCCESS)
        goto hash_end;

    rc = tc_handle_ctx->engine->ops->hash(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto hash_end;

hash_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_Sign (
    TC_HANDLE              handle,
    const uint32_t         key_index,
    const TC_BUFFER       *key_auth_msg,
    const TC_ALG           alg_sign,
    const TC_ALG           alg_hash,
    const TC_BUFFER       *plain_text,
    TC_BUFFER             *sign_text)
{
    TC_RC   rc = TC_SUCCESS;
    
    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx) {
        rc = TC_ERR_NULL;
        goto sign_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_SIGN);
    if (rc != TC_SUCCESS)
        goto sign_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                7,
                                handle,
                                key_index,
                                key_auth_msg,
                                alg_sign,
                                alg_hash,
                                plain_text,
                                sign_text);
    if (rc != TC_SUCCESS)
        goto sign_end;

    rc = tc_handle_ctx->engine->ops->sign(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto sign_end;

sign_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_VerifySignature (
    TC_HANDLE              handle,
    const uint32_t         key_index,
    const TC_BUFFER       *key_auth_msg,
    const TC_ALG           alg_sign,
    const TC_ALG           alg_hash,
    const TC_BUFFER       *plain_text,
    const TC_BUFFER       *sign_text,
    unsigned int          *verify)
{
    TC_RC   rc = TC_SUCCESS;
    
    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx) {
        rc = TC_ERR_NULL;
        goto verifysignature_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_VERIFYSIGNATURE);
    if (rc != TC_SUCCESS)
        goto verifysignature_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                8,
                                handle,
                                key_index,
                                key_auth_msg,
                                alg_sign,
                                alg_hash,
                                plain_text,
                                sign_text,
                                verify);
    if (rc != TC_SUCCESS)
        goto verifysignature_end;

    rc = tc_handle_ctx->engine->ops->verifysignature(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto verifysignature_end;

verifysignature_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_NVDefine (
    TC_HANDLE              handle,
    const uint32_t         nv_index,
    const uint32_t         hierarchy,
    const uint32_t         ulSize,
    const TC_BUFFER       *hierarchy_auth_msg)
{
    TC_RC   rc = TC_SUCCESS;
    
    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx) {
        rc = TC_ERR_NULL;
        goto nvdefine_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_NVDEFINE);
    if (rc != TC_SUCCESS)
        goto nvdefine_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                5,
                                handle,
                                nv_index,
                                hierarchy,
                                ulSize,
                                hierarchy_auth_msg);
    if (rc != TC_SUCCESS)
        goto nvdefine_end;

    rc = tc_handle_ctx->engine->ops->nvdefine(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto nvdefine_end;

nvdefine_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_NVRelease (
    TC_HANDLE              handle,
    const uint32_t         nv_index,
    const uint32_t         hierarchy,
    const TC_BUFFER       *hierarchy_auth_msg)
{
    TC_RC   rc = TC_SUCCESS;
    
    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx) {
        rc = TC_ERR_NULL;
        goto nvrelease_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_NVRELEASE);
    if (rc != TC_SUCCESS)
        goto nvrelease_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                4,
                                handle,
                                nv_index,
                                hierarchy,
                                hierarchy_auth_msg);
    if (rc != TC_SUCCESS)
        goto nvrelease_end;

    rc = tc_handle_ctx->engine->ops->nvrelease(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto nvrelease_end;

nvrelease_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_NVRead (
    TC_HANDLE              handle,
    const uint32_t         nv_index,
    const uint32_t         hierarchy,
    const TC_BUFFER       *hierarchy_auth_msg,
    const uint32_t         offset_read,
    const uint32_t         size_read,
    TC_BUFFER             *nv_data_read)
{
    TC_RC   rc = TC_SUCCESS;
    
    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx) {
        rc = TC_ERR_NULL;
        goto nvread_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_NVREAD);
    if (rc != TC_SUCCESS)
        goto nvread_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                7,
                                handle,
                                nv_index,
                                hierarchy,
                                hierarchy_auth_msg,
                                offset_read,
                                size_read,
                                nv_data_read);
    if (rc != TC_SUCCESS)
        goto nvread_end;

    rc = tc_handle_ctx->engine->ops->nvread(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto nvread_end;

nvread_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}

TC_RC TC_NVWrite (
    TC_HANDLE              handle,
    const uint32_t         nv_index,
    const uint32_t         hierarchy,
    const TC_BUFFER       *hierarchy_auth_msg,
    const uint32_t         offset_written,
    const TC_BUFFER       *nv_data_written)
{
    TC_RC   rc = TC_SUCCESS;
    
    TC_HANDLE_CTX* tc_handle_ctx = (TC_HANDLE_CTX*)(handle);

    if (NULL == tc_handle_ctx) {
        rc = TC_ERR_NULL;
        goto nvwrite_end;
    }

    rc = tc_handle_ctx->engine->alloc_api_ctx(API_NVWRITE);
    if (rc != TC_SUCCESS)
        goto nvwrite_end;

    rc = tc_handle_ctx->engine->api_ctx->init(
                                tc_handle_ctx->engine->api_ctx,
                                7,
                                handle,
                                nv_index,
                                hierarchy,
                                hierarchy_auth_msg,
                                offset_written,
                                nv_data_written);
    if (rc != TC_SUCCESS)
        goto nvwrite_end;

    rc = tc_handle_ctx->engine->ops->nvwrite(
                                tc_handle_ctx->engine->api_ctx);
    if (rc != TC_SUCCESS)
        goto nvwrite_end;

nvwrite_end:
    ENGINE_CTX_RELEASE(tc_handle_ctx->engine);
    return rc;
}