#include <stdio.h>
#include <string.h>

#include <tee_internal_api.h>

#include "VaultKeeperAPI.h"
#include "vk_constants.h"
#include "vk_data_struct.h"
#include "vk_utils.h"
#include "vk_log.h"

#define CMD_PROTOCOL CMD_PROTOCOL_TA

#define VK_SUCCESS           0
#define VK_ERR_OPEN_FAIL    -1

extern char TZ_APP_NAME[];
static char* client_id = NULL;

static TEE_TASessionHandle vk_session;

static uint32_t param_type = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT,
                                             TEE_PARAM_TYPE_MEMREF_OUTPUT,
                                             TEE_PARAM_TYPE_NONE,
                                             TEE_PARAM_TYPE_NONE);

TEE_UUID vk_uuid = {0,0,0,{0, 0, 0x56, 0x4c, 0x54, 0x4b, 0x50, 0x52}};

static int generate_cmd_no()
{
	// To observe between TAs, Generate command number. (Range:1~9999);
	// coverd milliseconds
	TEE_Time t;
	TEE_GetSystemTime(&t);

	return t.millis % 9999 + 1;
}

static TEE_Result open_vk_service()
{
	return TEE_OpenTASession(&vk_uuid, 1000000, 0, NULL, &vk_session, NULL);
}

static void release_vk_service()
{
	TEE_CloseTASession(vk_session);
}

int vk_read_sbox(const uint32_t sbox_type, void* out_data, size_t out_data_len, size_t* out_data_lenout)
{
	TEE_Result ret;
	int cmd_no = 0;
	const char prefix_name[] = {"TA_"};
	char ta_client_name[MAX_CLIENT_NAME_LEN] = {0,};
	TEE_Param params[4];
	uint32_t returnOrigin;
	t2t_request_t req;
	t2t_response_t rsp;

	memset(&req, 0, sizeof(req));
	memset(&rsp, 0, sizeof(rsp));

	cmd_no = generate_cmd_no();

	LOGI("[%s:VaultKeeper] Reqeust to read sbox(%d/%d)\n", TZ_APP_NAME, sbox_type, cmd_no);

	if (out_data == NULL || out_data_len <= 0 || out_data_len > MAX_TA_BUFFER_LEN) {
		LOGE("[%s:VaultKeeper] Invalid parameter\n", TZ_APP_NAME);
		ret = VK_ERR_OPEN_FAIL;
		goto out;
	}

	memcpy(ta_client_name, prefix_name, 3);
	memcpy(ta_client_name + 3, TZ_APP_NAME, strlen(TZ_APP_NAME));

	if ((ret = open_vk_service()) != VK_SUCCESS) {
		LOGE("[%s:VaultKeeper] Failed to connect VK service(%d/%d)\n", TZ_APP_NAME, cmd_no, ret);
		ret = VK_ERR_OPEN_FAIL;
		goto out;
	}

	req.cmd_no = cmd_no;
	memcpy(req.ta_name, ta_client_name, MAX_CLIENT_NAME_LEN);
	req.cmd_type = VK_CMD_READ_SBOX_BASE;
	req.cmd_sub_type = sbox_type;

	params[0].memref.buffer = &req;
	params[0].memref.size = sizeof(req);
	params[1].memref.buffer = &rsp;
	params[1].memref.size = sizeof(rsp);

	ret = TEE_InvokeTACommand(vk_session, 1000000, CMD_PROTOCOL, param_type, params, &returnOrigin);
	if (ret != TEE_SUCCESS) {
		LOGE("[%s:VaultKeeper] Failed to request command(%08x/%d%d)\n", TZ_APP_NAME, ret, returnOrigin, cmd_no);
		ret = VK_ERR_OPEN_FAIL;
		goto out;
	}

	if (rsp.cmd_no != cmd_no ||
		strncmp((char*)req.ta_name, (char*)rsp.ta_name, MAX_CLIENT_NAME_LEN)) {
		LOGE("[%s:VaultKeeper] Invalid response(%s/%d)\n", TZ_APP_NAME, rsp.ta_name, cmd_no);
		ret = VK_ERR_OPEN_FAIL;
		goto out;
	}

	if (rsp.out_buffer_len > out_data_len) {
		*out_data_lenout = out_data_len;
	} else {
		*out_data_lenout = rsp.out_buffer_len;
	}
	
	memcpy(out_data, rsp.out_buffer, *out_data_lenout);

out:
	if (rsp.result < 0) ret = rsp.result - 1;

	release_vk_service();
	return ret;
}

