/*
 * @file tfe_utils.h
 * This file provides common usage marcos and helper functions.
 */

#pragma once
#include <MESA/MESA_handle_logger.h>
#include <MESA/MESA_htable.h>
#include <time.h>
#include <dirent.h> //scan_dir
#include <stdbool.h>

#define TFE_STRING_MAX			2048
#define	TFE_PATH_MAX			256
#define TFE_SYMBOL_MAX			64
#define TFE_THREAD_MAX			128

#ifndef TFE_CONFIG_BACKLOG_DEFAULT
#define TFE_CONFIG_BACKLOG_DEFAULT		20
#endif

#define ALLOC(type, number) ((type *)calloc(sizeof(type), number))
#define	FREE(p)	{free(*p);*p=NULL;}

#define TFE_STRUCT_ALLOC(struct_type) __extension__			 										\
({                                                                                                  \
    ((struct_type) * ) __struct_ptr;																\
    __struct_ptr = ((struct_type) *)malloc(sizeof((struct_type)));               					\
    __struct_ptr;                                                                  					\
})

#define likely(expr)    __builtin_expect((expr), 1)
#define unlikely(expr)  __builtin_expect((expr), 0)
#define UNUSED	__attribute__((unused))

extern void * g_default_logger;
extern bool g_print_to_stderr;

