#include <tee_internal_api.h>
#include <string.h>
#include "process_requests.h"
#include "tee_macros.h"
#include <tui.h>

/* Orbis model support only one entry field */
#if defined(resolution_360_360)
    #define DEF_ENTRY_FIELD_COUNT   1
    #define ENTRY_FIELD_LENGTH_MAX  4
    #define LABEL_X_OFFSET  0
    #define LABEL_Y_OFFSET  0
#else  //resolution_360_360
    #define DEF_ENTRY_FIELD_COUNT   3
    #define ENTRY_FIELD_LENGTH_MAX  6
    #define LABEL_X_OFFSET  10
    #define LABEL_Y_OFFSET  50
#endif //resolution_360_360

#define MAX_EF_STR_LEN          26

struct label_info {
    int len;
    int width;
    int height;
};

TEE_TUIScreenLabel label;
bool is_tui_started;

TEE_Result process_reg_label(TEE_Param params[4])
{
    TEE_Result res = TEE_SUCCESS;

    struct label_info *info = (struct label_info *)params[0].memref.buffer;
    unsigned char *label_image = NULL;

    TEE_MemFill(&label, 0, sizeof(TEE_TUIScreenLabel));

    label_image = (unsigned char *)TEE_Malloc(info->len, 0);
    CHECK_MALLOC(label_image, res);

    TEE_MemMove(label_image, params[1].memref.buffer, info->len);

    label.image.source = TEE_TUI_REF_SOURCE;
    label.image.ref.image = label_image;
    label.image.ref.imageLength = info->len;

    label.imageXOffset = LABEL_X_OFFSET;
    label.imageYOffset = LABEL_Y_OFFSET;

    label.image.width = info->width;
    label.image.height = info->height;
FUNCTION_EXIT:
    return res;
}

