#include "em_common.h"

static void em_esi_item_free(em_esi_item *items, int num_of_items)
{
	int i = 0;
	em_esi_item *p = NULL;

	if (items == NULL)
		return;

	p = items;

	if (p) {
		for (i = 0; i < num_of_items; i++) {
			if (p[i].data) {
				em_free(p[i].data);
				p[i].data = NULL;
			}
		}
		em_free(p);
		p = NULL;
	}
}

int em_esi_check_init_state(const uint8_t *init_state)
{
	int ret = EM_ERR_EM_ESI_CHECK_INIT_STATE_UNKNOWN;

	if (memcmp(init_state, EM_MAGIC_ESI_V20_SHARING, strlen(EM_MAGIC_ESI_V20_SHARING)) == 0) {
		ret = EM_TYPE_INIT_STATE_SHARING;
	} else if (memcmp(init_state, EM_MAGIC_ESI_V20_SHARED, strlen(EM_MAGIC_ESI_V20_SHARED)) == 0) {
		ret = EM_TYPE_INIT_STATE_SHARED;
	} else if (memcmp(init_state, EM_MAGIC_ESI_V20_PRESHARED, strlen(EM_MAGIC_ESI_V20_PRESHARED)) == 0) {
		ret = EM_TYPE_INIT_STATE_PRESHARED;
	} else if (memcmp(init_state, EM_MAGIC_ESI_V20_INTERMEDIATE, strlen(EM_MAGIC_ESI_V20_INTERMEDIATE)) == 0) {
		ret = EM_TYPE_INIT_STATE_INTERMEDIATE;
	} else if (memcmp(init_state, EM_MAGIC_ESI_V20_COMPLETED, strlen(EM_MAGIC_ESI_V20_COMPLETED)) == 0) {
		ret = EM_TYPE_INIT_STATE_COMPLETED;
	} else {
		LOGE("%02x%02x%02x%02x%02x%02x%02x%02x\n", init_state[0], init_state[1], init_state[2],
		     init_state[3], init_state[4], init_state[5], init_state[6], init_state[7]);
		LOGE("%02x%02x%02x%02x%02x%02x%02x%02x\n", init_state[8], init_state[9], init_state[10],
		     init_state[11], init_state[12], init_state[13], init_state[14], init_state[15]);
	}
out:
	return ret;
}

int em_esi_get_all_item(const uint8_t *esi, em_esi_item *buf, uint32_t buf_size)
{
	int ret, i = 0;
	em_esi_meta meta = {0,};
	uint32_t offset = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_GET_ALL_ITEM, esi, buf);

	offset += EM_LEN_ESI_DIGEST;
	memcpy(&meta, esi + offset, sizeof(em_esi_meta));
	offset += sizeof(em_esi_meta);

	if (buf_size < meta.num_of_items) {
		LOGE("buf size isn't enough(%u/%u)\n", buf_size, meta.num_of_items);
		ret = EM_ERR_EM_ESI_GET_ALL_ITEM_BUF_SIZE;
		goto out;
	}

	for (i = 0; i < meta.num_of_items; i++) {
		if (offset >= EM_LEN_ESI) {
			LOGE("meta data isn't normal(%u/%u)\n", offset, EM_LEN_ESI);
			ret = EM_ERR_EM_ESI_GET_ALL_ITEM_META_DATA;
			goto out;
		}

		memcpy(&(buf[i].elem), esi + offset, sizeof(em_default_element));
		offset += sizeof(em_default_element);

		buf[i].data = (uint8_t *)em_calloc(buf[i].elem.len, 1);
		if (buf[i].data == NULL) {
			if (buf[i].elem.len == 0)
				continue;

			LOGE("Failed to allocate items[%u/%u].data memory\n", i, buf[i].elem.type);
			ret = EM_ERR_EM_ESI_GET_ALL_ITEM_ALLOC_ITEM_BUF;
			goto out;
		}
		memcpy(buf[i].data, esi + offset, buf[i].elem.len);
		offset += buf[i].elem.len;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_esi_update_digest(const uint32_t version, uint8_t *esi, const uint8_t *key)
{
	int ret;
	uint8_t digest[EM_LEN_ESI_DIGEST] = {0,};
	em_esi_meta meta = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_UPDATE_DIGEST, esi);

	memcpy(&meta, esi + EM_LEN_ESI_DIGEST, sizeof(em_esi_meta));

	if (version == 20) {
		if (key == NULL) {
			LOGI("Key isn't exists\n");
			ret = EM_SUCCESS;
			goto out;
		}

		ret = em_crypto_hmac(digest, esi + EM_LEN_ESI_DIGEST, sizeof(em_esi_meta) + meta.size_of_items, key,
				     EM_LEN_KEY_CORE_V20);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to make hmac(0x%08x)\n", ret);
			goto out;
		}
	} else if (version == 30) {
		ret = em_crypto_sha256(esi + EM_LEN_ESI_DIGEST, sizeof(em_esi_meta) + meta.size_of_items, digest);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to make hash(0x%08x)\n", ret);
			goto out;
		}
	} else {
		ret = EM_ERR_EM_ESI_UPDATE_DIGEST_UNKNOWN_VERSION;
		goto out;
	}

	memcpy(esi, digest, EM_LEN_ESI_DIGEST);
	ret = EM_SUCCESS;
out:
	return ret;
}

int em_esi_initialize(uint32_t version, uint8_t *buf, uint8_t *key, uint32_t write_counter)
{
	int ret;
	uint8_t digest[EM_LEN_ESI_DIGEST] = {0,};
	em_esi_meta meta = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_INIT, buf);

	memcpy(meta.magic, EM_MAGIC_ESI, EM_LEN_ESI_MAGIC);

#ifdef EMAS
	meta.writer = 'E';
#elif EMAS
	meta.writer = 'B';
#endif
	meta.write_counter = write_counter;
	meta.size_of_items = 0;
	meta.num_of_items = 0;
	meta.version = version;

	memcpy(buf + EM_LEN_ESI_DIGEST, &meta, sizeof(em_esi_meta));

	ret = em_esi_update_digest(version, buf, key);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to update digest(0x%08x)\n", ret);
		goto out;
	}

	ret = EM_SUCCESS;
out:

	return ret;
}

int em_esi_update(const uint32_t version, uint8_t *esi_buf, em_esi_meta *meta, const em_esi_item *item,
		  const uint8_t *key, const uint32_t key_len)
{
	int ret, i;
	uint32_t offset = 0;

	uint8_t digest[EM_LEN_ESI_DIGEST] = {0,};
	em_default_element elem = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_UPDATE, esi_buf, meta);

