#ifndef EP2SEC_COMMON_H
#define EP2SEC_COMMON_H

// standard libraries
#include <stdbool.h> // use booleans please!
#include <stddef.h> // size_t type
#include <stdint.h> // uintX_t types
#include <string.h> // strlen
#include <stdarg.h> // va_list
//#include <sys/types.h> // ssize_t
#include <stdlib.h> // strtoul
#include <limits.h> // int max

#include <mpos_auth_defs.h>
// top message
//#include "top_message.h"

// trustonic libraries
//#include <taStd.h>
#include <tee_internal_api.h>
//#include <tee_internal_api_ext.h>
//#include <tee_internal_api_tap.h>
//#include <callback.h>

#ifdef THP_WHITEBOX
#error "Whitebox is not supported!"
#endif

// logging stuff
//#include "other/logging.h"
#include "mpos_auth_defs.h"

// other useful macros!
static inline void __unused__(void* n, ...) { (void)(n); return; }
#define UNUSED(x, ...) __unused__(NULL, x, ##__VA_ARGS__)

TEE_Result _status_ok(const char* file, int line, const char* func);
TEE_Result _status_ok_prod();

#ifdef MODE_DEBUG
#define STATUS_OK() _status_ok(__FILE__, __LINE__, __func__);
#else
#define STATUS_OK() _status_ok_prod();

#endif

#define CHECK_OR_MESSAGE(statement, label, value, message, ...) \
do {\
    status = (statement); \
    if (status != TEE_SUCCESS) { \
        status = (value); \
        MPOS_AUTH_LOG(message,  ##__VA_ARGS__); \
        goto label; } } while(0); //NOLINT

#ifdef DEBUG_MPOS_AUTH
#define CHECK_OR(statement, label, value) CHECK_OR_MESSAGE(statement, label, value, "Failed checked call -> %08x, %s:%d", status, __func__, __LINE__)        
#else
#define CHECK_OR(statement, label, value) CHECK_OR_MESSAGE(statement, label, value, "Failed checked call -> %08x", status)
#endif

#define CHECK_MALLOC(pointer, size, label) \
do { \
    size_t _SIZE_ = (size); \
    (pointer) = (__typeof(pointer))calloc(1, _SIZE_); \
    CHECK_OR_MESSAGE((pointer) == NULL ? TEE_ERROR_OUT_OF_MEMORY : TEE_SUCCESS, label, status, "Failed allocation: `%s` of size %d", #pointer, _SIZE_); \
    MPOS_AUTH_LOG("%s:%d, %s allocated at addr = %p", __func__, __LINE__, #pointer, (void *)(pointer)); \
    } while(0); //NOLINT


#define CHECK(statement, label) CHECK_OR(statement, label, status)
        
//#define CHECK_ASN(statement, label) CHECK_OR_MESSAGE(statement, label, (status + TEE_ERROR_ASN), "Failed ASN checked call -> %08x", status)
#define CHECK_ASN(statement, label) CHECK_OR_MESSAGE(statement, label, (status), "Failed ASN checked call -> %08x", status)

#define ASSERT_EQ(a, b, err, exit_label) CHECK_OR_MESSAGE((a) == (b) ? TEE_SUCCESS : (err), exit_label, status, "Assertion failed %08x == %08x", a, b)

#define ASSERT_ZERO(x, err, exit_label) CHECK(0 == (x) ? TEE_SUCCESS : (err), exit_label)
    
#define ASSERT_TRUE(x, err, exit_label) CHECK(true == (x) ? TEE_SUCCESS : (err), exit_label)

// asn specific stuff, actual bool in asn_tools
extern const bool asn_trace_enabled;

////#define ASN_DEBUG(...) do { if(asn_trace_enabled) TRACE( __VA_ARGS__ ); } while(0);
////#define ASN_DEBUG(...) do { if(asn_trace_enabled) MPOS_AUTH_LOG( __VA_ARGS__ ); } while(0);
#define ASN_DEBUG(...)

#ifdef MODE_DEBUG
#define ASN_ASSERT(x) do { if(!(x)) { TRACE("ASN ASSERT FAIL @ %s:%d", __FILE__, __LINE__); panic(TEE_ERROR_ASN); } } while(0);
#else
////#define ASN_ASSERT(x) do { if(!(x)) { panic(TEE_ERROR_ASN); } } while(0);
#define ASN_ASSERT(x)
#endif

#ifndef MODE_DEBUG
#define ASN_DISABLE_XER_SUPPORT
#endif

// a couple reimplementations of stuff from stdlib
// #define CUSTOM_ALLOC
// #define TRACE_ALLOC

// hacks :)
// #define SKIP_CERTIFICATE_PERMUTATION_HACK

#if defined(CONFIG_SUPPORT_EP2)
#include "standard/standard.h"
#endif
#endif
