/**\file n_common.h
 * Common headers and low-level hugly functions & define
 *\author Castagnier Mickael
 *\version 1.0
 *\date 24/03/05
 */

#ifndef __NILOREA_COMMONS__
#define __NILOREA_COMMONS__

#ifdef __cplusplus
extern "C" {
#endif

/**\defgroup COMMONS COMMONS: macros, headers, defines, timers, allocators,...
  \addtogroup COMMONS
  @{
  */

#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <malloc.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <stdint.h>
#include <nilorea/n_log.h>
#include <nilorea/n_enum.h>

/*! set __windows__ if true */
#if defined(_WIN32) || defined(_WIN64)
#ifndef __windows__
#define __windows__
#endif
#ifndef WEXITSTATUS
#define WEXITSTATUS(w) (((w) >> 8) & 0xff)
#endif
#ifndef WIFEXITED
#define WIFEXITED(w) (((w)&0xff) == 0)
#endif
#else
#include <alloca.h>
#include <signal.h>
#endif

/*! feature test macro */
#define __EXTENSIONS__

#if defined(__GNUC__) && __GNUC__ >= 7
/*! fall through macro for switch cases, avoid warning at compilation */
#define FALL_THROUGH __attribute__((fallthrough))
#else
/*! fall through macro for switch cases, avoid warning at compilation */
#define FALL_THROUGH /* fall through */ \
    ((void)0)
#endif /* __GNUC__ >= 7 */

#if defined(_WIN32) || defined(_WIN64)
#if defined(_WIN64)
/*! Macro flag. Test if defined to check if we're on a 64 bits system */
#define ENV_64BITS
#else
/*! Macro flag. Test if defined to check if we're on a 32 bits system */
#define ENV_32BITS
#endif
#endif

#if !defined(ENV_32BITS) || !defined(ENV_64BITS)
#if defined(__GNUC__)
#if defined(__x86_64__) || defined(__ppc64__)
/*! defined to set number of bits of the system */
#define __ENVBITS __ENV_64BITS
#else
/*! defined to set number of bits of the system */
#define __ENVBITS __ENV_32BITS
#endif
#endif
#endif

/*! Little endian macro value */
#define BYTEORDER_LITTLE_ENDIAN 0  // Little endian machine.
                                   /*! Big endian macro value */
#define BYTEORDER_BIG_ENDIAN 1     // Big endian machine.

#ifndef BYTEORDER_ENDIAN
// Detect with GCC 4.6's macro.
#if defined(__BYTE_ORDER__)
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#else
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#warning "Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#warning "Setting default to BYTEORDER_LITTLE_ENDIAN"
#endif
// Detect with GLIBC's endian.h.
#elif defined(__GLIBC__)
#include <endian.h>
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#elif (__BYTE_ORDER == __BIG_ENDIAN)
#define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#else
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#warning "Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#warning "Setting default to BYTEORDER_LITTLE_ENDIAN"
#endif
// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro.
#elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
#define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
// Detect with architecture macros.
#elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
#define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#else
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#warning "Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#warning "Setting default to BYTEORDER_LITTLE_ENDIAN"
#endif
#endif

#if defined(__windows__)
/* typedefine for unsigned category for basic native types */
/*! shortcut for unsigned int*/
typedef unsigned int uint;
/*! shortcut for unsigned long*/
typedef unsigned long ulong;
/*! shortcut for unsigned short*/
typedef unsigned short ushort;
/*! shortcut for unsigned char*/
typedef unsigned char uchar;
#endif

/*! FORCE_INLINE portable macro */
#ifdef _MSVC_VER
#define FORCE_INLINE __forceinline
#elif defined(__linux__) || defined(__windows__)
#define FORCE_INLINE static inline __attribute__((always_inline))
#else
#define FORCE_INLINE static inline __attribute__((always_inline))
#endif

/*! define true */
#ifndef true
#define true (1 == 1)
#endif

/*! define TRUE */
#ifndef TRUE
#define TRUE true
#endif

/*! define false */
#ifndef false
#define false (1 == 0)
#endif

/*! define FALSE */
#ifndef FALSE
#define FALSE false
#endif

/*!  returned by N_STRLIST functions to tell the caller that the list is empty */
#ifndef EMPTY
#define EMPTY 2
#endif

/*! String or "NULL" string for logging purposes */
#define _str(__PTR) ((__PTR) ? (__PTR) : "NULL")
/*! String or NULL pointer for testing purposes */
#define _strp(__PTR) ((__PTR) ? (__PTR) : NULL)
/*! String or " " string for config purposes */
#define _strw(__PTR) ((__PTR) ? (__PTR) : " ")
/*! N_STR or "NULL" string for logging purposes */
#define _nstr(__PTR) ((__PTR && __PTR->data) ? (__PTR->data) : "NULL")
/*! N_STR or NULL pointer for testing purposes */
#define _nstrp(__PTR) ((__PTR && __PTR->data) ? (__PTR->data) : NULL)

/*! Malloc Handler to get errors and set to 0 */
#define Malloc(__ptr, __struct, __size)                                                                                                                                                           \
    {                                                                                                                                                                                             \
        int __n_errno = 0;                                                                                                                                                                        \
        int64_t __byte_size = __size;                                                                                                                                                             \
        if (__byte_size <= 0) __byte_size = 16;                                                                                                                                                   \
        errno = 0;                                                                                                                                                                                \
        __ptr = (__struct*)calloc(__byte_size, sizeof(__struct));                                                                                                                                 \
        __n_errno = errno;                                                                                                                                                                        \
        if (!__ptr) {                                                                                                                                                                             \
            n_log(LOG_ERR, "( %s *)calloc( %ld , sizeof( %s ) ) %s at line %d of %s \n", #__ptr, __size, #__struct, (__n_errno == 0) ? "malloc error" : strerror(__n_errno), __LINE__, __FILE__); \
        }                                                                                                                                                                                         \
    }

/*! Malloca Handler to get errors and set to 0 */
#define Alloca(__ptr, __size)                                                                                                                                      \
    {                                                                                                                                                              \
        int __n_errno = 0;                                                                                                                                         \
        int64_t __byte_size = __size;                                                                                                                              \
        if (__byte_size <= 0) __byte_size = 16;                                                                                                                    \
        errno = 0;                                                                                                                                                 \
        __ptr = alloca(__byte_size);                                                                                                                               \
        __n_errno = errno;                                                                                                                                         \
        if (!__ptr) {                                                                                                                                              \
            n_log(LOG_ERR, "%s=alloca( %d ) %s at line %d of %s \n", #__ptr, __size, (__n_errno == 0) ? "alloca error" : strerror(__n_errno), __LINE__, __FILE__); \
        } else {                                                                                                                                                   \
            memset(__ptr, 0, __byte_size);                                                                                                                         \
        }                                                                                                                                                          \
    }

/*! Realloc Handler to get errors */
#define Realloc(__ptr, __struct, __size)                                                                                                                                                           \
    {                                                                                                                                                                                              \
        int __n_errno = 0;                                                                                                                                                                         \
        int64_t __byte_size = __size * sizeof(__struct);                                                                                                                                           \
        if (__byte_size <= 0) __byte_size = 16;                                                                                                                                                    \
        errno = 0;                                                                                                                                                                                 \
        void* __new_ptr = (__struct*)realloc(__ptr, __byte_size);                                                                                                                                  \
        __n_errno = errno;                                                                                                                                                                         \
        if (!__new_ptr) {                                                                                                                                                                          \
            n_log(LOG_ERR, "( %s *)realloc( %s * sizeof( %d ) ) %s at line %d of %s \n", #__ptr, #__struct, __size, (__n_errno == 0) ? "realloc error" : strerror(__n_errno), __LINE__, __FILE__); \
        } else {                                                                                                                                                                                   \
            __ptr = __new_ptr;                                                                                                                                                                     \
        }                                                                                                                                                                                          \
    }

/*! Realloc + zero new memory zone Handler to get errors */
#define Reallocz(__ptr, __struct, __old_size, __size)                                                                                                                                              \
    {                                                                                                                                                                                              \
        int __n_errno = 0;                                                                                                                                                                         \
        size_t __byte_size = __size * sizeof(__struct);                                                                                                                                            \
        if (__byte_size <= 0) __byte_size = 16;                                                                                                                                                    \
        errno = 0;                                                                                                                                                                                 \
        void* __new_ptr = (__struct*)realloc(__ptr, __byte_size);                                                                                                                                  \
        __n_errno = errno;                                                                                                                                                                         \
        if (!__new_ptr) {                                                                                                                                                                          \
            n_log(LOG_ERR, "( %s *)realloc( %s * sizeof( %d ) ) %s at line %d of %s \n", #__ptr, #__struct, __size, (__n_errno == 0) ? "realloc error" : strerror(__n_errno), __LINE__, __FILE__); \
        } else {                                                                                                                                                                                   \
            __ptr = __new_ptr;                                                                                                                                                                     \
            if (__byte_size > __old_size) memset((__ptr + __old_size), 0, __byte_size - __old_size);                                                                                               \
        }                                                                                                                                                                                          \
    }

/*! Free Handler to get errors */
#define Free(__ptr)                                                                                          \
    if (__ptr) {                                                                                             \
        free(__ptr);                                                                                         \
        __ptr = NULL;                                                                                        \
    } else {                                                                                                 \
        n_log(LOG_DEBUG, "Free( %s ) already done or NULL at line %d of %s \n", #__ptr, __LINE__, __FILE__); \
    }

/*! Free Handler without log */
#define FreeNoLog(__ptr) \
    if (__ptr) {         \
        free(__ptr);     \
        __ptr = NULL;    \
    }

/*! macro to assert things */
#define __n_assert(__ptr, __ret)                                                             \
    if (!(__ptr)) {                                                                          \
        n_log(LOG_DEBUG, "if( !(%s) ) assert at line %d of %s", #__ptr, __LINE__, __FILE__); \
        __ret;                                                                               \
    }

/*! TEMP_FAILURE gnu macro portable version */
#define CALL_RETRY(retvar, expression) \
    do {                               \
        retvar = (expression);         \
    } while (retvar == -1 && errno == EINTR);

/*! next odd helper */
#define next_odd(__val) ((__val) % 2 == 0) ? (__val) : (__val + 1)

/*! next odd helper */
#define next_even(__val) ((__val) % 2 == 0) ? (__val + 1) : (__val)

/*! init error checking in a function */
#define init_error_check() \
    static int ___error__check_flag = FALSE;

/*! error checker type if( !toto ) */
#define ifnull if( !

/*! error checker type if( 0 != toto ) */
#define ifzero if( 0 ==

/*! error checker type if( toto == FALSE )  */
#define iffalse if( FALSE ==

/*! error checker type if( toto == FALSE )  */
#define iftrue if( TRUE ==

/*! check for errors */
#define checkerror()                                                                    \
    if (___error__check_flag == TRUE) {                                                 \
        n_log(LOG_ERR, "checkerror return false at line %d of %s", __LINE__, __FILE__); \
        goto error;                                                                     \
    }

/*! close a ifwhatever block */
#define endif ){                                                              \
        ___error__check_flag = TRUE;                                          \
        n_log(LOG_ERR, "First err was at line %d of %s", __LINE__, __FILE__); \
    }

/*! pop up errors if any */
#define get_error() \
    (___error__check_flag == TRUE)

/*! if( a , condition, b ) then a = b */
#define equal_if(__a, __cond, __b) \
    if ((__a)__cond(__b)) {        \
        __a = __b;                 \
    }

//#define RWLOCK_DEBUG 1
#ifdef RWLOCK_DEBUG
/*! flag to tell the API to log DEBUG operation on locks if needed */
#define RWLOCK_LOGLEVEL LOG_DEBUG
#else
/*! flag to tell the API to disable DEBUG log on locks */
#define RWLOCK_LOGLEVEL LOG_NULL
#endif

/*! Macro for initializing a rwlock */
#define init_lock(__rwlock_mutex)                                                                                                             \
    ({                                                                                                                                        \
        pthread_rwlockattr_t __attr;                                                                                                          \
        pthread_rwlockattr_init(&__attr);                                                                                                     \
        int __ret = 0;                                                                                                                        \
        do {                                                                                                                                  \
            n_log(RWLOCK_LOGLEVEL, "init_lock %s", #__rwlock_mutex);                                                                          \
            __rwlock_mutex = (pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER;                                                                    \
            __ret = pthread_rwlock_init(&(__rwlock_mutex), &__attr);                                                                          \
            if (__ret != 0) {                                                                                                                 \
                n_log(LOG_ERR, "Error %s while initializing %s at %s:%s:%d", strerror(__ret), #__rwlock_mutex, __FILE__, __func__, __LINE__); \
            }                                                                                                                                 \
            pthread_rwlockattr_destroy(&__attr);                                                                                              \
        } while (0);                                                                                                                          \
        __ret;                                                                                                                                \
    })

/*! Macro for acquiring a read lock on a rwlock mutex */
#define read_lock(__rwlock_mutex)                                                                                                             \
    ({                                                                                                                                        \
        int __ret = 0;                                                                                                                        \
        do {                                                                                                                                  \
            n_log(RWLOCK_LOGLEVEL, "read lock %s", #__rwlock_mutex);                                                                          \
            __ret = pthread_rwlock_rdlock(&(__rwlock_mutex));                                                                                 \
            if (__ret != 0) {                                                                                                                 \
                n_log(LOG_ERR, "Error %s while read locking %s at %s:%s:%d", strerror(__ret), #__rwlock_mutex, __FILE__, __func__, __LINE__); \
            }                                                                                                                                 \
        } while (0);                                                                                                                          \
        __ret;                                                                                                                                \
    })

/*! Macro for acquiring a write lock on a rwlock mutex */
#define write_lock(__rwlock_mutex)                                                                                                             \
    ({                                                                                                                                         \
        int __ret = 0;                                                                                                                         \
        do {                                                                                                                                   \
            n_log(RWLOCK_LOGLEVEL, "write lock %s", #__rwlock_mutex);                                                                          \
            __ret = pthread_rwlock_wrlock(&(__rwlock_mutex));                                                                                  \
            if (__ret != 0) {                                                                                                                  \
                n_log(LOG_ERR, "Error %s while write locking %s at %s:%s:%d", strerror(__ret), #__rwlock_mutex, __FILE__, __func__, __LINE__); \
            }                                                                                                                                  \
        } while (0);                                                                                                                           \
        __ret;                                                                                                                                 \
    })

/*! Macro for releasing read/write lock a rwlock mutex */
#define unlock(__rwlock_mutex)                                                                                                             \
    ({                                                                                                                                     \
        int __ret = 0;                                                                                                                     \
        do {                                                                                                                               \
            n_log(RWLOCK_LOGLEVEL, "unlock lock %s", #__rwlock_mutex);                                                                     \
            __ret = pthread_rwlock_unlock(&(__rwlock_mutex));                                                                              \
            if (__ret != 0) {                                                                                                              \
                n_log(LOG_ERR, "Error %s while unlocking %s at %s:%s:%d", strerror(__ret), #__rwlock_mutex, __FILE__, __func__, __LINE__); \
            }                                                                                                                              \
        } while (0);                                                                                                                       \
        __ret;                                                                                                                             \
    })
/*! Macro to destroy rwlock mutex */
#define rw_lock_destroy(__rwlock_mutex)                                                                                                     \
    ({                                                                                                                                      \
        int __ret = 0;                                                                                                                      \
        do {                                                                                                                                \
            n_log(RWLOCK_LOGLEVEL, "destroy lock %s", #__rwlock_mutex);                                                                     \
            __ret = pthread_rwlock_destroy(&(__rwlock_mutex));                                                                              \
            if (__ret != 0) {                                                                                                               \
                n_log(LOG_ERR, "Error %s while destroying %s at %s:%s:%d", strerror(__ret), #__rwlock_mutex, __FILE__, __func__, __LINE__); \
            }                                                                                                                               \
        } while (0);                                                                                                                        \
        __ret;                                                                                                                              \
    })

/*! Flag for SET something , passing as a function parameter */
#define SET 1234
/*! Flag for GET something , passing as a function parameter */
#define GET 4321
/*! Default APP_STATUS Value */
#define DEFAULT 1000
/*! Value of the state of an application who is running */
#define RUNNING 1001
/*! Value of the state of an application who want to stop his activity */
#define STOPWANTED 1002
/*! Value of the state of an application who is stopped */
#define STOPPED 1003
/*! Value of the state of an application who is paused */
#define PAUSED 1004

/*! Initialize the random sequence with time */
#define randomize()                  \
    {                                \
        srand((unsigned)time(NULL)); \
        rand();                      \
    }

#ifndef MIN
/*! define MIN macro */
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
/*! define MIN macro */
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif

/*! CONCAT macro helper */
#define CONCAT_BUILDER(a, b) a##b
/*! Concatenate two macro */
#define CONCAT(a, b) CONCAT_BUILDER(a, b)

#if (BYTEORDER_ENDIAN == BYTEORDER_LITTLE_ENDIAN)
/*! htonl for 64 bits numbers */
#define htonll(x) (x)
/*! ntonl for 64 bits numbers */
#define ntohll(x) (x)
#else
/*! htonl for 64 bits numbers */
#define htonll(x) ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
/*! ntonl for 64 bits numbers */
#define ntohll(x) ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
#endif

/* exit and print log to stderr */
void n_abort(char const* format, ...);

int get_computer_name(char* computer_name, size_t len);

/* get running program name */
char* get_prog_name(void);

/* get running program directory */
char* get_prog_dir(void);

/* test file presence*/
int file_exist(const char* filename);

/* shortcut for popen usage */
int n_popen(char* cmd, int read_buf_size, void** nstr_output, int* ret);

/* log environnement */
void log_environment(int loglevel);

#ifndef __windows__
/* reap zombie processes */
void sigchld_handler(int sig);
/* install signal SIGCHLD handler */
int sigchld_handler_installer();
/*! daemonize flag: no descriptor close at all */
#define N_DAEMON_NO_CLOSE 2
/*! daemonize flag: just do not redirect stdin/out/err to /dev/null */
#define N_DAEMON_NO_STD_REDIRECT 4
/*! daemonize flag: do not double fork */
#define N_DAEMON_NO_DOUBLE_FORK 8
/*! daemonize flag: do not call setsid */
#define N_DAEMON_NO_SETSID 16
/*! daemonize flag: do not call umask( 0 ) */
#define N_DAEMON_NO_UMASK 32
/*! daemonize flag: do not call chdir("/") */
#define N_DAEMON_NO_CHDIR 64
/*! daemonize flag: do not ignore SIGCHLD */
#define N_DAEMON_NO_SIGCHLD_IGN 128
/*! daemonize flag: do not use internal zombie SIGCHLD handler */
#define N_DAEMON_NO_SIGCHLD_HANDLER 256
/* daemonize */
int n_daemonize(void);
/* daemonize */
int n_daemonize_ex(int mode);
/* non blocking system call */
pid_t system_nb(const char* command, int* infp, int* outfp);
#endif

/* reconstruct hidden string */
void N_HIDE_STR(char* buf, ...);

char* n_get_file_extension(char path[]);
/**
  @}
  */

#ifdef __cplusplus
}
#endif

#endif  // header guard