#ifdef EMAS
	meta->writer = 'E';
#else
	meta->wirter = 'B';
#endif

	offset += EM_LEN_ESI_DIGEST;
	memcpy(esi_buf + offset, meta, sizeof(em_esi_meta));
	offset += sizeof(em_esi_meta);

	if (item != NULL) {
		for (i = 0; i < meta->num_of_items; i++) {
			memcpy(esi_buf + offset, &item[i].elem, sizeof(em_default_element));
			offset += sizeof(em_default_element);

			if (item[i].elem.type > EM_TYPE_ESI_ITEM_MAX)
				LOGI("Unknown item type(%u/%u)\n", item[i].elem.type, item[i].elem.len);

			if (offset + item[i].elem.len >= EM_LEN_ESI) {
				LOGE("Buffer size isn't enough(%u/%u)\n", offset + item[i].elem.len,
				     EM_LEN_ESI);
				ret = EM_ERR_EM_ESI_UPDATE_BUFFER_SIZE;
				goto out;
			}
			memcpy(esi_buf + offset, item[i].data, item[i].elem.len);
			offset += item[i].elem.len;
		}
	}

	ret = em_esi_update_digest(version, esi_buf, key);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to update digest(0x%08x)\n", ret);
		goto out;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

int em_esi_update_item(const uint32_t version, uint8_t *esi, const uint32_t type, const uint8_t *data,
		       const uint8_t *key, const uint32_t key_len)
{
	int ret, i = 0, exist_flags = 0, index = 0;
	uint32_t offset = 0, item_size = 0;
	em_esi_meta meta = {0,};
	em_esi_item *items = NULL, *p = NULL, *temp = NULL;

	tuc_table_v20 *p_tuc_table_v20 = NULL;
	tuc_table *p_tuc_table = NULL;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_UPDATE_ITEM, esi, data);

	offset += EM_LEN_ESI_DIGEST;
	memcpy(&meta, esi + offset, sizeof(em_esi_meta));

	if (meta.num_of_items > 0) {
		items = (em_esi_item *)em_calloc(sizeof(em_esi_item), meta.num_of_items);
		if (items == NULL) {
			LOGE("Failed to allocate memory\n");
			ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_ITEMS;
			goto out;
		}
	}

	if (items != NULL) {
		ret = em_esi_get_all_item(esi, items, meta.num_of_items);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get item(0x%08x)\n", ret);
			goto out;
		}
	}

	index = -1;
	for (i = 0; i < meta.num_of_items; i++)
	{
		if (type == items[i].elem.type) {
			exist_flags = 1;
			index = i;
			break;
		}
	}

	item_size = meta.size_of_items;

	if (exist_flags == 1) {
		temp = &items[index];
		switch (type) {
		case EM_TYPE_ESI_ITEM_INIT_STATE:
		case EM_TYPE_ESI_ITEM_TOKEN_STATE:
			if (temp->data)
				em_free(temp->data);
			item_size -= temp->elem.len;
			temp->elem.len = strlen((char *)data);
			temp->data = (uint8_t *)em_calloc(temp->elem.len, 1);
			if (temp->data == NULL) {
				LOGE("Failed to allocate buffer(%u)\n", type);
				ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_STATE_BUFFER;
				goto out;
			}
			break;
		case EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY:
			if (temp->data)
				em_free(temp->data);
			item_size -= temp->elem.len;

			p_tuc_table_v20 = (tuc_table_v20 *)data;
			temp->elem.len = (p_tuc_table_v20->num_of_tuc * sizeof(tuc_v20)) + 2;

			temp->data = (uint8_t *)em_calloc(temp->elem.len, 1);
			if (temp->data == NULL) {
				LOGE("failed to allocate memory tuc buffer\n");
				ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_TUC_BUFFER;
				goto out;
			}
			item_size += temp->elem.len;

			//memcpy(temp->data, p_tuc_table_v20, sizeof(uint16_t) + (sizeof(tuc_v20) * p_tuc_table_v20->num_of_tuc));
			memcpy(temp->data, &(p_tuc_table_v20->num_of_tuc), sizeof(uint16_t));
			offset = sizeof(uint16_t);
			for (i = 0; i < p_tuc_table_v20->num_of_tuc; i++) {
				memcpy(temp->data + offset, &(p_tuc_table_v20->tucs[i].mode_index), sizeof(uint16_t));
				offset += sizeof(uint16_t);
				memcpy(temp->data + offset, &(p_tuc_table_v20->tucs[i].flags), sizeof(uint8_t));
				offset += sizeof(uint8_t);
				memcpy(temp->data + offset, &(p_tuc_table_v20->tucs[i].count), sizeof(uint32_t));
				offset += sizeof(uint32_t);
			}
			break;
		case EM_TYPE_ESI_ITEM_TUC_TABLES:
			if (temp->data)
				em_free(temp->data);
			item_size -= temp->elem.len;

			p_tuc_table = (tuc_table *)data;
			temp->elem.len = (p_tuc_table->num_of_tuc * sizeof(tuc)) + 2;

			temp->data = (uint8_t *)em_calloc(temp->elem.len, 1);
			if (temp->data == NULL) {
				LOGE("failed to allocate memory tuc buffer\n");
				ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_TUC_BUFFER2;
				goto out;
			}
			item_size += temp->elem.len;

			memcpy(temp->data, &(p_tuc_table->num_of_tuc), sizeof(uint16_t));
			offset = sizeof(uint16_t);
			for (i = 0; i < p_tuc_table->num_of_tuc; i++) {
				memcpy(temp->data + offset, &(p_tuc_table->tucs[i].mode_index), sizeof(uint16_t));
				offset += sizeof(uint16_t);
				memcpy(temp->data + offset, &(p_tuc_table->tucs[i].flags), sizeof(uint8_t));
				offset += sizeof(uint8_t);
				memcpy(temp->data + offset, &(p_tuc_table->tucs[i].count), sizeof(uint32_t));
				offset += sizeof(uint32_t);
				memcpy(temp->data + offset, &(p_tuc_table->tucs[i].max_count), sizeof(uint32_t));
				offset += sizeof(uint32_t);
			}
			break;
		case EM_TYPE_ESI_ITEM_SHARED_KEY:
		case EM_TYPE_ESI_ITEM_SHARED_ESI_COUNTER:
		case EM_TYPE_ESI_ITEM_TOKEN_ID:
		case EM_TYPE_ESI_ITEM_LATEST_TOKEN_ID:
		case EM_TYPE_ESI_ITEM_LATEST_FAC_TOKEN_ID:
		case EM_TYPE_ESI_ITEM_DID:
		case EM_TYPE_ESI_ITEM_RECOVERY_COUNTER:
		case EM_TYPE_ESI_ITEM_IIN:
		case EM_TYPE_ESI_ITEM_PRIORITY_TIME:
			break;
		default:
			LOGE("Unknown Item type(0x%02x)\n", type);
			ret = EM_ERR_EM_ESI_UPDATE_ITEM_UNKNOWN_TYPE2;
			goto out;
		}

		if (type != EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY && type != EM_TYPE_ESI_ITEM_TUC_TABLES)
			memcpy(temp->data, data, temp->elem.len);

		meta.size_of_items = item_size;
		ret = em_esi_update(version, esi, &meta, items, key, key_len);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to update ESI(0x%08x)\n", ret);
			goto out;
		}
	} else {
		p = (em_esi_item *)em_calloc((meta.num_of_items + 1) * sizeof(em_esi_item), 1);
		if (p == NULL) {
			LOGE("Failed to allocate memory\n");
			ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_P;
			goto out;
		}

		for (i = 0; i < meta.num_of_items; i++) {
			p[i].elem.type = items[i].elem.type;
			p[i].elem.len = items[i].elem.len;

			p[i].data = (uint8_t *)em_calloc(p[i].elem.len, 1);
			if (p[i].data == NULL) {
				if (p[i].elem.len == 0)
					continue;
				LOGE("Failed to allocate memory\n");
				ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_DATA_BUFFER;
				goto out;
			}
			memcpy(p[i].data, items[i].data, p[i].elem.len);
		}
		p[meta.num_of_items].elem.type = type;

		switch (type) {
		case EM_TYPE_ESI_ITEM_INIT_STATE:
		case EM_TYPE_ESI_ITEM_TOKEN_STATE:
			p[meta.num_of_items].elem.len = strlen((char *)data);
			break;
		case EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY:
			p_tuc_table_v20 = (tuc_table_v20 *)data;
			p[meta.num_of_items].elem.len = (p_tuc_table_v20->num_of_tuc * sizeof(tuc_v20)) + 2;
			break;
		case EM_TYPE_ESI_ITEM_TUC_TABLES:
			p_tuc_table = (tuc_table *)data;
			p[meta.num_of_items].elem.len = (p_tuc_table->num_of_tuc * sizeof(tuc)) + 2;
			break;
		case EM_TYPE_ESI_ITEM_SHARED_KEY:
			p[meta.num_of_items].elem.len = 32;
			break;
		case EM_TYPE_ESI_ITEM_SHARED_ESI_COUNTER:
			/* case ESI_ITEM_SHARED_ITL_COUNTER: */
			p[meta.num_of_items].elem.len = sizeof(uint32_t);
			break;
		case EM_TYPE_ESI_ITEM_TOKEN_ID:
		case EM_TYPE_ESI_ITEM_LATEST_TOKEN_ID:
		case EM_TYPE_ESI_ITEM_LATEST_FAC_TOKEN_ID:
			p[meta.num_of_items].elem.len = EM_LEN_TOKEN_ID;
			break;
		case EM_TYPE_ESI_ITEM_DID:
			p[meta.num_of_items].elem.len = EM_LEN_DID;
			break;
		case EM_TYPE_ESI_ITEM_RECOVERY_COUNTER:
			p[meta.num_of_items].elem.len = sizeof(uint32_t);
			break;
		case EM_TYPE_ESI_ITEM_IIN:
			p[meta.num_of_items].elem.len = EM_LEN_IIN;
			break;
		case EM_TYPE_ESI_ITEM_PRIORITY_TIME:
			p[meta.num_of_items].elem.len = EM_LEN_PRIORITY_TIME;
			break;
		default:
			LOGE("Unknown Item type(%02x)\n", type);
			ret = EM_ERR_EM_ESI_UPDATE_ITEM_UNKNOWN_TYPE2;
			goto out;
		}

		p[meta.num_of_items].data = (uint8_t *)em_calloc(p[meta.num_of_items].elem.len, 1);
		if (p[meta.num_of_items].data == NULL) {
			LOGE("Failed to allocate memory\n");
			ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_DATA_BUF_2;
			goto out;
		}

		if (type == EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY) {
			memcpy(p[meta.num_of_items].data, &(p_tuc_table_v20->num_of_tuc), sizeof(uint16_t));
			offset = sizeof(uint16_t);
			for (i = 0; i < p_tuc_table_v20->num_of_tuc; i++) {
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table_v20->tucs[i].mode_index,
				       sizeof(uint16_t));
				offset += sizeof(uint16_t);
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table_v20->tucs[i].flags,
				       sizeof(uint8_t));
				offset += sizeof(uint8_t);
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table_v20->tucs[i].count,
				       sizeof(uint32_t));
				offset += sizeof(uint32_t);
			}
		} else if (type == EM_TYPE_ESI_ITEM_TUC_TABLES) {
			memcpy(p[meta.num_of_items].data + offset, &(p_tuc_table->num_of_tuc), sizeof(uint16_t));
			offset = sizeof(uint16_t);
			for (i = 0; i < p_tuc_table->num_of_tuc; i++) {
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table->tucs[i].mode_index,
				       sizeof(uint16_t));
				offset += sizeof(uint16_t);
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table->tucs[i].flags,
				       sizeof(uint8_t));
				offset += sizeof(uint8_t);
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table->tucs[i].count,
				       sizeof(uint32_t));
				offset += sizeof(uint32_t);
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table->tucs[i].max_count,
				       sizeof(uint32_t));
				offset += sizeof(uint32_t);
			}
		} else {
			memcpy(p[meta.num_of_items].data, data, p[meta.num_of_items].elem.len);
		}

		item_size += sizeof(em_default_element) + p[meta.num_of_items].elem.len;
		meta.size_of_items = item_size;
		meta.num_of_items += 1;
		ret = em_esi_update(version, esi, &meta, p, key, key_len);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to update ESI(%08x)\n", ret);
			goto out;
		}
	}

	ret = EM_SUCCESS;
