/*
 * tee_trusted_storage_samples_CA.c
 *
 * Trusted storage sample client application (NWd part)
 *
 */

#include "ta/test_TA_po_command_id.h"
#include "tee_client_api.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/*************************************************************************************************/

#define _EXIT_P(res) \
    do { \
        if (res) { \
            printf("Error: something went wrong, line %d\n", __LINE__); \
        } \
        exit(res); \
    } while (0)

/*************************************************************************************************/

TEEC_UUID uuid = {
    .timeLow = 0,
    .timeMid = 0,
    .timeHiAndVersion = 0,
    .clockSeqAndNode = {0, 0, 0, 0, 0, 0, 0x22, 0x67},
};

/*************************************************************************************************/

static const char *Sample_name[NUMBER_OF_SAMPLES] = {
    [PO_CREATE] = "create persistent object",
    [PO_WRITE] = "write data to persistent object",
    [PO_READ] = "read data from persistent object",
    [PO_DELETE] = "delete persistent object",
};

#define NUMBER_OF_ID 10

typedef struct {
    uint8_t run_mode;
    uint8_t fail_mode;
    size_t id_list_len;
    unsigned id_list[NUMBER_OF_ID];
} options_t;

enum {
    FAIL_MODE_CONTINUE = 0,
    FAIL_MODE_STOP,
};

enum {
    RUN_MODE_ALL = 0,
    RUN_MODE_SELECTED,
};

/*************************************************************************************************/

static void print_usage()
{
    printf("\ntrusted storage sample application\n");
    printf("Usage: ./tee_storage_samples [options [<args>]]\n");
    printf("Options:\n");
    printf("    -h           print this help and exit\n");
    printf("    -f           stop run if any sample fails\n");
    printf("    -s <args>    run sample with <args> list \n");
    for (unsigned i = 0; i < NUMBER_OF_SAMPLES; i++) {
        printf("        %u - %s\n", i, Sample_name[i]);
    }
    printf("Usage examples:\n");
    printf("    ./tee_storage_samples\n");
    printf("    ./tee_storage_samples -fs 0 1 2 3\n");
    printf("\n");
}

static void process_arguments(int argc, char *argv[], options_t *options)
{
    int c;
    unsigned id;

    if (!options) {
        _EXIT_P(EXIT_FAILURE);
    }
    memset(options, 0, sizeof(options_t));

    while ((c = getopt(argc, argv, "fs:h")) != -1) {
        switch (c) {
        case 's':
            options->run_mode = RUN_MODE_SELECTED;
            for (int i = optind - 1; i < argc; i++) {
                if (!sscanf(argv[i], "%u", &id)) {
                    break;
                }
                if (options->id_list_len >= NUMBER_OF_ID) {
                    printf("Warning: samples id-list is full\n");
                    break;
                }
                if (id >= NUMBER_OF_SAMPLES) {
                    printf("Error: unknown sample id = %d\n", id);
                    exit(EXIT_FAILURE);
                }
                options->id_list[options->id_list_len] = id;
                options->id_list_len++;
            }
            if (!options->id_list_len) {
                printf("Error: no selected samples\n");
                exit(EXIT_FAILURE);
            }
            break;
        case 'f':
            options->fail_mode = FAIL_MODE_STOP;
            break;
        case 'h':
            print_usage();
            exit(EXIT_SUCCESS);
        default:
            print_usage();
            exit(EXIT_FAILURE);
        }
    }

    if (options->run_mode == RUN_MODE_ALL) {
        for (unsigned i = 0; i < NUMBER_OF_SAMPLES; i++) {
            options->id_list[i] = i;
        }
        options->id_list_len = NUMBER_OF_SAMPLES;
    }
}

/*************************************************************************************************/

/*
 * Runs all or selected from command line samples
 */
int main(int argc, char *argv[])
{
    TEEC_Result res;
    TEEC_Context context;
    TEEC_Session session;
    uint32_t return_origin;
    options_t opt;

    process_arguments(argc, argv, &opt);

    printf("Initialize context\n");
    res = TEEC_InitializeContext(NULL, &context);
    if (res != TEEC_SUCCESS) {
        printf("Error: TEEC_InitializeContext failed, res = 0x%08x\n", res);
        return EXIT_FAILURE;
    }

    printf("Open session\n");
    res = TEEC_OpenSession(&context, &session, &uuid, 0, NULL, NULL, &return_origin);
    if (res != TEEC_SUCCESS) {
        printf("Error: TEEC_OpenSession failed, res = %x from %x\n", res, return_origin);
        TEEC_FinalizeContext(&context);
        return EXIT_FAILURE;
    }

    printf("Run\n");
    for (unsigned i = 0, id; i < opt.id_list_len; i++) {
        id = opt.id_list[i];
        printf("%u.%s\n", id, Sample_name[id]);
        res = TEEC_InvokeCommand(&session, id, NULL, &return_origin);
        if ((res != TEEC_SUCCESS)
         || (return_origin != TEEC_ORIGIN_TRUSTED_APP)) {
            printf("%u.%s failed, res = 0x%08x\n", id, Sample_name[id], res);
            if (opt.fail_mode) {
                return EXIT_FAILURE;
            }
        } else {
            printf("%u.%s successfully executed\n", id, Sample_name[id]);
        }
    }

    TEEC_CloseSession(&session);
    TEEC_FinalizeContext(&context);

    printf("Done\n");

    return EXIT_SUCCESS;
}
