#pragma once
#include "conf/config.inc"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <assert.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>


#ifndef NDEBUG
    #define DEBUG
#endif

#define strnchr(s, c, n) (char*)memchr(s, c, n)
#define cstrlen(cstr) (sizeof(cstr)-1)
#define strccpy(p, cstr) strcpy(p, cstr) + cstrlen(cstr)

extern time_t* BEFORE_NOW; // for obfuscating NOW acesses only
extern time_t NOW;
typedef uint64_t msec_t;
extern msec_t NOW_MSEC;
extern char NOW_STR[cstrlen("Sun Sep 16 01:03:52 1973")+1]; // some stray newline
void update_now();

#define msleep(ms) usleep((ms)*1000)

extern int RECONFIGURE;
extern int CLEANUP;
extern enum shutdown_e {SHUTDOWN_NONE=0, SHUTDOWN_GRACEFUL=1, SHUTDOWN_NOW=2} SHUTDOWN;

const char* src_basename(const char*);

int null_fd();

typedef enum tristate_e {TRI_FALSE=0, TRI_TRUE=1, TRI_NONE=-1} tristate_t;
#define bi2tri(bi) ((bi)? TRI_TRUE: TRI_FALSE)

#define likely(x)   __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define unless(x) if (unlikely(!(x)))

#define NOOP do{}while(0)
#define VAL(x) #x
#define STRINGIFY(x) VAL(x)

#define _PRAGMA(x) _Pragma(#x)
#define MESSAGE(x) _PRAGMA(message(#x))

#define ALWAYS_INLINE inline __attribute__((always_inline))
#ifdef NDEBUG
    #define INLINE ALWAYS_INLINE
#else
    #define INLINE // for better stacktraces and/or profiling
#endif

#define is_noop_errno(rv) ((rv) == -1 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN))

#define TERM_RED   "\x1B[31m"
#define TERM_GREEN "\x1B[32m"
#define TERM_RESET "\033[0m"

typedef const char* loglevel_t;
extern struct loglevels_s {
    loglevel_t io, debug, info, notice, error;
} loglevels;
extern loglevel_t* loglevel;

typedef struct {
    const unsigned* no;
    const int* cfd;
    const int* sfd;
} logctx_t;
extern const logctx_t* logctx;
class LogCtx {
    private:
        const logctx_t* bup;
    public:
        INLINE LogCtx(const logctx_t* l): bup(logctx) { logctx = l; }
        INLINE ~LogCtx() { logctx = bup; }
};

bool in_main_thread();

void log_bt(int fd=-1);

#define STDERR(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
//#define log_ts(...) log(__VA_ARGS__) // TODO: need threadsafe logging?
#define log(lvl, fmt, ...) log_(&(loglevels.lvl), fmt, ##__VA_ARGS__)
#define log_(lvl, fmt, ...) if (unlikely(lvl >= loglevel)) { if (logctx) log__(lvl, "[%u/%d/%d] " fmt, *(logctx->no), *(logctx->cfd), *(logctx->sfd), ##__VA_ARGS__); else log__(lvl, fmt, ##__VA_ARGS__); } else NOOP
#define log__(lvl, fmt, ...) STDERR("%s %d %s " fmt, NOW_STR, (int)getpid(), *(lvl), ##__VA_ARGS__)
#define die(fmt, ...) do{log(error, fmt " - DYING", ##__VA_ARGS__); exit(1);}while(0)
#define log_errno(lvl, fmt, ...) log(lvl, fmt " - %d: %s", ##__VA_ARGS__, errno, strerror(errno))
#define die_errno(fmt, ...) die(fmt " - %d: %s", ##__VA_ARGS__, errno, strerror(errno))
#define log_mark(lvl) log(lvl, "[%s:%d] %s", src_basename(__FILE__), __LINE__, __PRETTY_FUNCTION__)
#define log_access(fmt, ...) log(notice, fmt, ##__VA_ARGS__)

#define talloc(t)  (t*)malloc(sizeof(t))
#define tcalloc(t) (t*)calloc(1, sizeof(t))

#define MIN(a, b) (((a)<(b))?(a):(b))
#define MAX(a, b) (((a)>(b))?(a):(b))
#define ABS(x)    (((x)<0)?-(x):(x))

#define CONCAT_(a, b) a ## b
#define CONCAT(a, b) CONCAT_(a, b)

#ifndef NULL
    #define NULL ((void*)0)
#endif
#define NOPE ((void*)-1)

#define safe_free(p) do { if (p) { free(p); p = NULL; } } while (0)
#define safe_delete(p) do { if (p) { delete p; p = NULL; } } while (0)

#define member_sizeof(t, m) sizeof(((t*)NULL)->m)
#define member_offset(t, m) ((&((t*)NULL)->m)) ///< offsetof
//#define member_offset(t, m) ((size_t)(&((t*)sizeof(t))->m) - sizeof(t)) ///< offsetof but w/o NULL ptr

#define EINTR_RETRY(func) while (unlikely(((func) == -1) && (errno == EINTR))) {}

typedef sig_atomic_t atomic_val_t;
typedef volatile atomic_val_t atomic_t;
#define mem_barrier() asm volatile("": : :"memory")
#ifdef __arm__
    #define cpu_relax() do { usleep(100); asm volatile("": : :"memory"); } while (0) // TODO: could use ARM yield?
#else
    #define cpu_relax() asm volatile("pause\n": : :"memory")
#endif
#define atomic_swp(p, o, n) __sync_bool_compare_and_swap((p), (o), (n))
#define atomic_add(p, v) __sync_add_and_fetch((p), (v))
#define atomic_get_and_reset(p) __sync_fetch_and_and((p), 0)
ALWAYS_INLINE void spin_lock(atomic_t* l) { while (unlikely(!atomic_swp(l, 0, 1))) { while (*l) cpu_relax(); } }
ALWAYS_INLINE void spin_unlock(atomic_t* l) { mem_barrier(); assert(*l); *l = 0; }
class SpinLock {
    private:
        atomic_t* lock;
    public:
        INLINE SpinLock(atomic_t* l): lock(l) { spin_lock(lock); }
        INLINE ~SpinLock() { spin_unlock(lock); }
};

//#define CONSTRUCTOR __attribute__((constructor)) // a constructor with a priority runs before a constructor without a priority
//#define CONSTRUCTOR_EARLY __attribute__((constructor(101)))
//#define CONSTRUCTOR_LATE __attribute__((constructor(65535)))
//#define DESTRUCTOR __attribute__((destructor)) // a destructor with a priority runs before a destructor without a priority
//#define DESTRUCTOR_EARLY __attribute__((destructor(65535)))
//#define DESTRUCTOR_LATE __attribute__((destructor(101)))

#define UNUSED __attribute__((unused))
#define PACKED __attribute__((packed))

#define INIT_EARLY __attribute__((init_priority(101)))
#define INIT_LATE __attribute__((init_priority(65535)))

//#define THREAD_LOCAL __thread

/*#ifdef NDEBUG
    #define BOOT_INFO(fmt, ...) UNUSED static bool CONCAT(BOOT_INFO_, __LINE__)()
#else
    #define BOOT_INFO(fmt, ...) static void CONSTRUCTOR CONCAT(BOOT_INFO_, __LINE__)() { STDERR("INFO: " fmt, ##__VA_ARGS__); }
#endif*/