out:
	if (p != NULL)
		em_esi_item_free(items, meta.num_of_items - 1);
	else
		em_esi_item_free(items, meta.num_of_items);

	em_esi_item_free(p, meta.num_of_items);

	return ret;
}

int em_esi_remove_item(const uint32_t version, uint8_t *esi, const uint32_t type, const uint8_t *key,
		       const uint32_t key_len)
{
	int ret, i = 0;

	em_esi_meta meta = {0,};
	em_esi_item *items = NULL, *new_items = NULL;

	uint32_t item_size = 0, origin_num_of_items = 0, offset = 0;
	uint32_t exist_flags = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_REMOVE_ITEM, esi);

	memcpy(&meta, esi + EM_LEN_ESI_DIGEST, sizeof(em_esi_meta));

	origin_num_of_items = meta.num_of_items;

	if (meta.num_of_items != 0) {
		items = (em_esi_item *)em_calloc(sizeof(em_esi_item), meta.num_of_items);
		if (items == NULL) {
			LOGE("Failed to allocate item memory\n");
			ret = EM_ERR_EM_ESI_REMOVE_ITEM_ALLOC_ITEM_BUFFER;
			goto out;
		}

		ret = em_esi_get_all_item(esi, items, meta.num_of_items);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get item(0x%08x)\n", ret);
			goto out;
		}

		for (i = 0; i < meta.num_of_items; i++) {
			if (type == items[i].elem.type) {
				exist_flags = 1;
				break;
			}
		}

		if (exist_flags == 0) {
			LOGE("Not exists requested item type(0x%04x)\n", type);
			ret = EM_ERR_EM_ESI_REMOVE_ITEM_NOT_EXISTS_TYPE;
			goto out;
		}
	} else {
		LOGE("No item in ESI(%u)\n", meta.num_of_items);
		ret = EM_ERR_EM_ESI_REMOVE_ITEM_NO_ITEM;
		goto out;
	}

	if (meta.num_of_items > 1) {
		new_items = (em_esi_item *)em_calloc(sizeof(em_esi_item), meta.num_of_items - 1);
		if (new_items == NULL) {
			LOGE("Failed to allocate new items buffer\n");
			ret = EM_ERR_EM_ESI_REMOVE_ITEM_ALLOC_NEW_BUFFER;
			goto out;
		}

		for (i = 0; i < meta.num_of_items; i++) {
			if (items[i].elem.type == type) // Ignore requsted type for remove
				continue;

			new_items[offset].elem.type = items[i].elem.type;
			new_items[offset].elem.len = items[i].elem.len;
			new_items[offset].data = (uint8_t *)em_calloc(items[i].elem.len, 1);
			if (new_items[offset].data == NULL) {
				LOGE("Failed to allocate memory data\n");
				ret = EM_ERR_EM_ESI_REMOVE_ITEM_ALLOC_DATA_BUFFER;
				goto out;
			}

			memcpy(new_items[offset].data, items[i].data, items[i].elem.len);

			item_size += items[i].elem.len + sizeof(em_default_element);
			offset += 1;
		}
	}

	meta.size_of_items = item_size;
	meta.num_of_items -= 1;

	memset(esi, 0, EM_LEN_ESI);
	ret = em_esi_update(version, esi, &meta, new_items, key, EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to update esi(0x%08x)\n", ret);
		goto out;
	}

	ret = EM_SUCCESS;
