#include "em_common.h"

static void em_request_make_header(em_header_info *header, const char *type)
{
	if (header == NULL || type == NULL)
		return;

	memcpy(header->prefix, EM_MAGIC_HEADER_PREFIX, EM_LEN_HEADER_FIELD);
	memcpy(header->type, type, EM_LEN_HEADER_FIELD);
	memcpy(header->version, EM_MAGIC_PACKET_VERSION, EM_LEN_HEADER_VERSION);
}

int em_request_token(uint8_t *message, uint32_t *len_message, uint64_t *flags, uint8_t *esi, em_keeping_item *keep,
		     const uint8_t *imei, const uint8_t *model_name, const uint8_t *otp, const uint8_t *single_id,
		     const uint16_t *modes, const uint16_t len_mode, const uint8_t *date)
{
	int ret;

	uint16_t i = 0;

	em_header_info header = {0,};
	em_default_meta meta = {0,};
	em_default_element elem = {0,};

	uint8_t did[EM_LEN_DID] = {0,};
	uint32_t offset = 0, offset_base = 0, pos_set = 0;
	uint32_t len_header = 0, num_of_data = 0;
	uint16_t elem_buf = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_REQUEST_TOKEN, message, len_message, flags, esi, keep, imei, model_name, otp,
		      single_id, modes, date);

	em_request_make_header(&header, EM_MAGIC_TYPE_TOKEN_REQUEST);
	memcpy(message, &header, sizeof(em_header_info));
	offset += sizeof(em_header_info);

	pos_set = (uint32_t)(offset + sizeof(uint32_t));

	len_header = sizeof(em_header_info) + sizeof(uint32_t) + (sizeof(uint32_t) * 5);
	memcpy(message + offset, &len_header, sizeof(uint32_t));
	offset += sizeof(uint32_t) + (sizeof(uint32_t) * 5);

	memcpy(message + pos_set, &offset, sizeof(uint32_t)); // Token Offset;
	pos_set += sizeof(uint32_t);

	// Token Information
	offset_base = offset;
	offset += sizeof(em_default_meta);

	elem.type = EM_TYPE_INFO_TOKE_ID;
	elem.len = EM_LEN_TOKEN_ID;
	memcpy(message + offset, &elem, sizeof(em_default_element));
	offset += sizeof(em_default_element);
	offset += EM_LEN_TOKEN_ID;
	num_of_data++;

	elem.type = EM_TYPE_INFO_TOKE_UNIQUE_INFO;
	elem.len = 0;
	memcpy(message + offset, &elem, sizeof(em_default_element));
	offset += sizeof(em_default_element);
	num_of_data++;

	elem.type = EM_TYPE_INFO_TOKE_NUM_DEVICES;
	elem.len = sizeof(uint16_t);
	memcpy(message + offset, &elem, sizeof(em_default_element));
	offset += sizeof(em_default_element);
	elem_buf = 1;
	memcpy(message + offset, &elem_buf, sizeof(uint16_t));
	offset += sizeof(uint16_t);
	num_of_data++;

	memcpy(meta.magic, EM_MAGIC_TOKEN, strlen(EM_MAGIC_TOKEN));
	meta.len = offset - offset_base - sizeof(em_default_meta);
	meta.num_of_data = num_of_data;
	memcpy(message + offset_base, &meta, sizeof(em_default_meta));

	// Device Information
	memcpy(message + pos_set, &offset, sizeof(uint32_t));
	pos_set += sizeof(uint32_t);

	offset_base = offset;
	num_of_data = elem_buf = 0;
	offset += sizeof(em_default_meta);

	if (strlen((const char *)model_name) > EM_LEN_MODEL_NAME) {
		LOGE("model name isn't normal(%u)\n", (uint32_t)strlen((const char *)model_name));
		ret = EM_ERR_EM_REQUEST_TOKEN_MODEL_NAME;
		goto out;
	}

	elem.type = EM_TYPE_INFO_DEVI_MODEL_NAME;
	elem.len = strlen((const char *)model_name);
	memcpy(message + offset, &elem, sizeof(em_default_element));
	offset += sizeof(em_default_element);
	memcpy(message + offset, model_name, elem.len);
	offset += elem.len;
	num_of_data++;
	memcpy(keep->model_name, model_name, elem.len);

	elem.type = EM_TYPE_INFO_DEVI_DID;
	elem.len = EM_LEN_DID;
	memcpy(message + offset, &elem, sizeof(em_default_element));
	offset += sizeof(em_default_element);
	ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_DID, message + offset, EM_LEN_DID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get did from esi(0x%08x)\n", ret);
		goto out;
	}
	offset += elem.len;
	num_of_data++;

	elem.type = EM_TYPE_INFO_DEVI_IMEI;
	elem.len = EM_LEN_IMEI;
	memcpy(message + offset, &elem, sizeof(em_default_element));
	offset += sizeof(em_default_element);

	if (!(flags[0] & EM_FLAGS_0_EXIST_IMEI)) {
		memset(message + offset, EM_MAGIC_PADDING, EM_LEN_IMEI);
	} else {
		memcpy(message + offset, imei, EM_LEN_IMEI);
	}
	offset += elem.len;
	num_of_data++;

	memcpy(meta.magic, EM_MAGIC_TOKEN_DEVICE, strlen(EM_MAGIC_TOKEN_DEVICE));
	meta.len = offset - offset_base - sizeof(em_default_meta);
	meta.num_of_data = num_of_data;
	memcpy(message + offset_base, &meta, sizeof(em_default_meta));

	// Issuer Information
	memcpy(message + pos_set, &offset, sizeof(uint32_t));
	pos_set += sizeof(uint32_t);

	offset_base = offset;
	num_of_data = elem_buf = 0;
	offset += sizeof(em_default_meta);

	if (strlen((const char *)single_id) > EM_LEN_SINGLE_ID) {
		LOGE("single id isn't normal(%u)\n", (uint32_t)strlen((const char *)single_id));
		ret = EM_ERR_EM_REQUEST_TOKEN_SINGLE_ID;
		goto out;
	}

	elem.type = EM_TYPE_INFO_ISSU_SINGLE_ID;
	elem.len = strlen((const char *)single_id);
	memcpy(message + offset, &elem, sizeof(em_default_element));
	offset += sizeof(em_default_element);
	memcpy(message + offset, single_id, elem.len);
	offset += elem.len;
	num_of_data++;
	memcpy(keep->single_id, single_id, elem.len);

	memcpy(keep->otp, otp, EM_LEN_OTP);
	elem.type = EM_TYPE_INFO_ISSU_OTP;
	elem.len = EM_LEN_OTP;
	memcpy(message + offset, &elem, sizeof(em_default_element));
	offset += sizeof(em_default_element);
	memcpy(message + offset, otp, elem.len);
	offset += elem.len;
	num_of_data++;

	elem.type = EM_TYPE_INFO_ISSU_NONCE;
	elem.len = EM_LEN_NONCE;
	memcpy(message + offset, &elem, sizeof(em_default_element));
	offset += sizeof(em_default_element);
	ret = em_get_random(keep->nonce, EM_LEN_NONCE);
	if (ret != EM_LEN_NONCE) {
		LOGE("Failed to generate nonce (0x%08x)\n", ret);
		ret = EM_ERR_EM_REQUEST_TOKEN_NONCE;
		goto out;
	}
	memcpy(message + offset, keep->nonce, elem.len);
	offset += elem.len;
	num_of_data++;

	memcpy(meta.magic, EM_MAGIC_TOKEN_ISSUER, strlen(EM_MAGIC_TOKEN_ISSUER));
	meta.len = offset - offset_base - sizeof(em_default_meta);
	meta.num_of_data = num_of_data;
	memcpy(message + offset_base, &meta, sizeof(em_default_meta));

	// Mode Information
	memcpy(message + pos_set, &offset, sizeof(uint32_t));
	pos_set += sizeof(uint32_t);

	offset_base = offset;
	num_of_data = elem_buf = 0;

	if (len_mode > EM_LEN_MAX_MODE) {
		LOGE("Exceeds the number of modes you can request at a time(%d)\n", len_mode);
		ret = EM_ERR_EM_REQUEST_TOKEN_MODE;
		goto out;
	}

	memcpy(meta.magic, EM_MAGIC_TOKEN_MODE, strlen(EM_MAGIC_TOKEN_MODE));
	meta.len = len_mode * sizeof(em_default_element);
	meta.num_of_data = len_mode;
	memcpy(message + offset_base, &meta, sizeof(em_default_meta));
	offset += sizeof(em_default_meta);

	elem.len = 0;
	for (i=0; i < len_mode; i++) {
		elem.type = modes[i];
		memcpy(message + offset, &elem, sizeof(em_default_element));
		offset += sizeof(em_default_element);
	}

	// Validation Information
	memcpy(message + pos_set, &offset, sizeof(uint32_t));
	pos_set += sizeof(uint32_t);

	offset_base = offset;
	num_of_data = elem_buf = 0;
	offset += sizeof(em_default_meta);

	memcpy(keep->date, date, EM_LEN_DATE);
	elem.type = EM_TYPE_INFO_VALI_EXPIRY_DATE;
	elem.len = EM_LEN_DATE;
	memcpy(message + offset, &elem, sizeof(em_default_element));
	offset += sizeof(em_default_element);
	memcpy(message + offset, keep->date, elem.len);
	offset += elem.len;
	num_of_data++;

	memcpy(meta.magic, EM_MAGIC_TOKEN_VALIDATE, strlen(EM_MAGIC_TOKEN_VALIDATE));
	meta.len = offset - offset_base - sizeof(em_default_meta);
	meta.num_of_data = num_of_data;
	memcpy(message + offset_base, &meta, sizeof(em_default_meta));

	*len_message = offset;

	flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;
	flags[1] |= EM_FLAGS_1_EXIST_RETURN_KEEPING_ITEM;
	ret = EM_SUCCESS;
