#ifndef DATABASE_H
#define DATABASE_H

// it's not good practice to include this here, but I don't see much better way of doing this
#include "common.h"
#if defined(CONFIG_SUPPORT_EP2)
#include "ep2_public_key.h"
#endif

typedef enum {
    HARDCODED,
    PERSISTANT,
    GKLP_TEMPORARY,
    EP2_TEMPORARY,
    ATTESTATION,
    TEMPORARY
} persistance_type;


#define FILE_STORAGE_DB_PREFIX "tf"
#define PUBLIC_KEY_DB_PREFIX "pk"
#define TRM_KEY_DB_PREFIX "acq-trm-key"

// fix me!! : it's temporary define to avoid build
#define TEE_ERROR_DATABASE_ID_UNKNOWN		-10
#define TEE_ERROR_DATABASE_ID_MISMATCH		-11
#define TEE_ERROR_DATABASE_OUTPUT_SIZE_MISMATCH	-12
#define ACQUIRER_NAME_LENGTH		11

/**
* @brief Identification of structures stored in database
* 
* public keys are loaded outside these constraints, 
* everything else (data, symmetric keys) should be listed here
*/
#define D(id, type, persistance) id,
typedef enum {
#include "database_ids.h"
} KeyDBId;

/**
* @brief stores a DES symetric key identified by id into DB
* 
* @param dbid database id of the entry
* @param oh object handler, that is initialized correctly
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_set(KeyDBId dbid, TEE_ObjectHandle oh);

#if defined(CONFIG_SUPPORT_EP2)
/**
* @brief stores a DES symetric key identified by id into DB
* 
* @param id id of the entry, should be of type TEE_TYPE_DES3, only few are available
* @param data actual key data, sould be bits long
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_set_des3(KeyDBId id, const uint8_t* data);

// TODO: descriptions
TEE_Result database_set_trm_key(const char* acq_name, const uint8_t* data);
TEE_Result database_get_trm_key(const char* acq_name, TEE_ObjectHandle* oh);
#endif

/**
* @brief stores some bytes into DB
* 
* @param id id of the entry, should be of type TEE_TYPE_DATA
* @param data actual data to store
* @param data_size size of data in bytes
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_set_data(KeyDBId id, uint8_t* data, size_t data_size);

/**
* @brief stores some bytes into DB, identified directly
* 
* @param object_id direct object id of the entry
* @param data actual data to store
* @param data_size size of data in bytes
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_set_data_direct(const char* object_id, uint8_t* data, size_t data_size);

/**
* @brief appends bytes to already existing DB entry,
* 
* @param id id of the entry, should be of type TEE_TYPE_DATA
* @param data pointer to data, owned by the calling function
* @param output_size size of data in bytes
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_append_data(KeyDBId id, uint8_t* data, size_t data_size);

/**
* @brief appends bytes to already existing DB entry, identified directly
* 
* @param id object id of the entry
* @param data pointer to data, owned by the calling function
* @param output_size size of data in bytes
* @return TEE_SUCCESS if alles gut
*/
TEE_Result
database_append_data_direct(const char* object_id, uint8_t* data, size_t data_size);

/**
* @brief retrieves some bytes from DB
* 
* @param id id of the entry, should be of type TEE_TYPE_DATA
* @param data pointer to returned data, owned by the calling function
* @param output_size size of returned data in bytes
* @param check_output_size makes the function fail if number of returned bytes does not match output_size
* @param offset reading the data from an offset
* @return TEE_SUCCESS if alles gut, TEE_ERROR_DATABASE_OUTPUT_SIZE_MISSMATCH output_size not as expected
*/
TEE_Result database_get_data(KeyDBId id, uint8_t* data, size_t* output_size, bool check_output_size, size_t offset);

/**
* @brief retrieves some bytes from DB directly by object name, otherwise same as database_get_data
* 
* @param name name of the object in database
* @param data pointer to returned data, owned by the calling function
* @param output_size size of returned data in bytes
* @param check_output_size makes the function fail if number of returned bytes does not match output_size
* @param offset reading the data from an offset
* @return TEE_SUCCESS if alles gut, TEE_ERROR_DATABASE_OUTPUT_SIZE_MISSMATCH output_size not as expected
*/
TEE_Result
database_get_data_direct(const char* name, uint8_t* data, size_t* output_size, bool check_output_size, size_t offset);