out:
	em_esi_item_free(items, origin_num_of_items);

	em_esi_item_free(new_items, offset);

	return ret;
}

int em_esi_get_item(const uint8_t *esi, const uint32_t type, uint8_t *buf, const uint32_t len_buf)
{
	int ret, i = 0;

	em_esi_meta meta = {0,};
	em_esi_item *items = NULL;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_GET_ITEM, esi, buf);

	memcpy(&meta, esi + EM_LEN_ESI_DIGEST, sizeof(em_esi_meta));
	// TODo CHECK MAGIC

	if (meta.num_of_items == 0) {
		LOGE("Type(%u) isn't exists in ESI\n", type);
		ret = EM_ERR_EM_ESI_GET_ITEM_ITEM_IS_NOT_EXISTS;
		goto out;
	}

	items = (em_esi_item *)em_calloc(meta.num_of_items, sizeof(em_esi_item));
	if (items == NULL) {
		LOGE("Failed to allocate item memory\n");
		ret = EM_ERR_EM_ESI_GET_ITEM_ALLOC_ITEM_BUFFER;
		goto out;
	}

	ret = em_esi_get_all_item(esi, items, meta.num_of_items);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get items(0x%08x)\n", ret);
		goto out;
	}

	for (i = 0; i < meta.num_of_items; i++){
		if (items[i].elem.type == type) {
			if (items[i].elem.len > len_buf) {
				LOGE("buf size isn't enough(%u/%u)\n", items[i].elem.len, len_buf);
				ret = EM_ERR_EM_ESI_GET_ITEM_BUFFER_NOT_ENOUGH;
				goto out;
			}
			memcpy(buf, items[i].data, items[i].elem.len);
			ret = EM_SUCCESS;
			goto out;
		}
	}

	ret = EM_ERR_EM_ESI_GET_ITEM_ITEM_IS_NOT_EXISTS;
out:
	em_esi_item_free(items, meta.num_of_items);
	return ret;
}

