
#include "em_common.h"

static int em_cmd_pre_processing(em_context *ctx);
static int em_cmd_processing(em_context *ctx);
static int em_cmd_post_processing(em_context *ctx);
static int em_cmd_get_ststus(em_context *ctx);
static void em_cmd_print(uint32_t cmd);

int em_cmd_handler(em_context *ctx)
{
	int ret;

	EM_CHECK_NULL(__func__, EM_ERR_CMD_HANDLER, ctx);

	ret = em_cmd_pre_processing(ctx);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to em_cmd_pre_processing(0x%08x)\n", ret);
		goto out;
	}

	ret = em_cmd_processing(ctx);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to em_cmd_processing(0x%08x)\n", ret);
		goto out;
	}

	ret = em_cmd_post_processing(ctx);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to em_cmd_post_processing(0x%08x)\n", ret);
		goto out;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_cmd_pre_processing(em_context *ctx)
{
	int ret;

	uint8_t ess_need_prop_did = 0;

	em_esi_meta meta = {0,};
	em_core_v20 *core = NULL;

	em_cmd_print(ctx->cmd);

	core = (em_core_v20 *)em_calloc(sizeof(em_core_v20), 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_CMD_PRE_PROCESSING, ctx, core);

	if (ctx->cmd == EM_CMD_ESS) {
		ret = em_ess_get_command_type(ctx);
		if (ret != EM_SUCCESS)
			LOGE("%s : Failed to get command type(0x%08x)\n", __func__, ret);
		else
			LOGI("Converted cmd type 0x%08x\n", ctx->cmd);

		if (ctx->cmd == EM_CMD_REQ_RECOVERY_ESS_V1 || ctx->cmd == EM_CMD_RECOVERY_ESS_V1)
			ess_need_prop_did = 1;
	}

	if (ctx->cmd == EM_CMD_INIT || ctx->cmd == EM_CMD_RECOVERY || ess_need_prop_did == 1 ||
	    ctx->cmd == EM_CMD_MAKE_ITL_REQ) {
		if ((ctx->flags[0] & EM_FLAGS_0_EXIST_ESI) &&
		    memcmp(ctx->esi, EM_MAGIC_ESI, strlen(EM_MAGIC_ESI)) != 0) {
			if (em_is_all_zero(ctx->did, EM_LEN_DID) == EM_SUCCESS) {
				LOGE("Did doesn't exist\n");
				ret = EM_ERR_EM_CMD_PRE_PROCESSING_UNKNOWN_DID;
				goto out;
			}

			if (memcmp(ctx->did, "30", 2) == 0)
				ctx->esi_version = 30;
			else
				ctx->esi_version = 20;
		}
		ret = EM_SUCCESS;
		goto out;
	}

	memcpy(&meta, ctx->esi + EM_LEN_ESI_DIGEST, sizeof(em_esi_meta));

	if (meta.version == 30) {
		ctx->esi_version = 30;
		// TODO : EM3.0 Verify ESI
	} else {
		ctx->esi_version = 20;
		ret = em_esi_check_validation_v20(ctx->esi, ctx->is_provision);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to check esi validation(0x%08x)\n", ret);
			ctx->flags[1] |= EM_FLAGS_1_NEED_RECOVERY_ESI;
			goto out;
		}

		ret = em_esi_get_item(ctx->esi, EM_TYPE_ESI_ITEM_DID, ctx->did, EM_LEN_DID);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get did from esi(0x%08x)\n", ret);
			goto out;
		}

		if (ctx->is_provision) {
			ret = em_read_core((uint8_t *)core, sizeof(em_core_v20));
			if (ret != EM_SUCCESS) {
				if ((uint32_t)ret == EM_ERR_EM_READ_CORE_ALL_ZERO) {
					LOGE("Core is all zero(0x%08x)\n", ret);
				} else {
					LOGE("Failed to read core(0x%08x)\n", ret);
					goto out;
				}
			}
			if (memcmp(core->magic, EM_MAGIC_EM_CORE, strlen(EM_MAGIC_EM_CORE)) == 0)
				memcpy(ctx->key, core->key, EM_LEN_KEY_CORE_V20);
		}

		if (!ctx->is_provision || memcmp(core->magic, EM_MAGIC_EM_CORE, strlen(EM_MAGIC_EM_CORE)) != 0) {
			ret = em_esi_get_item(ctx->esi, EM_TYPE_ESI_ITEM_SHARED_KEY, ctx->key, EM_LEN_KEY_CORE_V20);
			if (ret != EM_SUCCESS) {
				LOGE("Failed to get key from esi(0x%08x)\n", ret);
				goto out;
			}
		}
	}

	ret = EM_SUCCESS;
