/*
 * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Created in Samsung Ukraine R&D Center (SRK) under a contract between
 * LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine)
 * and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
 *
 * Created on: Jul 1, 2016
 * Author: Oleksii Kachkan <o.kachkan@samsung.com>
 * Brief: Helper functions for attestation key's operations.
 */

#include "TigerStorageUtils.h"

#include "TigerLogging.h"
#include "TigerMacros.h"
#include "TigerSskds.h"
#include "TigerCounterDataStore.h"
#include "TzwString.h"

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int32_t getOffsetTo(TigerDeviceIdObjField_t field) {
    int32_t pos = -1;
    switch (field) {
    case DEVICE_ID:
        pos = 0;
        break;
    case REG_COUNT:
        pos = DEVICE_ID_SIZE;
        break;
    case COUNTER:
        pos = DEVICE_ID_SIZE + sizeof(TigerCounter_t);
        break;
    default:
        break;
    }

    return pos;
}

int32_t getSessionOffsetTo(TigerSessionObjFiled_t field){
    int32_t pos = -1;
    switch (field) {
    case SESSION_ID:
        pos = 0;
        break;
    case ALIAS_NAME:
        pos = sizeof(uint64_t);
        break;
    case UID:
        pos = MAX_KEY_ALIAS_SIZE_BYTES + sizeof(uint64_t);
        break;
    case CHALLENGE:
        pos = MAX_KEY_ALIAS_SIZE_BYTES + sizeof(uint64_t) + sizeof(uint32_t);
        break;
    case GENTIME:
        pos = MAX_KEY_ALIAS_SIZE_BYTES + sizeof(uint64_t) + sizeof(uint32_t) + MAX_CHALLENGE_NAME_SIZE;
        break;
    default:
        break;
    }

    return pos;
}

bool isPermanentObject(const uint8_t* objectId, uint32_t objectIdLen) {
    TIGER_ASSERT(objectId != NULL);
    TIGER_ASSERT(objectIdLen > 0);

    S_VAR_NOT_USED(objectIdLen);

    if (0 == strncmp((const char*) objectId, TIGER_ALIAS_PREFIX, strlen(TIGER_ALIAS_PREFIX))) {
        return true;
    }
    return false;
}

TEE_Result saveToPersistentObject(const char* alias, uint8_t* data, uint32_t dataSize) {
    LOG_FUNC_BEGIN;

    TIGER_ASSERT(alias != NULL);
    TIGER_ASSERT(data != NULL);
    TIGER_ASSERT(dataSize != 0);

    TzwSfsObject_t objHandle = TEE_HANDLE_NULL;
    TEE_Result status = tzwOpenSfsObject((void*) alias,
                                         strlen(alias),
                                         TEE_DATA_FLAG_ACCESS_WRITE | TEE_DATA_FLAG_ACCESS_WRITE_META,
                                         &objHandle);

    if (TEE_SUCCESS == status) {
        tzwCloseAndDeleteSfsObject(objHandle);
        objHandle = TEE_HANDLE_NULL;
        LOG_D("Object '%s' successfully deleted!", alias);
    }

    status = tzwCreateSfsObject((void*) alias, strlen(alias),
                                TEE_DATA_FLAG_ACCESS_WRITE | TEE_DATA_FLAG_ACCESS_WRITE_META,
                                &objHandle);
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("tzwCreateSfsObject()")


    status = tzwWriteToSfsObject(objHandle, data, dataSize);
    if (TEE_SUCCESS != status) {
        LOG_E("tzwWriteToSfsObject() failed. Result - %x ", status);
    } else {
        LOG_D("Object '%s' was successfully saved!", alias);
    }

    tzwCloseSfsObject(objHandle);

    LOG_FUNC_END;
    return status;
}