out:
	return ret;
}

int em_request_time(uint8_t *message, uint32_t *len_message, uint64_t *flags, em_keeping_item *keep)
{
	int ret;

	uint32_t offset = 0;

	em_header_info header = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_REQUEST_TIME, message, len_message, flags, keep);

	em_request_make_header(&header, EM_MAGIC_TYPE_TIME_REQUEST);

	memcpy(message, &header, sizeof(em_header_info));
	offset += sizeof(em_header_info);

	ret = em_get_random(keep->nonce, EM_LEN_NONCE);
	if (ret != EM_LEN_NONCE) {
		LOGE("Failed to generate nonce(0x%08x)\n", ret);
		ret = EM_ERR_EM_REQUEST_TIME_NONCE;
		goto out;
	}

	memcpy(message + offset, keep->nonce, EM_LEN_NONCE);
	offset += EM_LEN_NONCE;

	*len_message = offset;

	flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;
	flags[1] |= EM_FLAGS_1_EXIST_RETURN_KEEPING_ITEM;

	ret = EM_SUCCESS;
out:
	return ret;
}

int em_reqeust_recovery(em_context *ctx)
{
	int ret;
	uint8_t *packet = NULL;
	uint32_t offset = 0;

	em_header_info header = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_REQUEST_RECOVERY, ctx);
	packet = ctx->message;

	em_request_make_header(&header, EM_MAGIC_TYPE_RECOVERY_REQUEST);

	memcpy(packet, &header, sizeof(em_header_info));
	offset += sizeof(em_header_info);

	ret = em_get_random(ctx->keep.nonce, EM_LEN_NONCE);
	if (ret != EM_LEN_NONCE) {
		LOGE("Failed to get nonce(0x%08x)\n", ret);
		ret = EM_ERR_EM_REQUEST_RECOVERY_NONCE;
		goto out;
	}

	if (strlen((const char *)ctx->single_id) > EM_LEN_SINGLE_ID) {
		LOGE("single_id size is abnormal(%d)\n", strlen((const char *)ctx->single_id));
		ret = EM_ERR_EM_REQUEST_TOKEN_SINGLE_ID;
		goto out;
	}

	memcpy(packet + offset, ctx->single_id, strlen((const char *)ctx->single_id));
	offset += 40;

	memcpy(packet + offset, ctx->otp, EM_LEN_OTP);
	offset += EM_LEN_OTP;

	memcpy(packet + offset, ctx->did, EM_LEN_DID);
	offset += EM_LEN_DID;

	memcpy(packet + offset, ctx->keep.nonce, EM_LEN_NONCE);
	offset += EM_LEN_NONCE;

	LOGE("<<<ydp >>> %s %s %s\n", ctx->single_id, ctx->otp, ctx->did);

	ctx->len_message = offset;
	LOGI("Success to make RecoveryITL req msg!(%u)\n", offset);

	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_KEEPING_ITEM | EM_FLAGS_1_EXIST_RETURN_MESSAGE;

	ret = EM_SUCCESS;
out:
	return ret;
}
