#ifndef OPENSSL_HEADER_PLATFORMS_H
#define OPENSSL_HEADER_PLATFORMS_H

#include <string.h>
#include <inttypes.h>
#include <stdint.h>
#include <stddef.h>
#include <time.h>

#if defined(x86_64)
    #include <sys/fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <strings.h>
#endif

#include <openssl/boringssl_config.h>
#include <openssl/utils.h>

#define FIPS_TAGS "FIPS_bssl"

#define LOG_BUF_SIZE 512
#define LOG_TZ_APP_NAME_MAX_LEN 32

#define PREFIX_TAG "["
#define D_TAG ":SCrypto] <DEBUG> "
#define I_TAG ":SCrypto] <INFO> "
#define E_TAG ":SCrypto] <ERROR> "
#define P_TAG ":SCrypto] <PERF> "

void ctr128_inc(uint8_t *counter);
void get_timevec(unsigned char *buf, unsigned long *pctr);

#if defined(MOBICORE) || defined(QSEE)

    unsigned long int getuid();
    unsigned long int getpid();
    unsigned long int getppid();

#endif

// WARING!!!
// Its mandatory to have instance of the variable in
// client application. The variable contains TZ application name,
// it is used for logging as well. Due to SCrypto library uses it,
// in case of the variable doesnt exist, link error will happen
extern char TZ_APP_NAME[];

#if defined(MOBICORE)

#define AF_RNG              (8U << 24)
#define TLAPI_ALG_SECURE_RANDOM (AF_RNG | 1)

/** Formatted logging functions.
 *
 * tlApiLogvPrintf, tlApiLogPrintf
 *
 * Minimal printf-like function to print logging message to NWd log.
 *
 * Supported formatters:
 *   %s String, NULL value emit "<NULL>".
 *   %x %X hex
 *   %p pointer (hex with fixed width of 8)
 *   %d %i signed decimal
 *   %u unsigned decimal
 *   %t timestamp (if available in platform). NOTE: This does not consume any value in parameter list.
 *   %% outputs single %
 *
 *   %s, %x, %d, and %u support width (example %5s). Width is interpreted as minimum number of characters.
 *   Hex number is left padded using '0' to desired width.
 *   Decimal number is left padded using ' ' to desired width.
 *   String is right padded to desired length.
 *
 *   Newline is used to terminate logging line.
 *
 * @param fmt     Formatter
 */
    extern void tlApiLogPrintf(const char *fmt, ...);
    extern void tlApiExit(uint32_t);
    extern void* tlApiMalloc(size_t size, uint32_t hint);
    extern void* tlApiRealloc(void* buffer, uint32_t newSize);
    extern void tlApiFree(void *buffer);
    struct tm* gmtime_r( const time_t* timep __attribute__((unused)), struct tm* result __attribute__((unused)) );

    #define printf tlApiLogPrintf
    #define ADD_LF printf("\n")

    #define fips_exit(x) do { tlApiExit(x); } while(0)

#elif defined(QSEE)

    extern void qsee_printf(const char* fmt, ...);

    extern void* qsee_malloc(size_t size);
    extern void* qsee_realloc(void *buf,size_t size);
    extern void qsee_free(void *buf);

    extern int strcasecmp(const char *a, const char *b);
    extern int strncasecmp(const char *a, const char *b, size_t n);

    #define printf qsee_printf
    #define ADD_LF

    #define fips_exit(x) abort()

#elif defined(x86_64)
    #define fips_exit(x) exit(x)
    #define ADD_LF
#endif

extern char log_buf[];
extern int log_app_name_len;

#define log_common(tag, fmt, ...) \
            do{ int log_len;                                        \
                \
                if ((tag == NULL) || (fmt == NULL)) { break; } \
                \
                if (log_app_name_len == 0) \
                {  \
                    if (LOG_TZ_APP_NAME_MAX_LEN < __strnlen(TZ_APP_NAME, LOG_TZ_APP_NAME_MAX_LEN) + 1) \
                    { log_app_name_len = LOG_TZ_APP_NAME_MAX_LEN; } \
                    else { log_app_name_len = strlen(TZ_APP_NAME) + 1; } \
                }  \
                \
                log_len = 0; \
                memcpy(&log_buf[log_len], PREFIX_TAG, sizeof(PREFIX_TAG));  \
                log_len += sizeof(PREFIX_TAG) - 1;  \
                \
                memcpy(&log_buf[log_len], TZ_APP_NAME, log_app_name_len);  \
                log_len += log_app_name_len - 1; \
                \
                memcpy(&log_buf[log_len], tag, strlen(tag));  \
                log_len += strlen(tag); \
                \
                int log_left_room = __strnlen(fmt, LOG_BUF_SIZE - log_len); \
                memcpy(&log_buf[log_len], fmt, log_left_room);  \
                log_len += log_left_room; \
                log_buf[log_len] = 0;  \
                printf(log_buf, __VA_ARGS__);                                                        \
                ADD_LF; \
            } while(0)
#define __log_common(tag, ...) log_common(tag, __VA_ARGS__, 0)

/* --------------------------------------------
 *    Debug Purpose
 * --------------------------------------------
 *
 * LOGFIPS_E and LOGFIPS_I are always printed
 * LOGFIPS_D is printed only for debug purpose. DEBUG_LOG_FIPS_BSSL=TRUE is required
 * LOGFIPS_P is used for performance analysis. DEBUG_LOG_PERFORMANCE_FIPS_BSSL=TRUE is required
 *
 */

#ifdef __ANDROID__
    #define LOGFIPS_E(...) __android_log_print(ANDROID_LOG_ERROR, FIPS_TAGS, __VA_ARGS__)
    #define LOGFIPS_I(...) __android_log_print(ANDROID_LOG_INFO,  FIPS_TAGS, __VA_ARGS__)
#else
    #define LOGFIPS_E(...) do { __log_common(E_TAG, __VA_ARGS__); } while(0)
    #define LOGFIPS_I(...) do { __log_common(I_TAG, __VA_ARGS__); } while(0)
#endif // __ANDROID__

#ifdef DEBUG_LOG_PERFORMANCE_FIPS_BSSL
    #define LOGFIPS_P(...) do { __log_common(P_TAG, __VA_ARGS__); } while(0)
    #define PERFORMANCE_INIT_TIME_VAR(NAME) unsigned long NAME = 0
    #define PERFORMANCE_GET_TIME(VAR_NAME)  do {VAR_NAME = (unsigned long)get_time_ms();}while(0)
#else
    #define LOGFIPS_P(...)
    #define PERFORMANCE_INIT_TIME_VAR(NAME) do {}while(0)
    #define PERFORMANCE_GET_TIME(VAR_NAME)  do {}while(0)
#endif

#ifdef DEBUG_LOG_FIPS_BSSL
    #ifdef __ANDROID__
        #define LOGFIPS_D(...) __android_log_print(ANDROID_LOG_DEBUG, FIPS_TAGS, __VA_ARGS__)
    #else
        #define  LOGFIPS_D(...) do { __log_common(D_TAG, __VA_ARGS__); } while(0)
    #endif // __ANDROID__
#else
    #define LOGFIPS_D(...)
#endif // DEBUG_LOG_FIPS_BSSL

double get_time_ms(void);
int FIPS_get_allocated_heap_size();


#endif
