/**
 * @file   TigerFingerprintIdTable.c
 * @brief  Handling of real FID (32-byte array)
 *         and mapping them to integer counters
 * @author Konstyantyn Volobuyev (k.volobuyev@samsung.com)
 * @date   Oct 02, 2017
 *
 * Copyright (c) 2017 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)
 **/

#include "TigerFingerprintIdTable.h"

#include "TigerLogging.h"
#include "TigerMacros.h"
#include "TigerStorageUtils.h"
#include "TzwString.h"

#include <inttypes.h>

#define AUTHHAT_FID_SIZE 32 // TODO(k.volobuyev): get size from bio buffer header
#define MAX_FP_ENROLLED 10  // The maximum number of fingerprints that can be enrolled per a device.
#define INITIAL_SOTER_FID 2 // to avoid synchronization with other FID using services (IFAA)

#if defined(TIGER_BUILD_TESTS)
#define TIGER_FID_TABLE_ALIAS  "TEST_TIGER_FID_TABLE"
#else
#define TIGER_FID_TABLE_ALIAS  "TIGER_FID_TABLE"
#endif

typedef struct {
    uint8_t realFid[AUTHHAT_FID_SIZE];
    uint32_t soterFid;
} __attribute__ ((packed)) FingerprintIdEntry_t;

typedef struct {
    FingerprintIdEntry_t entries[MAX_FP_ENROLLED];
} __attribute__ ((packed)) FidTable_t;

/**
 * @brief Module-global instance of FID table
 */
static FidTable_t gfidTable;

/**
 * @brief Module-global SOTER FID initial value
 */
static uint32_t gInitialSoterFid = INITIAL_SOTER_FID;

static uint32_t getMaxSoterFid();

void initFPTable() {
    LOG_FUNC_BEGIN;

    uint32_t fidTableSize = (uint32_t) sizeof(FidTable_t);
    TEE_Result status = readFromPersistentObject(TIGER_FID_TABLE_ALIAS,
                                                 (uint8_t*) &gfidTable,
                                                 &fidTableSize);
    if(TEE_SUCCESS != status) {
        TEE_Result status = saveToPersistentObject(TIGER_FID_TABLE_ALIAS,
                                                    (uint8_t*) &gfidTable,
                                                    fidTableSize);
        LOG_I("initFPTable::saveToPersistentObject() result = %x", status);
    }

    LOG_FUNC_END;
}

TEE_Result tigerFPTableLoad() {
    LOG_FUNC_BEGIN;

    uint32_t fidTableSize = (uint32_t) sizeof(FidTable_t);
    TEE_Result status = readFromPersistentObject(TIGER_FID_TABLE_ALIAS,
                                                 (uint8_t*) &gfidTable,
                                                 &fidTableSize);
    if(TEE_ERROR_ITEM_NOT_FOUND == status) {
        initFPTable();
        LOG_I("initFPTable in tigerFPTableLoad");
        status = readFromPersistentObject(TIGER_FID_TABLE_ALIAS,
                                         (uint8_t*) &gfidTable,
                                         &fidTableSize);

    }

    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("readFromPersistentObject");

    LOG_FUNC_END;
    return status;
}

TEE_Result tigerFPTableSave() {
    LOG_FUNC_BEGIN;

    uint32_t fidTableSize = (uint32_t) sizeof(FidTable_t);
    TEE_Result status = saveToPersistentObject(TIGER_FID_TABLE_ALIAS,
                                               (uint8_t*) &gfidTable,
                                               fidTableSize);
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("saveToPersistentObject");

    LOG_FUNC_END;
    return status;
}

TEE_Result tigerFPTableInsert(const uint8_t* const realFid,
                              const uint32_t realFidSize,
                              const uint32_t entryIndex,
                              const uint32_t soterFid) {
    LOG_FUNC_BEGIN;

    TIGER_CHECK_FUNCTION_ARGUMENT_RETURN(NULL != realFid);
    TIGER_CHECK_FUNCTION_ARGUMENT_RETURN(entryIndex < MAX_FP_ENROLLED);

    gfidTable.entries[entryIndex].soterFid = soterFid;
    tzwMemMove(gfidTable.entries[entryIndex].realFid, realFid, realFidSize);

    // backup the table to SFS storage after each insertion
    TEE_Result status = tigerFPTableSave();
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("tigerFPTableSave");

    LOG_FUNC_END;
    return status;
}

TEE_Result tigerFPTableGetUniqueId(const uint8_t* const realFid,
                                   const uint32_t realFidSize,
                                   const uint32_t entryIndex,
                                   uint32_t* soterFid) {
    LOG_FUNC_BEGIN;

    TIGER_CHECK_FUNCTION_ARGUMENT_RETURN(NULL != realFid);
    TIGER_CHECK_FUNCTION_ARGUMENT_RETURN(NULL != soterFid);
    TIGER_CHECK_FUNCTION_ARGUMENT_RETURN(entryIndex < MAX_FP_ENROLLED);

    TEE_Result status = TEE_SUCCESS;

    uint32_t nextSoterFid = 0;
    int32_t res = tzwMemCompare(gfidTable.entries[entryIndex].realFid,
                                realFid, realFidSize);
    if (0 == res) {
        nextSoterFid = gfidTable.entries[entryIndex].soterFid;
        LOG_D("The FID table contains SOTER FID for entry index %"PRIu32"", entryIndex);
    } else {
        LOG_D("The FID table doesn't contains SOTER FID for entry index %"PRIu32". ", entryIndex);
        // each new enrolled fingerprint requires unique integer FID (SOTER logic)
        nextSoterFid = getMaxSoterFid() + 1;
        status = tigerFPTableInsert(realFid, realFidSize, entryIndex, nextSoterFid);
        TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("tigerFPTableInsert");
    }

    *soterFid = nextSoterFid;

    LOG_FUNC_END;
    return status;
}

static uint32_t getMaxSoterFid() {
    uint32_t soterFid = gInitialSoterFid;
    for (uint32_t i = 0; i < MAX_FP_ENROLLED; i++) {
        if (soterFid < gfidTable.entries[i].soterFid) {
            soterFid = gfidTable.entries[i].soterFid;
        }
    }
    return soterFid;
}