int em_get_tuc_table(uint8_t *esi, void *buf, uint32_t len_buf)
{
	int ret;
	em_esi_meta meta = {0,};
	uint32_t type = 0;
	uint32_t len_table_max = 0;

	memcpy(&meta, esi + EM_LEN_ESI_DIGEST, sizeof(em_esi_meta));

	if (meta.version == 20) {
		type = EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY;
		len_table_max = sizeof(tuc_table_v20);
	} else if (meta.version == 30) {
		type = EM_TYPE_ESI_ITEM_TUC_TABLES;
		len_table_max = sizeof(tuc_table);
	} else {
		LOGE("Unknown version(%u)\n", meta.version);
		ret = EM_ERR_EM_ESI_GET_TUC_TABLE_UNKNOWN_VERSION;
		goto out;
	}

	if (len_buf < len_table_max) {
		LOGE("buffer isn't enough(%u/%u)\n", len_buf, len_table_max);
		ret = EM_ERR_EM_ESI_GET_TUC_TABLE_NOT_ENOUGH;
		goto out;
	}

	ret = em_esi_get_item(esi, type, buf, len_buf);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get item(0x%08x)\n", ret);
		goto out;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

int em_esi_check_validation(uint8_t *local_esi)
{
	int ret;
	em_esi_meta meta = {0,};

	// TODO ESI 3.0 Validation

	if (local_esi == NULL) {
		// READ ESI on RPMB
	}

	// HASH CHECK

	ret = EM_SUCCESS;
out:
	return ret;
}

int em_esi_check_validation_v20(const uint8_t *esi, uint32_t rpmb_provision)
{
	int ret;

	em_core_v20 core = {0,};
	em_esi_meta meta = {0,};

	uint32_t size = 0;
	uint8_t hmac[EM_LEN_ESI_DIGEST] = {0,};
	uint8_t key[EM_LEN_KEY_CORE_V20] = {0,};
	uint8_t init_state[EM_LEN_ESI_INIT_STATE] = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_CHECK_VALIDATION_V20, esi);

	memcpy(&meta, esi + EM_LEN_ESI_DIGEST, sizeof(em_esi_meta));
	if (memcmp(meta.magic, EM_MAGIC_ESI, strlen(EM_MAGIC_ESI)) != 0) {
		LOGE("esi meta isn't normal(%02x/%02x/%02x)\n", meta.magic[0], meta.magic[1],
		     meta.magic[2]);
		ret = EM_ERR_EM_ESI_CHECK_VALIDATION_V20_ESI_MAGIC;
		goto out;
	}

	size = EM_LEN_ESI_DIGEST + sizeof(em_esi_meta) + meta.size_of_items;
	if (size > EM_LEN_ESI) {
		LOGE("meta data isn't normal(%u/%u)\n", size, EM_LEN_ESI);
		ret = EM_ERR_EM_ESI_CHECK_VALIDATION_V20_META;
		goto out;
	}

	ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_INIT_STATE, init_state, EM_LEN_ESI_INIT_STATE);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get init state from esi(0x%08x)\n", ret);
		goto out;
	}

	ret = em_esi_check_init_state(init_state);
	if ((uint32_t)ret == EM_ERR_EM_ESI_CHECK_INIT_STATE_UNKNOWN) {
		LOGE("init state is unknown\n");
		goto out;
	}

	if (rpmb_provision && ret == EM_TYPE_INIT_STATE_COMPLETED) {
		ret = em_read_core((uint8_t *)&core, sizeof(em_core_v20));
		if (ret != EM_SUCCESS) {
			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) {
			LOGE("core mg isn't normal(%02x/%02x/%02x/%02x)\n", core.magic[0],
			     core.magic[1], core.magic[2], core.magic[3]);
			// If it is too long, qc bootloader wiil be panic
			ret = EM_ERR_EM_ESI_CHECK_VALIDATION_V20_CORE_MG;
			goto out;
		}

		memcpy(key, core.key, EM_LEN_KEY_CORE_V20);
	} else {
		ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_SHARED_KEY, 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_esi_get_item(esi, EM_TYPE_ESI_ITEM_SHARED_ESI_COUNTER, (uint8_t *)&core.esi_ctr,
				      sizeof(uint32_t));
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get count from esi(0x%08x)\n", ret);
			goto out;
			}*/
	}

	ret = em_crypto_hmac(hmac, esi + EM_LEN_ESI_DIGEST, sizeof(em_esi_meta) + meta.size_of_items, key,
			     EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to make hamc(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp(esi, hmac, EM_LEN_ESI_DIGEST) != 0) {
		LOGE("Failed to validate ESI\n");
		ret = EM_ERR_EM_ESI_CHECK_VALIDATION_V20_HMAC;
		goto out;
	}

	if (core.esi_ctr > meta.write_counter) {
		LOGE("Failed to check count(%u/%u)\n", core.esi_ctr, meta.write_counter);
		ret = EM_ERR_EM_ESI_CHECK_VALIDATION_V20_COUNTER;
		goto out;
	}

	ret = EM_SUCCESS;
out:
	memset(&core, 0, sizeof(em_core_v20));
	memset(key, 0, EM_LEN_KEY_CORE_V20);

	return ret;
}