out:
	if (core) {
		memset(core, 0, sizeof(em_core_v20));
		em_free(core);
		core = NULL;
	}

	return ret;
}

static int em_cmd_processing(em_context *ctx)
{
	int ret;

	EM_CHECK_NULL(__func__, EM_ERR_EM_CMD_PROCESSING, ctx);

	switch (ctx->cmd) {
	case EM_CMD_GET_STATUS:
		ret = em_token_get_status(ctx, ctx->token, ctx->esi, ctx->modes[0], ctx->date, ctx->flags,
					  ctx->is_provision);
		break;
	case EM_CMD_INIT:
		ret = em_init_v20(ctx->esi, ctx->flags, ctx->did, ctx->is_provision);
		break;
	case EM_CMD_INSTALL_TOKEN:
		ret = em_token_install(ctx->esi_version, ctx->cmd, ctx->flags, ctx->esi, ctx->token, ctx->did,
				       ctx->is_provision, &ctx->keep, ctx->key);
		break;
	case EM_CMD_TOKEN_IS_INSTALLED:
		ret = em_token_is_installed(ctx->token, ctx->esi, ctx->flags);
		break;
	case EM_CMD_GET_TUC:
		ret = em_token_get_usage_count(ctx->esi_version, ctx->esi, ctx->modes[0], &ctx->ret_temp);
		break;
	case EM_CMD_GET_MODES:
		ret = em_token_get_mode_information(ctx->token, ctx->esi, ctx->flags, ctx->message, &ctx->len_message);
		break;
	case EM_CMD_GET_MODES_BIT:
		ret = em_token_get_mode_information_for_bit(ctx->token, ctx->esi, ctx->flags, ctx->mode_bits);
		break;
	case EM_CMD_ESS:
		ret = em_ess_command(ctx);
		break;
	case EM_CMD_REQ_TOKEN_ESS_V1:
		ret = em_ess_command(ctx);
		break;
	case EM_CMD_INSTALL_TOKEN_ESS_V1:
		ret = em_ess_command(ctx);
		break;
	case EM_CMD_DELETE_TOKEN_ESS_V1:
		ret = em_ess_command(ctx);
		break;
	case EM_CMD_REQ_RECOVERY_ESS_V1:
		ret = em_ess_command(ctx);
		break;
	case EM_CMD_RECOVERY_ESS_V1:
		ret = em_ess_command(ctx);
		break;
	case EM_CMD_SUPPORT_ESS_V1:
		ret = em_ess_command(ctx);
		break;
	case EM_CMD_GET_MODES_ESS_V1:
		ret = em_ess_command(ctx);
		break;
	case EM_CMD_GET_PRIORITY_TIME:
	case EM_CMD_SET_PRIORITY_TIME:
		ret = em_ess_command(ctx);
		break;
	case EM_CMD_TOKEN_REQUEST:
		ret = em_request_token(ctx->message, &ctx->len_message, ctx->flags, ctx->esi, &ctx->keep, ctx->imei,
				       ctx->model_name, ctx->otp, ctx->single_id, ctx->modes, ctx->cnt_mode, ctx->date);
		break;
	case EM_CMD_TIME_REQUEST:
		ret = em_request_time(ctx->message, &ctx->len_message, ctx->flags, &ctx->keep);
		break;
	case EM_CMD_TIME_CHECK:
		ret = em_token_check_time_msg(ctx->message, ctx->len_message, ctx->flags, &ctx->keep);
		break;
	case EM_CMD_MAKE_ITL_REQ:
		ret = em_reqeust_recovery(ctx);
		break;
	case EM_CMD_RECOVERY:
		ret = em_esi_recovery(ctx);
		break;
	default:
		LOGE("Unknown command(0x%08x)\n", ctx->cmd);
		ret = EM_ERR_EM_CMD_PROCESSING_UNKNOWN_COMMAND;
		goto out;
	}

	if (ret != EM_SUCCESS) {
		LOGE("Failed to process command(0x%04x/0x%08x)\n", ctx->cmd, ret);
		goto out;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_cmd_post_processing(em_context *ctx)
{
	int ret;

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_ESI) || ctx->cmd == EM_CMD_REQ_RECOVERY_ESS_V1 || ctx->cmd == EM_CMD_MAKE_ITL_REQ) {
		ret = EM_SUCCESS;
		goto out;
	}

	if (ctx->esi_version == 20)
		ret = em_esi_check_validation_v20(ctx->esi, ctx->is_provision);
	else
		ret = em_esi_check_validation(ctx->esi);

	if (ret == EM_SUCCESS)
		LOGE("Validate!!\n");

out:
	memset(ctx->key, 0, EM_LEN_KEY_CORE_V20);

	return ret;
}