int vk_read_vault(const uint32_t vault_type, void* out_data, size_t out_data_len, size_t* out_data_lenout)
{
	TEE_Result ret;
	int cmd_no = 0;
	const char prefix_name[] = {"TA_"};
	char ta_client_name[MAX_CLIENT_NAME_LEN] = {0,};
	TEE_Param params[4];
	uint32_t returnOrigin;
	t2t_request_t req;
	t2t_response_t rsp;

	memset(&req, 0, sizeof(req));
	memset(&rsp, 0, sizeof(rsp));

	cmd_no = generate_cmd_no();

	LOGI("[%s:VaultKeeper] Reqeust to read vault(%d/%d)\n", TZ_APP_NAME, vault_type, cmd_no);

	if (out_data == NULL || out_data_len <= 0 || out_data_len > MAX_TA_BUFFER_LEN) {
		LOGE("[%s:VaultKeeper] Invalid parameter\n", TZ_APP_NAME);
		ret = VK_ERR_OPEN_FAIL;
		goto out;
	}

	memcpy(ta_client_name, prefix_name, 3);
	memcpy(ta_client_name + 3, TZ_APP_NAME, strlen(TZ_APP_NAME));

	if ((ret = open_vk_service()) != VK_SUCCESS) {
		LOGE("[%s:VaultKeeper] Failed to connect VK service(%d/%d)\n", TZ_APP_NAME, cmd_no, ret);
		ret = VK_ERR_OPEN_FAIL;
		goto out;
	}

	req.cmd_no = cmd_no;
	memcpy(req.ta_name, ta_client_name, MAX_CLIENT_NAME_LEN);
	req.cmd_type = VK_CMD_READ_BASE;
	req.cmd_sub_type = vault_type;

	params[0].memref.buffer = &req;
	params[0].memref.size = sizeof(req);
	params[1].memref.buffer = &rsp;
	params[1].memref.size = sizeof(rsp);

	ret = TEE_InvokeTACommand(vk_session, 1000000, CMD_PROTOCOL, param_type, params, &returnOrigin);
	if (ret != TEE_SUCCESS) {
		LOGE("[%s:VaultKeeper] Failed to request command(%08x/%d%d)\n", TZ_APP_NAME, ret, returnOrigin, cmd_no);
		ret = VK_ERR_OPEN_FAIL;
		goto out;
	}

	if (rsp.cmd_no != cmd_no ||
		strncmp((char*)req.ta_name, (char*)rsp.ta_name, MAX_CLIENT_NAME_LEN)) {
		LOGE("[%s:VaultKeeper] Invalid response(%s/%d)\n", TZ_APP_NAME, rsp.ta_name, cmd_no);
		ret = VK_ERR_OPEN_FAIL;
		goto out;
	}

	if (rsp.out_buffer_len > out_data_len) {
		*out_data_lenout = out_data_len;
	} else {
		*out_data_lenout = rsp.out_buffer_len;
	}
	
	memcpy(out_data, rsp.out_buffer, *out_data_lenout);

out:
	if (rsp.result < 0) ret = rsp.result - 1;

	release_vk_service();
	return ret;
}

int vk_write_vault(const uint32_t vault_type, const void* in_data, size_t in_data_len)
{
	TEE_Result ret;
	int cmd_no = 0;
	const char prefix_name[] = {"TA_"};
	char ta_client_name[MAX_CLIENT_NAME_LEN] = {0,};
	TEE_Param params[4];
	uint32_t returnOrigin;
	t2t_request_t req;
	t2t_response_t rsp;

	memset(&req, 0, sizeof(req));
	memset(&rsp, 0, sizeof(rsp));

	cmd_no = generate_cmd_no();

	LOGI("[%s:VaultKeeper] Reqeust to write vault(%d/%d)\n", TZ_APP_NAME, vault_type, cmd_no);

	if (in_data == NULL || in_data_len <= 0 || in_data_len > MAX_TA_BUFFER_LEN) {
		LOGE("[%s:VaultKeeper] Invalid parameter\n", TZ_APP_NAME);
		ret = VK_ERR_OPEN_FAIL;
		goto out;
	}

	memcpy(ta_client_name, prefix_name, 3);
	memcpy(ta_client_name + 3, TZ_APP_NAME, strlen(TZ_APP_NAME));

	if ((ret = open_vk_service()) != VK_SUCCESS) {
		LOGE("[%s:VaultKeeper] Failed to connect VK service(%d/%d)\n", TZ_APP_NAME, cmd_no, ret);
		ret = VK_ERR_OPEN_FAIL;
		goto out;
	}

	req.cmd_no = cmd_no;
	memcpy(req.ta_name, ta_client_name, MAX_CLIENT_NAME_LEN);
	req.cmd_type = VK_CMD_WRITE_BASE;
	req.cmd_sub_type = vault_type;
	memcpy(&(req.in_buffer), in_data, in_data_len);
	req.in_buffer_len = in_data_len;

	params[0].memref.buffer = &req;
	params[0].memref.size = sizeof(req);
	params[1].memref.buffer = &rsp;
	params[1].memref.size = sizeof(rsp);

	ret = TEE_InvokeTACommand(vk_session, 1000000, CMD_PROTOCOL, param_type, params, &returnOrigin);
	if (ret != TEE_SUCCESS) {
		LOGE("[%s:VaultKeeper] Failed to request command(%08x/%d%d)\n", TZ_APP_NAME, ret, returnOrigin, cmd_no);
		ret = VK_ERR_OPEN_FAIL;
		goto out;
	}

	if (rsp.cmd_no != cmd_no ||
		strncmp((char*)req.ta_name, (char*)rsp.ta_name, MAX_CLIENT_NAME_LEN)) {
		LOGE("[%s:VaultKeeper] Invalid response(%s/%d)\n", TZ_APP_NAME, rsp.ta_name, cmd_no);
		ret = VK_ERR_OPEN_FAIL;
		goto out;
	}

out:
	if (rsp.result < 0) ret = rsp.result - 1;

	release_vk_service();
	return ret;
}