int em_esi_recovery(em_context *ctx)
{
	int ret, i = 0;

	uint8_t *digest = NULL, *item_pointer = NULL, init_flag = 0;
	uint16_t version_itl = 0, len_internal_itl = 0, lty_type = 0;
	uint32_t esi_counter = 0, recovery_counter = 0, server_type = 0;
	uint32_t offset_digest = 0, offset_packet = 0, offset_itl = 0, offset_item = 0;

	em_core_v20 core = {0,};
	em_esi_meta meta = {0,};

	uint8_t token_id[EM_LEN_TOKEN_ID] = {0,}, token_id2[EM_LEN_TOKEN_ID] = {0,};
	uint8_t latest_token_id[EM_LEN_TOKEN_ID] = {0,};
	uint8_t init_state_v20[20] = {0,};

	uint8_t *itl = ctx->recovery_data;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_RECOVERY, ctx);

	digest = (uint8_t *)em_calloc(10256, sizeof(uint8_t));
	if (digest == NULL) {
		LOGE("Failed to allocate digest\n");
		ret = EM_ERR_EM_ESI_RECOVERY_ALLOC_DIGEST;
		goto out;
	}

	memcpy(digest + offset_digest, ctx->did, EM_LEN_DID);
	offset_digest += EM_LEN_DID;

	memcpy(digest + offset_digest, ctx->keep.nonce, EM_LEN_NONCE);
	offset_digest += EM_LEN_NONCE;

	if (ctx->is_provision) {
		if (memcmp(ctx->did, "20", 2) == 0) {
			ctx->esi_version = 20;

			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)
				init_flag = 1;
			else
				esi_counter = core.esi_ctr;
		} else if (memcmp(ctx->did, "30", 2) == 0) {
			// TODO 3.0 read from rpmb
			ctx->esi_version = 30;
		} else {
			LOGE("Unknown em version(%02x/%02x)\n", ctx->did[0], ctx->did[1]);
			ret = EM_ERR_EM_ESI_RECOVERY_DID_VERSION;
			goto out;
		}
	}

	ret = em_esi_initialize(ctx->esi_version, ctx->esi, NULL, esi_counter);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to initialize esi(0x%08x)\n", ret);
		goto out;
	}

	memcpy(&meta, ctx->esi + EM_LEN_ESI_DIGEST, sizeof(em_esi_meta));

	if (memcmp(itl, EM_MAGIC_PACKET_ENG, strlen(EM_MAGIC_PACKET_ENG)) != 0) {
		LOGE("Unknown packet matic(%02x/%02x/%02x)\n", itl[0], itl[1], itl[2]);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_PACKET_MAGIC;
		goto out;
	}
	offset_packet += strlen(EM_MAGIC_PACKET_ENG);

	// IRS Chkeck
	if (memcmp(itl + offset_packet, EM_MAGIC_IRS, strlen(EM_MAGIC_IRS)) != 0) {
		LOGE("Unknown packet type(%02x/%02x/%02x)\n", itl[offset_packet], itl[offset_packet + 1],
		     itl[offset_packet + 2]);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_PACKET_TYPE;
		goto out;
	}
	offset_packet += strlen(EM_MAGIC_IRS);

	if (memcmp(itl + offset_packet, EM_MAGIC_PACKET_VERSION, strlen(EM_MAGIC_PACKET_VERSION)) != 0) {
		LOGE("Unknown packet version(%02x/%02x/%02x/%02x)\n", itl[offset_packet], itl[offset_packet + 1],
		     itl[offset_packet + 2], itl[offset_packet + 3]);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_PACKET_VERSION;
		goto out;
	}
	offset_packet += strlen(EM_MAGIC_PACKET_VERSION);

	offset_itl = offset_packet;
	if (memcmp(itl + offset_packet, EM_MAGIC_ITL, strlen(EM_MAGIC_ITL)) != 0) {
		LOGE("Unknown ITL magic(%02x/%02x/%02x)\n", itl[offset_packet], itl[offset_packet + 1],
		     itl[offset_packet + 1]);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_MAGIC;
		goto out;
	}
	offset_packet += strlen(EM_MAGIC_ITL);

	memcpy(&version_itl, itl + offset_packet, sizeof(uint16_t));
	if (version_itl != EM_VERSION_ITL) {
		LOGE("Unknown ITL version(0x%04x)\n", version_itl);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_VERSION;
		goto out;
	}
	offset_packet += sizeof(uint16_t);

	memcpy(&len_internal_itl, itl + offset_packet, sizeof(uint16_t));
	offset_packet += sizeof(uint16_t) + 9; /* 9 byte is reserved */
	item_pointer = itl + offset_packet;

	if (ctx->cmd == EM_CMD_RECOVERY_ESS_V1 && len_internal_itl == 0) /* FOR ESS ITL SIZE */
		len_internal_itl++;

	offset_packet += len_internal_itl * EM_LEN_TOKEN_ID;

	meta.num_of_items = len_internal_itl;
	meta.size_of_items = len_internal_itl * EM_LEN_TOKEN_ID;

	if (meta.size_of_items > EM_LEN_RECOVERY_DATA) {
		LOGE("itl item size isn't normal(%u/%u)\n", meta.size_of_items,
		     (uint32_t)EM_LEN_RECOVERY_DATA);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_MAX_SIZE;
		goto out;
	}

	if (meta.num_of_items > EM_LEN_RECOVERY_NUM_OF_TOKEN_ID) {
		LOGE("itl num data isn't normal(%u/%u)\n", meta.num_of_items,
		     (uint32_t)EM_LEN_RECOVERY_NUM_OF_TOKEN_ID);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_MAX_NUM;
		goto out;
	}

	memcpy(digest + offset_digest, itl + offset_itl, EM_LEN_TOKEN_ID + meta.size_of_items);
	offset_digest += EM_LEN_TOKEN_ID + meta.size_of_items;

	ret = em_crypto_verify_recovery_data(itl, offset_packet, digest, offset_digest, &server_type);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify recovery data(0x%08x)\n", ret);
		goto out;
	}

	offset_item = 0;
	for (i = 0; i < meta.num_of_items; i++){
		if (offset_item >= EM_LEN_RECOVERY_DATA) {
			LOGE("offset isn't normal(%u/%u)\n", offset_item,
			     (uint32_t)EM_LEN_RECOVERY_DATA);
			ret = EM_ERR_EM_ESI_RECOVERY_ITL_MAX_SIZE_2;
			goto out;
		}
		memcpy(token_id, item_pointer + offset_item, 5);
		memcpy(token_id + 5, item_pointer + offset_item + 10, 6);

		if (memcmp(token_id, token_id2, 11) > 0) {
			memcpy(token_id2, token_id, 11);
			memcpy(latest_token_id, item_pointer + offset_item, EM_LEN_TOKEN_ID);
		}
		offset_item += EM_LEN_TOKEN_ID;
	}

	lty_type = (server_type == EM_FLAG_EM_TOKEN_FROM_FAC) ? EM_TYPE_ESI_ITEM_LATEST_FAC_TOKEN_ID
							      : EM_TYPE_ESI_ITEM_TOKEN_ID;
	ret = em_esi_update_item(ctx->esi_version, ctx->esi, lty_type, latest_token_id, NULL, EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to update LTI(%04x) to ESI without key(%08x)\n", lty_type, ret);
		goto out;
	}

	if (init_flag == 0) {
		memcpy(init_state_v20, EM_MAGIC_ESI_V20_COMPLETED, strlen(EM_MAGIC_ESI_V20_COMPLETED));

		recovery_counter = core.recovery_ctr;
		core.recovery_ctr += 1;

		ret = em_write_core((uint8_t *)&core, sizeof(em_core_v20));
		if (ret != EM_SUCCESS) {
			LOGE("Failed to write core(0x%08x)\n", ret);
			goto out;
		}

		ret = em_esi_update_item(ctx->esi_version, ctx->esi, EM_TYPE_ESI_ITEM_RECOVERY_COUNTER,
					 (uint8_t *)&recovery_counter, core.key, EM_LEN_KEY_CORE_V20);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to update recovery counter(0x%08x)\n", ret);
			goto out;
		}
	} else if (init_flag == 1) {
		// Sharing
		if (em_get_random(core.key, EM_LEN_KEY_CORE_V20) != EM_LEN_KEY_CORE_V20) {
			LOGE("Failed to make key\n");
			ret = EM_ERR_EM_ESI_RECOVERY_MAKE_KEY;
			goto out;
		}

		ret = em_esi_update_item(ctx->esi_version, ctx->esi, EM_TYPE_ESI_ITEM_SHARED_KEY, core.key, core.key,
					 EM_LEN_KEY_CORE_V20);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to update item(0x%08x)\n", ret);
			goto out;
		}
		memcpy(init_state_v20, EM_MAGIC_ESI_V20_SHARING, strlen(EM_MAGIC_ESI_V20_SHARING));
	}

	ret = em_esi_update_item(ctx->esi_version, ctx->esi, EM_TYPE_ESI_ITEM_INIT_STATE, init_state_v20, core.key,
				 EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to add init_state(%08x)\n", ret);
		goto out;
	}

	ret = em_esi_update_item(ctx->esi_version, ctx->esi, EM_TYPE_ESI_ITEM_DID, ctx->did, core.key,
				 EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to add DID(%08x)\n", ret);
		goto out;
	}

	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_ESI | EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;

	ret = EM_SUCCESS;
out:
	memset(&core, 0, sizeof(em_core_v20));

	if (digest)
		em_free(digest);

	return ret;
}