TEE_Result process_start_tui()
{
    TEE_Result res = TEE_SUCCESS;

    TEE_TUIScreenInfo scr_info;
    TEE_TUIScreenConfiguration conf;
    TEE_TUIEntryField *ef = NULL;
    TEE_TUIButtonType key_pressed;

    TEE_MemFill(&conf, 0, sizeof(TEE_TUIScreenConfiguration));

    /*=================================================
     * [01] Get screen info
     *=================================================*/
    res = TEE_TUIGetScreenInfo(TEE_TUI_PORTRAIT,
                               DEF_ENTRY_FIELD_COUNT,
                               &scr_info);
    CHECK_TEE_ERROR(res);

    /*=================================================
     * [02] Init TUI session
     *=================================================*/
    res = TEE_TUIInitSession();
    CHECK_TEE_ERROR(res);
    is_tui_started = true;

    /*=================================================
     * [03] Set default configuration
     *=================================================*/
    conf.screenOrientation = TEE_TUI_PORTRAIT;
    TEE_MemMove(&conf.label, &label, sizeof(TEE_TUIScreenLabel));

    conf.requestedButtons[TEE_TUI_VALIDATE] = true;
    conf.requestedButtons[TEE_TUI_CANCEL] = true;
    conf.requestedButtons[TEE_TUI_CORRECTION] = true;

    /*=================================================
     * [*] Set entry fields
     *=================================================*/
    ef = (TEE_TUIEntryField *)TEE_Malloc((uint32_t)(sizeof(TEE_TUIEntryField) * DEF_ENTRY_FIELD_COUNT), 0);
    CHECK_MALLOC(ef, res);

    TEE_MemFill(ef, 0, sizeof(TEE_TUIEntryField) * DEF_ENTRY_FIELD_COUNT);

    ef[0].type = TEE_TUI_NUMERICAL;
    ef[0].mode = TEE_TUI_CLEAR_MODE;
    ef[0].minExpectedLength = 4;
    ef[0].maxExpectedLength = ENTRY_FIELD_LENGTH_MAX;
    ef[0].buffer = (char *)TEE_Malloc((uint32_t)(sizeof(char) * (ENTRY_FIELD_LENGTH_MAX+1)), 0);
    CHECK_MALLOC(ef[0].buffer, res);
    ef[0].bufferLength = MAX_EF_STR_LEN;
    ef[0].label = TEE_Malloc(strlen("Enter PIN")+1, 0);
    sprintf(ef[0].label, "Enter PIN");

/* Orbis model don't use a second entry field */
#ifndef TUI_MODEL_ORBIS
    ef[1].type = TEE_TUI_ALPHANUMERICAL;
    ef[1].mode = TEE_TUI_CLEAR_MODE;
    ef[1].minExpectedLength = 4;
    ef[1].maxExpectedLength = ENTRY_FIELD_LENGTH_MAX;
    ef[1].buffer = (char *)TEE_Malloc((uint32_t)(sizeof(char) * (ENTRY_FIELD_LENGTH_MAX+1)), 0);
    CHECK_MALLOC(ef[1].buffer, res);
    ef[1].bufferLength = MAX_EF_STR_LEN;
    ef[1].label = TEE_Malloc(strlen("Enter Name")+1, 0);
    sprintf(ef[1].label, "Enter Name");

    ef[2].type = TEE_TUI_ALPHANUMERICAL;
    ef[2].mode = TEE_TUI_HIDDEN_MODE;
    ef[2].minExpectedLength = 4;
    ef[2].maxExpectedLength = ENTRY_FIELD_LENGTH_MAX;
    ef[2].buffer = (char *)TEE_Malloc((uint32_t)(sizeof(char) * (ENTRY_FIELD_LENGTH_MAX+1)), 0);
    CHECK_MALLOC(ef[2].buffer, res);
    ef[2].bufferLength = MAX_EF_STR_LEN;
    ef[2].label = TEE_Malloc(strlen("Enter Password")+1, 0);
    sprintf(ef[2].label, "Enter Password");
#endif  //TUI_MODEL_ORBIS
    /*=================================================
     * [04] Display screen
     *=================================================*/
    res = TEE_TUIDisplayScreen(&conf,
                               false,
                               ef,
                               DEF_ENTRY_FIELD_COUNT,
                               &key_pressed);
    if (res != TEE_SUCCESS) {
        printf("TEE_TUIDisplayScreen res: 0x%08x\n", res);
        if (res == TEE_ERROR_BAD_STATE) {
            printf("TEE_TUIDisplayScreen returned TEE_ERROR_BAD_STATE.\n"
                   "TUI session has been closed automatically because the TUI session\n"
                   "timeout has been reached or a TUI session has been closed\n"
                   "due to an OS specific external event. TEST PASSED.\n");
            res = TEE_SUCCESS;
            is_tui_started = false;
            goto FUNCTION_EXIT;
        }
    }

    res = TEE_TUICloseSession();
    is_tui_started = false;
    printf("TEE_TUICloseSession res: %d\n", res);

    CHECK_TEE_ERROR(res);

FUNCTION_EXIT:
    if (ef != NULL) {
        if (ef[0].label) {
            TEE_Free(ef[0].label);
        }
        if (ef[0].buffer) {
            TEE_Free(ef[0].buffer);
        }
        if (ef[1].label) {
            TEE_Free(ef[1].label);
        }
        if (ef[1].buffer) {
            TEE_Free(ef[1].buffer);
        }
        if (ef[2].label) {
            TEE_Free(ef[2].label);
        }
        if (ef[2].buffer) {
            TEE_Free(ef[2].buffer);
        }
        TEE_Free(ef);
    }

    if (label.image.ref.image != NULL) {
        TEE_Free(label.image.ref.image);
        label.image.ref.image = NULL;
    }

    return res;
}

