#include "process_cmd_TA.h"

void create_artificial_rpmb_block(uint8_t *service_name, uint32_t policy);
void clear_rpmb();

/**
 * @brief
 * process_cmd
 * Process command
 *
 * @param[in] commandId - command id
 * @param[in] sendmsg   - tci request message
 * @param[in] respmsg   - tci response message
 * @param[in] ta_name   - tci response message
 *
 * @return HDM status code
 */
hdm_return_code_t process_cmd_from_ta(uint32_t commandId, hdmMessage_t *sendmsg, hdmMessage_t *respmsg, TEEC_UUID *ta_name)
{
        hdm_return_code_t ret = HDM_TATA_SUCCESS;
        uint32_t merged_policy;
        tz_hdm_rpmb_t rpmb_data;

        HDM_LOG("process_cmd_from_ta\n");

        // Device integrity check   
        device_status = hdm_ICCC_check();
        if (device_status != HDM_DEVICE_OK) {
                HDM_LOG("hdm_ICCC_check FAIL");
        }   

        /*  Process command message */
        switch (commandId) {
                case KG_UNBLOCK_CMD:
                        HDM_LOG_DEBUG("Case: KG_UNBLOCK_CMD");
                        if (check_permission(ta_name)) {
                                ret = merge_rpmb_policies_service_name((uint8_t*) KG_SERVICE_NAME, &merged_policy);
                                HDM_LOG_DEBUG("Applying policy 0x%02x\n", merged_policy);
                                ret = hypervisor_communication(merged_policy, HDM_HYPERVISOR_CMD_WRITE);
                                
                        } else {
                                HDM_LOG("command deny\n");
                                HDM_LOG_DEBUG("TA %02x%02x%02x%02x%02x%02x%02x%02x doesn't have permission to execute command %x",
                                ta_name->clockSeqAndNode[0],
                                ta_name->clockSeqAndNode[1],
                                ta_name->clockSeqAndNode[2],
                                ta_name->clockSeqAndNode[3],
                                ta_name->clockSeqAndNode[4],
                                ta_name->clockSeqAndNode[5],
                                ta_name->clockSeqAndNode[6],
                                ta_name->clockSeqAndNode[7], KG_UNBLOCK_CMD);
                                ret = HDM_COMMAND_DENY;
                        }
                        break;
                case KG_BLOCK_CMD:
                        HDM_LOG_DEBUG("Case: KG_BLOCK_CMD");

                        if (check_permission(ta_name)) {
                                //TODO: Check service name (dynamic)
                                //TODO: Check return value
                                ret = merge_rpmb_policies(&merged_policy);
                                HDM_LOG_DEBUG("Applying policy 0x%02x\n", merged_policy);
                                ret = hypervisor_communication(merged_policy, HDM_HYPERVISOR_CMD_WRITE);

                        } else {
                                HDM_LOG("command deny\n");
                                HDM_LOG_DEBUG("TA %02x%02x%02x%02x%02x%02x%02x%02x doesn't have permission to execute command %x",
                                ta_name->clockSeqAndNode[0],
                                ta_name->clockSeqAndNode[1],
                                ta_name->clockSeqAndNode[2],
                                ta_name->clockSeqAndNode[3],
                                ta_name->clockSeqAndNode[4],
                                ta_name->clockSeqAndNode[5],
                                ta_name->clockSeqAndNode[6],
                                ta_name->clockSeqAndNode[7], KG_BLOCK_CMD);
                                ret = HDM_COMMAND_DENY;
                        }
                        break;
                case KG_GET_STATUS_CMD:
                        HDM_LOG_DEBUG("Case: KG_GET_STATUS_CMD");
                        if (check_permission(ta_name)) {
                                uint32_t device_block = HDM_TATA_ERROR;
                                uint32_t compromise_block = HDM_TATA_ERROR;
                                
                                ret = kg_get_status(&device_block, &compromise_block);
                                switch(ret) {
                                        case HDM_STATUS_SUCCESS:
                                                HDM_LOG_DEBUG("kg_get_status SUCCESS ret = %d", ret);
                                                HDM_LOG_DEBUG("kg_status->device_block = 0x%08X", device_block);
                                                HDM_LOG_DEBUG("kg_status->compromise_block = 0x%08X", compromise_block);
                                                ret = HDM_TATA_SUCCESS;
                                                break;
                                        case HDM_RPMB_SERVICE_NAME_MISMATCH:
                                        case HDM_RPMB_MAGIC_FAIL:
                                                HDM_LOG("kg_get_status not activated");
                                                HDM_LOG_DEBUG("kg_get_status FAIL ret = %d", ret);
                                                ret = HDM_KG_NOT_ACTIVATED;
                                                break;
                                        default:
                                                HDM_LOG("kg_get_status FAIL");
                                                HDM_LOG_DEBUG("kg_get_status FAIL ret = %d", ret);
                                                ret = HDM_TATA_ERROR;
                                                break;
                                }

                                respmsg->tata_message.kg_status.device_block = device_block;
                                respmsg->tata_message.kg_status.compromise_block = compromise_block;

                        } else {
                                HDM_LOG("command deny\n");
                                HDM_LOG_DEBUG("TA %02x%02x%02x%02x%02x%02x%02x%02x doesn't have permission to execute command %x",
                                ta_name->clockSeqAndNode[0],
                                ta_name->clockSeqAndNode[1],
                                ta_name->clockSeqAndNode[2],
                                ta_name->clockSeqAndNode[3],
                                ta_name->clockSeqAndNode[4],
                                ta_name->clockSeqAndNode[5],
                                ta_name->clockSeqAndNode[6],
                                ta_name->clockSeqAndNode[7], KG_GET_STATUS_CMD);
                                ret = HDM_COMMAND_DENY;
                        }
                        break;
                default:
                        HDM_LOG_DEBUG("Unknown Command");
                        ret = HDM_UNKNOWN_TYPE;
                        break;
        }

        return ret;
}