int em_esi_set_priority_time(em_context *ctx)
{
	int ret;

	int flag = 0, i = 0;
	char esi_priority_time[EM_LEN_PRIORITY_TIME + 1] = {0,};
	char priority_time[EM_LEN_PRIORITY_TIME + 1] = {0,};
	char token_expiry_date[EM_LEN_DATE + 1] = {0,};
	char token_id[EM_LEN_TOKEN_ID + 1] = {0,};
	uint8_t current_count = 1;
	char current_count_string[2 + 1] = {0,};
	char *date = (char *)em_calloc(sizeof(char), EM_LEN_DATE + 1);

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_SET_PRIORITY_TIME, ctx, date);

	memcpy(date, ctx->message + strlen(EM_MAGIC_ESS_PREFIX_HEADER_SET_PRIORITY), EM_LEN_DATE);
	if (em_is_all_zero((unsigned char *)date, EM_LEN_DATE) == EM_SUCCESS) {
		LOGE("Failed to set priority time, server_date is all zero\n");
		ret = EM_ERR_EM_ESI_SET_PRIORITY_TIME_DATE;
		goto make_message;
	}

	for (i = 0; i < EM_LEN_DATE; i++) {
		if ('0' > date[i] || date[i] > '9') {
			LOGE("date isn't normal(%s)\n", date);
			ret = EM_ERR_EM_ESI_SET_PRIORITY_TIME_DATE_FORMAT;
			goto make_message;
		}
	}

	ret = em_token_get_expiry_date(ctx, token_expiry_date);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get expiry date(0x%08x)\n", ret);
		goto make_message;
	}

	if (memcmp(token_expiry_date, date, EM_LEN_DATE) < 0) {
		LOGE("over date(%s/%s)\n", token_expiry_date, date);
		ret = EM_ERR_EM_ESI_SET_PRIORITY_TIME_BEYOND;
		goto make_message;
	}

	ret = em_esi_get_item(ctx->esi, EM_TYPE_ESI_ITEM_TOKEN_ID, (uint8_t *)token_id, EM_LEN_TOKEN_ID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get token id(0x%08x)\n", ret);
		goto make_message;
	}

	ret = em_esi_get_item(ctx->esi, EM_TYPE_ESI_ITEM_PRIORITY_TIME, (uint8_t *)esi_priority_time,
			      EM_LEN_PRIORITY_TIME);
	if (ret != EM_SUCCESS) {
		if ((uint32_t)ret != EM_ERR_EM_ESI_GET_ITEM_ITEM_IS_NOT_EXISTS) {
			LOGE("Failed to get priority_time(0x%08x)\n", ret);
			goto make_message;
		}
		LOGI("Not exists priority time\n");
	} else {
		if (memcmp(token_id, esi_priority_time + EM_LEN_DATE, EM_LEN_TOKEN_ID) == 0) {
			LOGI("Matched TID\n");
			if ((uint8_t)esi_priority_time[EM_LEN_PRIORITY_TIME - 1] >= EM_LEN_PRIORITY_TIME_COUNT) {
				LOGE("Failed to set priority_time, Excceed count(%d)!\n",
				     (uint8_t)esi_priority_time[EM_LEN_PRIORITY_TIME - 1]);
				ret = EM_ERR_EM_ESI_SET_PRIORITY_TIME_SET_COUNT;
				goto make_message;
			}
			current_count = (((uint8_t)esi_priority_time[EM_LEN_PRIORITY_TIME - 1]) + 1);
		}
	}

	memcpy(priority_time, date, EM_LEN_DATE);
	memcpy(priority_time + EM_LEN_DATE, token_id, EM_LEN_TOKEN_ID);
	memcpy(priority_time + EM_LEN_DATE + EM_LEN_TOKEN_ID, &current_count, sizeof(uint8_t));

	ret = em_esi_update_item(ctx->esi_version, ctx->esi, EM_TYPE_ESI_ITEM_PRIORITY_TIME, (uint8_t *)priority_time,
				 ctx->key, EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("%s : Failed to add update item(%08x)\n", __func__, ret);
		goto make_message;
	}

	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_ESI;
	ret = EM_SUCCESS;