TEE_Result process_start_tui_land()
{
    TEE_Result res = TEE_SUCCESS;

    TEE_TUIScreenInfo scr_info;
    TEE_TUIScreenConfiguration conf;
    TEE_TUIEntryField *ef = NULL;
    TEE_TUIButtonType key_pressed;

    TEE_MemFill(&conf, 0, sizeof(TEE_TUIScreenConfiguration));

    /*=================================================
     * [01] Get screen info
     *=================================================*/
    res = TEE_TUIGetScreenInfo(TEE_TUI_LANDSCAPE,
                               DEF_ENTRY_FIELD_COUNT,
                               &scr_info);
    CHECK_TEE_ERROR(res);

    /*=================================================
     * [02] Init TUI session
     *=================================================*/
    res = TEE_TUIInitSession();
    CHECK_TEE_ERROR(res);
    is_tui_started = true;

    /*=================================================
     * [03] Set default configuration
     *=================================================*/
    conf.screenOrientation = TEE_TUI_LANDSCAPE;
    TEE_MemMove(&conf.label, &label, sizeof(TEE_TUIScreenLabel));

    conf.requestedButtons[TEE_TUI_VALIDATE] = true;
    conf.requestedButtons[TEE_TUI_CANCEL] = true;
    conf.requestedButtons[TEE_TUI_CORRECTION] = true;

    /*=================================================
     * [*] Set entry fields
     *=================================================*/
    ef = (TEE_TUIEntryField *)TEE_Malloc((uint32_t)(sizeof(TEE_TUIEntryField) * DEF_ENTRY_FIELD_COUNT), 0);
    CHECK_MALLOC(ef, res);

    TEE_MemFill(ef, 0, sizeof(TEE_TUIEntryField) * DEF_ENTRY_FIELD_COUNT);

    ef[0].type = TEE_TUI_NUMERICAL;
    ef[0].mode = TEE_TUI_CLEAR_MODE;
    ef[0].minExpectedLength = 0;
    ef[0].maxExpectedLength = ENTRY_FIELD_LENGTH_MAX;
    ef[0].buffer = (char *)TEE_Malloc((uint32_t)(sizeof(char) * (ENTRY_FIELD_LENGTH_MAX+1)), 0);
    CHECK_MALLOC(ef[0].buffer, res);
    ef[0].bufferLength = MAX_EF_STR_LEN;
    ef[0].label = TEE_Malloc(strlen("Enter PIN")+1, 0);
    sprintf(ef[0].label, "Enter PIN");

    ef[1].type = TEE_TUI_ALPHANUMERICAL;
    ef[1].mode = TEE_TUI_CLEAR_MODE;
    ef[1].minExpectedLength = 0;
    ef[1].maxExpectedLength = ENTRY_FIELD_LENGTH_MAX;
    ef[1].buffer = (char *)TEE_Malloc((uint32_t)(sizeof(char) * (ENTRY_FIELD_LENGTH_MAX+1)), 0);
    CHECK_MALLOC(ef[1].buffer, res);
    ef[1].bufferLength = MAX_EF_STR_LEN;
    ef[1].label = TEE_Malloc(strlen("Enter Name")+1, 0);
    sprintf(ef[1].label, "Enter Name");

    ef[2].type = TEE_TUI_ALPHANUMERICAL;
    ef[2].mode = TEE_TUI_HIDDEN_MODE;
    ef[2].minExpectedLength = 0;
    ef[2].maxExpectedLength = ENTRY_FIELD_LENGTH_MAX;
    ef[2].buffer = (char *)TEE_Malloc((uint32_t)(sizeof(char) * (ENTRY_FIELD_LENGTH_MAX+1)), 0);
    CHECK_MALLOC(ef[2].buffer, res);
    ef[2].bufferLength = MAX_EF_STR_LEN;
    ef[2].label = TEE_Malloc(strlen("Enter Password")+1, 0);
    sprintf(ef[2].label, "Enter Password");

    /*=================================================
     * [04] Display screen
     *=================================================*/
    res = TEE_TUIDisplayScreen(&conf,
                               false,
                               ef,
                               DEF_ENTRY_FIELD_COUNT,
                               &key_pressed);
    if (res != TEE_SUCCESS) {
        printf("TEE_TUIDisplayScreen res: 0x%08x\n", res);
        if (res == TEE_ERROR_BAD_STATE) {
            printf("TEE_TUIDisplayScreen returned TEE_ERROR_BAD_STATE.\n"
                   "TUI session has been closed automatically because the TUI session\n"
                   "timeout has been reached or a TUI session has been closed\n"
                   "due to an OS specific external event. TEST PASSED.\n");
            res = TEE_SUCCESS;
            is_tui_started = false;
            goto FUNCTION_EXIT;
        }
    }

    res = TEE_TUICloseSession();
    is_tui_started = false;
    printf("TEE_TUICloseSession res: %d\n", res);
    CHECK_TEE_ERROR(res);
FUNCTION_EXIT:
    if (ef != NULL) {
        if (ef[0].label) {
            TEE_Free(ef[0].label);
        }
        if (ef[0].buffer) {
            TEE_Free(ef[0].buffer);
        }
        if (ef[1].label) {
            TEE_Free(ef[1].label);
        }
        if (ef[1].buffer) {
            TEE_Free(ef[1].buffer);
        }
        if (ef[2].label) {
            TEE_Free(ef[2].label);
        }
        if (ef[2].buffer) {
            TEE_Free(ef[2].buffer);
        }
        TEE_Free(ef);
    }

    if (label.image.ref.image != NULL) {
        TEE_Free(label.image.ref.image);
        label.image.ref.image = NULL;
    }

    return res;
}