TEE_Result readFromPersistentObject(const char* alias, OUT uint8_t* data, IN OUT uint32_t* dataSize) {
    LOG_FUNC_BEGIN;

    TIGER_ASSERT(alias != NULL);
    TIGER_ASSERT(strlen(alias) != 0);
    TIGER_ASSERT(data != NULL);

    TzwSfsObject_t objHandle = TEE_HANDLE_NULL;
    TEE_Result status = TEE_SUCCESS;
    do {
        LOG_D("Openning %s file", alias);
        status = tzwOpenSfsObject((void*) alias, strlen(alias), TEE_DATA_FLAG_ACCESS_READ, &objHandle);
        TIGER_CHECK_TEE_STATUS_SUCCESS_BREAK("tzwOpenSfsObject()");

        TEE_ObjectInfo info;
        tzwMemFill(&info, 0, sizeof(TEE_ObjectInfo));
        status = tzwGetSfsObjectInfo(objHandle, &info);
        TIGER_CHECK_TEE_STATUS_SUCCESS_BREAK("tzwGetSfsObjectInfo()");
        TIGER_CHECK_CONDITION_BREAK(*dataSize >= info.dataSize);

        uint32_t readBytesCount = 0;
        status = tzwReadFromSfsObject(objHandle, data, info.dataSize, &readBytesCount);
        TIGER_CHECK_TEE_STATUS_SUCCESS_BREAK("tzwReadFromSfsObject");
        if (info.dataSize != readBytesCount) {
            LOG_E("tzwReadFromSfsObject failed");
            break;
        }

        (*dataSize) = readBytesCount;
    } while (0);

    tzwCloseSfsObject(objHandle);

    LOG_FUNC_END;
    return status;
}

TEE_Result deletePersistentObject(const uint8_t* objectId, uint32_t objectIdLen) {
    TzwSfsObject_t obj = TEE_HANDLE_NULL;
    TEE_Result status = TEE_SUCCESS;
    status = tzwOpenSfsObject((void*) objectId, objectIdLen,
            TEE_DATA_FLAG_ACCESS_READ | TEE_DATA_FLAG_ACCESS_WRITE | TEE_DATA_FLAG_ACCESS_WRITE_META, &obj);

    if (TEE_ERROR_ITEM_NOT_FOUND == status) {
        LOG_D("Item %.*s does not exist.", objectIdLen, objectId);
        status = TEE_SUCCESS;
    } else if (TEE_SUCCESS == status) {
        tzwCloseAndDeleteSfsObject(obj);
    }

    return status;
}

TEE_Result clearSFS() {
    LOG_FUNC_BEGIN;
    TEE_Result status = TEE_SUCCESS;
    TzwSfsObjectList_t objectEnumerator = TEE_HANDLE_NULL;
    do {
        status = tzwAllocateSfsObjectList(&objectEnumerator);
        TIGER_CHECK_TEE_STATUS_SUCCESS_BREAK("tzwAllocateSfsObjectList failure.")

        status = tzwStartSfsObjectList(objectEnumerator);
        if (TEE_ERROR_ITEM_NOT_FOUND == status) {
            LOG_D("No object in the storage");
            break;
        }
        TIGER_CHECK_TEE_STATUS_SUCCESS_BREAK("tzwStartSfsObjectList failure.")

        uint8_t objectId[MAX_OBJ_ID_SIZE_BYTES] = {0};
        uint32_t objectIdLen = 0;
        while (TEE_ERROR_ITEM_NOT_FOUND != tzwGetNextSfsObject(objectEnumerator, objectId, &objectIdLen)) {
            LOG_D("Found object: %.*s", objectIdLen, objectId);
            if (false == isPermanentObject(objectId, objectIdLen)) {
                LOG_I("unsed object is being removed: %.*s", objectIdLen, objectId);

                status = deletePersistentObject(objectId, objectIdLen);
                TIGER_CHECK_TEE_STATUS_SUCCESS_CONTINUE("deletePersistentObject");
            }
        }
    } while(0);

    tzwFreeSfsObjectList(objectEnumerator);

    LOG_FUNC_END;
    return status;
}