make_message:
	ctx->len_message = 0;
	em_ess_update_req_msg(ctx->message, &(ctx->len_message), (uint8_t *)EM_MAGIC_ESS_PREFIX_RETURN_COMMAND,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND), NULL, EM_LEN_MESSAGE);
	em_ess_update_req_msg(ctx->message, &(ctx->len_message), ctx->ess_command_type, 1,
			      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE);
	if (ret != EM_SUCCESS)
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)EM_MAGIC_PRIORITY_NOK, (uint32_t)strlen(EM_MAGIC_PRIORITY_NOK));
	else
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)EM_MAGIC_PRIORITY_OK, (uint32_t)strlen(EM_MAGIC_PRIORITY_OK));

	em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
		      (uint8_t *)EM_MAGIC_PRIORITY_SEPARATOR, (uint32_t)strlen(EM_MAGIC_PRIORITY_SEPARATOR));

	if (ctx->flags[1] & EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE) {
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)EM_MAGIC_PRIORITY_NO_TOKEN, (uint32_t)strlen(EM_MAGIC_PRIORITY_NO_TOKEN));
		ret = EM_SUCCESS;
		goto out;
	}

	if (ret == EM_SUCCESS) {
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message, (uint8_t *)date,
			      (uint32_t)EM_LEN_DATE);
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)EM_MAGIC_PRIORITY_SEPARATOR, (uint32_t)strlen(EM_MAGIC_PRIORITY_SEPARATOR));
		em_itoa_for_mode(current_count, current_count_string);
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)current_count_string, (uint32_t)strlen(current_count_string));
	} else if ((uint32_t)ret == EM_ERR_EM_ESI_SET_PRIORITY_TIME_SET_COUNT) {
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)EM_MAGIC_PRIORITY_EXCEEDED_PR_COUNT,
			      (uint32_t)strlen(EM_MAGIC_PRIORITY_EXCEEDED_PR_COUNT));
	} else if ((uint32_t)ret == EM_ERR_EM_ESI_SET_PRIORITY_TIME_BEYOND) {
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)EM_MAGIC_PRIORITY_BEYOND_GENERAL,
			      (uint32_t)strlen(EM_MAGIC_PRIORITY_BEYOND_GENERAL));
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)EM_MAGIC_PRIORITY_SEPARATOR, (uint32_t)strlen(EM_MAGIC_PRIORITY_SEPARATOR));
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)token_expiry_date, (uint32_t)EM_LEN_DATE);
	} else {
		em_ess_update_req_msg(ctx->message, &(ctx->len_message), (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_ERROR,
				      strlen(EM_MAGIC_ESS_AT_COMMAND_ERROR), (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM,
				      EM_LEN_MESSAGE);
		em_snprintf((char *)(ctx->message + ctx->len_message), 8 + 1, "%08X", ret);
		ctx->len_message += 8;
		LOGE("Some process is wrong(0x%08x)\n", ret);
	}

	ret = EM_SUCCESS;
out:
	if (date)
		em_free(date);

	LOGI("%s : result %d(%s)\n", __func__, ret, ctx->message);
	return ret;
}

int em_esi_get_priority_time(em_context *ctx)
{
	int ret;

	int flag = 0;
	char esi_priority_time[EM_LEN_PRIORITY_TIME + 1] = {0,};
	char token_expiry_date[EM_LEN_DATE + 1] = {0,};
	char real_expiry_date[EM_LEN_DATE + 1] = {0,};
	char token_id[EM_LEN_TOKEN_ID + 1] = {0,};
	uint8_t current_count = 0;
	char current_count_string[2 + 1] = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_GET_PRIORITY_TIME, ctx);

	ret = em_token_get_expiry_date(ctx, token_expiry_date);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get expiry date(0x%08x)\n", ret);
		ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;
		goto make_message;
	}

	memcpy(real_expiry_date, token_expiry_date, EM_LEN_DATE);

	ret = em_esi_get_item(ctx->esi, EM_TYPE_ESI_ITEM_PRIORITY_TIME, (uint8_t *)esi_priority_time,
			      EM_LEN_PRIORITY_TIME);
	if (ret != EM_SUCCESS) {
		if ((uint32_t)ret != EM_ERR_EM_ESI_GET_ITEM_ITEM_IS_NOT_EXISTS) {
			LOGE("Failed to get priority_time(0x%08x)\n", ret);
			goto make_message;
		}
		LOGI("Not exists priority time\n");
		ret = EM_SUCCESS;
		goto make_message;
	}

	ret = em_esi_get_item(ctx->esi, EM_TYPE_ESI_ITEM_TOKEN_ID, (uint8_t *)token_id, EM_LEN_TOKEN);
	if (ret != EM_SUCCESS) {
		if ((uint32_t)ret != EM_ERR_EM_ESI_GET_ITEM_ITEM_IS_NOT_EXISTS) {
			LOGE("Failed to get token id(0x%08x)\n", ret);
			goto make_message;
		}
	}

	if (memcmp(esi_priority_time + EM_LEN_DATE, token_id, EM_LEN_TOKEN_ID) == 0) {
		if (memcmp(token_expiry_date, esi_priority_time, EM_LEN_DATE) > 0)
			memcpy(real_expiry_date, esi_priority_time, EM_LEN_DATE);

		current_count = (((uint8_t)esi_priority_time[EM_LEN_PRIORITY_TIME - 1]));
		flag |= EM_PRIORITY_FLAG_MATCHED_TID;
	} else {
		LOGE("Not Matched token id\n");
		print_msg_hex((char *)"token_id", (uint8_t *)token_id, EM_LEN_TOKEN_ID);
		print_msg_hex((char *)"esi_token_id", (uint8_t *)(esi_priority_time + EM_LEN_DATE),
			      EM_LEN_TOKEN_ID);
	}

	ret = EM_SUCCESS;

make_message:
	ctx->len_message = 0;
	em_ess_update_req_msg(ctx->message, &(ctx->len_message), (uint8_t *)EM_MAGIC_ESS_PREFIX_RETURN_COMMAND,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND), NULL, EM_LEN_MESSAGE);
	em_ess_update_req_msg(ctx->message, &(ctx->len_message), ctx->ess_command_type, 1,
			      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE);

	if (ret == EM_SUCCESS) {
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)EM_MAGIC_PRIORITY_OK, (uint32_t)strlen(EM_MAGIC_PRIORITY_OK));
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)EM_MAGIC_PRIORITY_SEPARATOR, (uint32_t)strlen(EM_MAGIC_PRIORITY_SEPARATOR));

		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)real_expiry_date, (uint32_t)EM_LEN_DATE);
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)EM_MAGIC_PRIORITY_SEPARATOR, (uint32_t)strlen(EM_MAGIC_PRIORITY_SEPARATOR));

		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)token_expiry_date, (uint32_t)EM_LEN_DATE);
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)EM_MAGIC_PRIORITY_SEPARATOR, (uint32_t)strlen(EM_MAGIC_PRIORITY_SEPARATOR));

		if (flag & EM_PRIORITY_FLAG_MATCHED_TID)
			em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
				      (uint8_t *)esi_priority_time, (uint32_t)EM_LEN_DATE);
		else
			em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
				      (uint8_t *)EM_MAGIC_PRIORITY_NONE, (uint32_t)strlen(EM_MAGIC_PRIORITY_NONE));

		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)EM_MAGIC_PRIORITY_SEPARATOR, (uint32_t)strlen(EM_MAGIC_PRIORITY_SEPARATOR));

		em_itoa_for_mode(current_count, current_count_string);
		em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
			      (uint8_t *)current_count_string, (uint32_t)strlen(current_count_string));
	} else {
		if (ctx->flags[1] & EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE) {
			em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
				      (uint8_t *)EM_MAGIC_PRIORITY_NOK, (uint32_t)strlen(EM_MAGIC_PRIORITY_NOK));

			em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
				      (uint8_t *)EM_MAGIC_PRIORITY_SEPARATOR,
				      (uint32_t)strlen(EM_MAGIC_PRIORITY_SEPARATOR));

			em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
				      (uint8_t *)EM_MAGIC_PRIORITY_NO_TOKEN,
				      (uint32_t)strlen(EM_MAGIC_PRIORITY_NO_TOKEN));
		} else {
			em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
				      (uint8_t *)EM_MAGIC_PRIORITY_NOK, (uint32_t)strlen(EM_MAGIC_PRIORITY_NOK));

			em_add_string((uint8_t *)ctx->message, EM_LEN_MESSAGE, (uint32_t *)&ctx->len_message,
				      (uint8_t *)EM_MAGIC_PRIORITY_SEPARATOR,
				      (uint32_t)strlen(EM_MAGIC_PRIORITY_SEPARATOR));

			em_ess_update_req_msg(ctx->message, &(ctx->len_message),
					      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_ERROR,
					      strlen(EM_MAGIC_ESS_AT_COMMAND_ERROR),
					      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE);
			em_snprintf((char *)(ctx->message + ctx->len_message), 8 + 1, "%08X", ret);
			ctx->len_message += 8;
			LOGE("Some process is wrong(0x%08x)\n", ret);
		}
	}

	ret = EM_SUCCESS;
out:
	LOGI("result %s\n", ctx->message);
	return ret;
}