/**
* @brief retrieves a Trustonic object from DB
* 
* @param id id of the entry
* @param persistent_oh object handle to returned data, owned by the calling function
* @return TEE_SUCCESS if alles gut
* 
* persistent_oh should be later cleaned via TEE_CloseObject
*/
TEE_Result database_get(KeyDBId id, TEE_ObjectHandle* persistent_oh);

/**
* @brief retrieves a Trustonic object from DB directly by name
* 
* @param id id of the entry
* @param persistent_oh object handle to returned data, owned by the calling function
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_get_direct(const char *object_id, TEE_ObjectHandle *persistent_oh);

/**
* @brief checks if Trustonic object exists in DB
* 
* @param id id of the entry
* @return true/false
*/
bool database_exists(KeyDBId id);

/**
* @brief checks if Trustonic object exists in DB
* 
* @param object_id object id of the entry
* @return true/false
*/
bool database_exists_direct(const char* object_id);

#if defined(CONFIG_SUPPORT_EP2)
/**
* @brief stores an RSA public key into DB directly
* 
* @param exponent exponent data
* @param exponent exponent data size
* @param exponent modulus data
* @param exponent modulus data size
* @param name id where this goes into DB
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_store_pk_direct(uint8_t* exponent, size_t exponent_size, uint8_t* modulus, size_t modulus_size, const char* name);

/**
* @brief stores an RSA public key into DB
* 
* @param pk data and id of the public key
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_store_pk(const public_key* pk);

/**
* @brief stores an RSA keypair into DB
* 
* @param key_id keydbid for this entry
* @return TEE_SUCCESS if alles gut
*/
TEE_Result
database_store_key_pair(KeyDBId key_id, 
                        uint8_t* modulus, 
                        size_t modulus_length, 
                        uint8_t* public_exponent, 
                        size_t pubexp_length, 
                        uint8_t* private_exponent, 
                        size_t privexp_length);


/**
* @brief retreives an RSA public key from DB
* 
* @param id identifictaion of public key (should be generated by pk_get_database_id)
* @param persistent_oh object handle to returned data, owned by the calling function
* @return TEE_SUCCESS if alles gut
* 
* persistent_oh should be later cleaned via TEE_CloseObject
*/
TEE_Result database_get_pk(char* id, TEE_ObjectHandle* persistent_oh);
#endif


/**
* @brief removes object from database identified by id
* 
* @param id id of object to remove
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_remove(KeyDBId id);


/**
* @brief removes object from database identified by name
* 
* @param id id of object to remove
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_remove_direct(const char* object_name);


/**
* @brief gets the size of data for database object
* 
* @param id id of object
* @param data_size the result itself
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_get_data_size(KeyDBId id, size_t* data_size);

/**
* @brief gets the size of data in database directly by object name
* 
* @param object_name name of object
* @param data_size the result itself
* @return TEE_SUCCESS if alles gut
*/
TEE_Result
database_get_data_size_direct(const char* object_name, size_t* data_size);


/**
* @brief retrieves raw string keyid in db for a given database id
* 
* @param id id of object to remove
* @param keyid returned string
* @param handle_type type of handle to return, data, key,...
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_dbid_to_strid(KeyDBId id, const char** keyid, unsigned handle_type);

/**
* @brief Cleans all database objects with certain prefix
* 
* @param prefix the prefix
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_clean_objects_with_prefix(const char* prefix);

/**
* @brief Clean part of the database
* @param clean_type what to clean in the db
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_clean(persistance_type clean_type);

/**
* @brief Clean everything, only leave hardcoded stuff in
* @param clean_persistant if false, only temporary stuff is cleaned
* @return TEE_SUCCESS if alles gut
*/
TEE_Result database_clean_full(bool clean_persistant);

#endif