// ============= BEGIN WORKAROUND =============
void create_artificial_rpmb_block(uint8_t *service_name, uint32_t policy) {
        hdm_return_code_t ret;
        tz_hdm_rpmb_t rpmb_data;
        uint32_t merge;

        HDM_LOG_DEBUG("create_artificial_rpmb_block\n");

        ret = read_policy_rpmb(&rpmb_data);
        // Only treat RPMB fail cases
        if (ret == HDM_RPMB_FAIL) {
                HDM_LOG("RPMB Fail");
                HDM_LOG_DEBUG("RPMB Fail ret = %d", ret);

                goto exit;
        }

        ret = get_curr_service_index(rpmb_data, service_name);
        if (ret == HDM_RPMB_SERVICE_NAME_MISMATCH) {
                HDM_LOG_DEBUG("RPMB No such service name %s, creating @ %d\n", service_name, current_service_index);
                rpmb_data.data[current_service_index].isActivated = 1;
                
                //TODO: Check receiving compromise block
                rpmb_data.data[current_service_index].compromise_block = 0x0;
                
                rpmb_data.data[current_service_index].device_block = policy;
                
                //TODO: Check default policy version
                rpmb_data.data[current_service_index].policy_version = 1;
                
                TEE_MemMove(rpmb_data.data[current_service_index].service_name, service_name, JWS_HEADER_SERVICE_NAME_LEN);
        
        } else if (current_service_index < MAX_SERVICE_NUM) {
                HDM_LOG_DEBUG("RPMB Found service name %s @ %d\n", service_name, current_service_index);
                //TODO: Check receiving compromise block
                //rpmb_data.data[current_service_index].compromise_block = payload.compromise_block;
                
                rpmb_data.data[current_service_index].device_block = policy;
                
                //TODO: Check for update on policy_version
                //rpmb_data.data[current_service_index].policy_version++;

        } else {
                HDM_LOG("RPMB save Fail");
                HDM_LOG_DEBUG("RPMB save Fail ret = %d", ret);

                goto exit;
        }

        ret = hdm_rpmb_write((uint8_t *)&rpmb_data, sizeof(rpmb_data));
        if (ret != HDM_STATUS_SUCCESS)
        {
                HDM_LOG("RPMB write FAIL");
                HDM_LOG_DEBUG("RPMB write FAIL ret = %d", ret);

                goto exit;
        }

exit:
        return;
}

void clear_rpmb() {
        hdm_return_code_t ret;
        tz_hdm_rpmb_t rpmb_data;
        uint32_t merge;

        HDM_LOG_DEBUG("clear_rpmb\n");

        ret = read_policy_rpmb(&rpmb_data);
        // Only treat RPMB fail cases
        if (ret == HDM_RPMB_FAIL) {
                HDM_LOG("RPMB Fail");
                HDM_LOG_DEBUG("RPMB Fail ret = %d", ret);

                goto exit;
        }

        for (int i=1; i<MAX_SERVICE_NUM; i++) {
                TEE_MemFill(&(rpmb_data.data[i]), 0, sizeof(tz_hdm_rpmb_data_t));
        }

        ret = hdm_rpmb_write((uint8_t *)&rpmb_data, sizeof(rpmb_data));
        if (ret != HDM_STATUS_SUCCESS)
        {
                HDM_LOG("RPMB write FAIL");
                HDM_LOG_DEBUG("RPMB write FAIL ret = %d", ret);

                goto exit;
        }

exit:
        return;
}
// ============= END WORKAROUND =============