static void em_cmd_print(uint32_t cmd)
{
	switch (cmd) {
	case EM_CMD_GET_STATUS:
		LOGI("EM_CMD_GET_STATUS\n");
		break;
	case EM_CMD_INIT:
		LOGI("EM_CMD_INIT\n");
		break;
	case EM_CMD_INSTALL_TOKEN:
		LOGI("EM_CMD_INSTALL_TOKEN\n");
		break;
	case EM_CMD_TOKEN_IS_INSTALLED:
		LOGI("EM_CMD_TOKEN_IS_INSTALLED\n");
		break;
	case EM_CMD_GET_TUC:
		LOGI("EM_CMD_GET_TUC\n");
		break;
	case EM_CMD_GET_MODES:
		LOGI("EM_CMD_GET_MODES\n");
		break;
	case EM_CMD_GET_MODES_BIT:
		LOGI("EM_CMD_GET_MODES_BIT\n");
		break;
	case EM_CMD_ESS:
		LOGI("EM_CMD_ESS\n");
		break;
	case EM_CMD_REQ_TOKEN_ESS_V1:
		LOGI("EM_CMD_REQ_TOKEN_ESS_V1\n");
		break;
	case EM_CMD_INSTALL_TOKEN_ESS_V1:
		LOGI("EM_CMD_INSTALL_TOKEN_ESS_V1\n");
		break;
	case EM_CMD_DELETE_TOKEN_ESS_V1:
		LOGI("EM_CMD_DELETE_TOKEN_ESS_V1\n");
		break;
	case EM_CMD_REQ_RECOVERY_ESS_V1:
		LOGI("EM_CMD_REQ_RECOVERY_ESS_V1\n");
		break;
	case EM_CMD_RECOVERY_ESS_V1:
		LOGI("EM_CMD_RECOVERY_ESS_V1\n");
		break;
	case EM_CMD_SUPPORT_ESS_V1:
		LOGI("EM_CMD_SUPPORT_ESS_V1\n");
		break;
	case EM_CMD_GET_MODES_ESS_V1:
		LOGI("EM_CMD_GET_MODES_ESS_V1\n");
		break;
	case EM_CMD_TOKEN_REQUEST:
		LOGI("EM_CMD_TOKEN_REQUEST\n");
		break;
	case EM_CMD_TIME_REQUEST:
		LOGI("EM_CMD_TIME_REQUEST\n");
		break;
	case EM_CMD_TIME_CHECK:
		LOGI("EM_CMD_TIME_CHECK\n");
		break;
	default:
		LOGE("Unknown command(0x%08x)\n", cmd);
	}
}