#define TFE_LOG_ERROR(handler, fmt, ...)															\
do { if(g_print_to_stderr) fprintf(stderr, fmt "\n" , ##__VA_ARGS__);								\
	 MESA_handle_runtime_log(handler, RLOG_LV_FATAL, __FUNCTION__, fmt, ##__VA_ARGS__); } while(0)

#define TFE_LOG_INFO(handler, fmt, ...)																\
do { if(g_print_to_stderr) fprintf(stderr, fmt "\n", ##__VA_ARGS__);								\
	 MESA_handle_runtime_log(handler, RLOG_LV_INFO, __FUNCTION__, fmt, ##__VA_ARGS__); } while(0)	\

#define TFE_LOG_DEBUG(handler, fmt, ...)															\
do { MESA_handle_runtime_log(handler, RLOG_LV_DEBUG, __FUNCTION__, fmt, ##__VA_ARGS__); } while(0)	\

#define CHECK_OR_EXIT(condition, fmt, ...)																			\
do { if(!(condition)) { TFE_LOG_ERROR(g_default_logger, fmt, ##__VA_ARGS__); exit(EXIT_FAILURE);  } } while(0)		\

#define DIE(fmt, ...)                                                                                   \
do { TFE_LOG_ERROR(g_default_logger, "DIE: " fmt, ##__VA_ARGS__); abort(); } while(0)                   \

#define CHECK_OR_DIE(condition, fmt, ...)															\
do { if(!(condition)) { TFE_LOG_ERROR(g_default_logger, fmt, ##__VA_ARGS__); abort();  } } while(0)	\

#define TFE_STREAM_LOG_DEBUG(stream, fmt, ...)
#define TFE_STREAM_LOG_INFO(stream, fmt, ...)
#define TFE_STREAM_LOG_ERROR(stream, fmt, ...) do { fprintf(stderr, fmt "\n", ##__VA_ARGS__); } while(0)

#define TFE_STREAM_TRACE_TAG_INFO(stream, tag, fmt, ...)
#define TFE_STREAM_TRACE_TAG_VERBOSE(stream, tag, fmt, ...)

#ifndef offsetof
/** Return the offset of a field in a structure. */
#define offsetof(TYPE, MEMBER)  __builtin_offsetof (TYPE, MEMBER)
#endif

/**
 * Return pointer to the wrapping struct instance.
 *
 * Example:
 *
 *  struct wrapper {
 *      ...
 *      struct child c;
 *      ...
 *  };
 *
 *  struct child *x = obtain(...);
 *  struct wrapper *w = container_of(x, struct wrapper, c);
 */
#ifndef container_of
#define container_of(ptr, type, member)	__extension__ ({											\
			const typeof(((type *)0)->member) *_ptr = (ptr); 										\
			__attribute__((unused)) type *_target_ptr =	(type *)(ptr);								\
			(type *)(((uintptr_t)_ptr) - offsetof(type, member)); 									\
		})
#endif

#define	ATOMIC_INC(x) 		__atomic_fetch_add(x,1,__ATOMIC_RELAXED)
#define	ATOMIC_DEC(x) 		__atomic_fetch_sub(x,1,__ATOMIC_RELAXED)
#define	ATOMIC_READ(x)		__atomic_fetch_add(x,0,__ATOMIC_RELAXED)
#define	ATOMIC_ADD(x, y) 	__atomic_fetch_add(x,y,__ATOMIC_RELAXED)
#define ATOMIC_ZERO(x)      __atomic_fetch_and(x,0,__ATOMIC_RELAXED)
#define ATOMIC_SET(x,y)     __atomic_store_n(x,y,__ATOMIC_RELAXED)
#ifndef MAX
#define MAX(a, b)  	(((a) > (b)) ? (a) : (b))
#endif

#ifndef MIN
#define MIN(a, b)  	(((a) < (b)) ? (a) : (b))
#endif

char* tfe_strdup(const char* s);
char *tfe_thread_safe_ctime(const time_t *tp, char *buf, int len);


#define TFE_SET_USED(x)					(void)(x)
#define TFE_PTR_ADD(ptr, x)				((void*)((uintptr_t)(ptr) + (x)))
#define TFE_PTR_SUB(ptr, x)				((void*)((uintptr_t)ptr - (x)))
#define TFE_PTR_DIFF(ptr1, ptr2) 		((uintptr_t)(ptr1) - (uintptr_t)(ptr2))
#define TFE_DIM(x)						(sizeof (x) / sizeof ((x)[0]))

#ifndef TAILQ_FOREACH_SAFE
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)								\
         for ((var) = TAILQ_FIRST((head));										\
             (var) && ((tvar) = TAILQ_NEXT((var), field), 1);					\
            (var) = (tvar))
#endif

#include <stdio.h>

static inline void tfe_hexdump2file(FILE *f, const char * title, const void * buf, unsigned int len)
{
	unsigned int i, out, ofs;
	const unsigned char *data = (const unsigned char *)buf;

#define LINE_LEN 80
	char line[LINE_LEN];    /* space needed 8+16*3+3+16 == 75 */

	fprintf(f, "%s at [%p], len=%u\n", (title)? title  : "  Dump data", data, len);
	ofs = 0;
	while (ofs < len) {
		/* format the line in the buffer, then use printf to output to screen */
		out = snprintf(line, LINE_LEN, "%08X:", ofs);
		for (i = 0; ((ofs + i) < len) && (i < 16); i++)
			out += snprintf(line+out, LINE_LEN - out, " %02X", (data[ofs+i] & 0xff));
		for(; i <= 16; i++)
			out += snprintf(line+out, LINE_LEN - out, " | ");
		for(i = 0; (ofs < len) && (i < 16); i++, ofs++) {
			unsigned char c = data[ofs];
			if ( (c < ' ') || (c > '~'))
				c = '.';
			out += snprintf(line+out, LINE_LEN - out, "%c", c);
		}
		fprintf(f, "%s\n", line);
	}
	fflush(f);

#undef LINE_LEN
}
static inline unsigned char* tfe_hexdump(unsigned char *dst, unsigned char *src, size_t len)
{
    static unsigned char  hex[] = "0123456789abcdef";

    while (len--) {
        *dst++ = hex[*src >> 4];
        *dst++ = hex[*src++ & 0xf];
    }

    return dst;
}
int tfe_scandir(const char *dir, struct dirent ***namelist,
              int(*filter)(const struct dirent *),
              int(*compar)(const void *, const void *));

char *tfe_read_file(const char *filename, size_t *filelen);

const char * tfe_version();
int tfe_decode_base64url(u_char *dst, u_char *src);