//
// Created by Ht on 2021/7/3.
//

#ifndef INC_0_LIBDEVCPP_EFFLOG_H
#define INC_0_LIBDEVCPP_EFFLOG_H

#include <stdio.h>
#include <string.h>
#include <pthread.h>

#ifdef MACOS
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif
#include <string.h>
#include <stdarg.h>
#ifndef WIN32
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>

#else
#include <time.h>
#include <winsock2.h>
#include <WS2tcpip.h>
#define uint32_t unsigned int
#endif

#if defined(__cplusplus)
extern "C" {
#endif

#define MAX_LOG_BUFF_SIZE 65536

static const char *sval_default_efflog_addr_ = "224.0.0.155";
static const int ival_default_efflog_port__ = 63355;

static const char sval_eff_log_sign_[] = "<<EFL::";
static const size_t ival_eff_log_sign_len = sizeof(sval_eff_log_sign_)-1;
static const char sval_eff_log_sign_end_[] = "::EFL>>";
static const size_t ival_eff_log_sign_end_len = sizeof(sval_eff_log_sign_end_)-1;

#ifdef WIN32
static char inited = 0;
#endif

#define efflog_none         "\033[0m"
#define efflog_black        "\033[0;30m"
#define efflog_dark_gray    "\033[1;30m"
#define efflog_blue         "\033[0;34m"
#define efflog_light_blue   "\033[1;34m"
#define efflog_green        "\033[0;32m"
#define efflog_light_green  "\033[1;32m"
#define efflog_cyan         "\033[0;36m"
#define efflog_light_cyan   "\033[1;36m"
#define efflog_red          "\033[0;31m"
#define efflog_light_red    "\033[1;31m"
#define efflog_purple       "\033[0;35m"
#define efflog_light_purple "\033[1;35m"
#define efflog_brown        "\033[0;33m"
#define efflog_yellow       "\033[1;33m"
#define efflog_light_gray   "\033[0;37m"
#define efflog_white        "\033[1;37m"

#define efflog_none_len    4
#define efflog_other_len   7

#define efflog_LOG             efflog_none
#define efflog_WARN            efflog_brown
#define efflog_ERR             efflog_red
#define efflog_Mark            efflog_light_cyan
#define efflog_High            efflog_light_green
#define efflog_Info            efflog_light_blue
#define efflog_ExMark          efflog_light_purple

typedef unsigned char CMBool;
typedef unsigned int CMSize;

#define CMTrue      1
#define CMFalse     0

typedef struct {
    int net_intf_;
    void* sock_addr_;
    char *log_key_;
    unsigned char log_pause_;
    char* log_id_;
    pthread_mutex_t mutex_;
    char log_buff_[MAX_LOG_BUFF_SIZE];
} CMLogIntf;

static char* time_baes_ = NULL;

#ifdef WIN32
static __attribute__((unused))
int efl_gettimeofday(struct timeval* tp, void* tzp)
{
    time_t clock;
    struct tm tm;
    SYSTEMTIME wtm;
    GetLocalTime(&wtm);
    tm.tm_year = wtm.wYear - 1900;
    tm.tm_mon = wtm.wMonth - 1;
    tm.tm_mday = wtm.wDay;
    tm.tm_hour = wtm.wHour;
    tm.tm_min = wtm.wMinute;
    tm.tm_sec = wtm.wSecond;
    tm.tm_isdst = -1;
    clock = mktime(&tm);
    tp->tv_sec = (long)clock;
    tp->tv_usec = wtm.wMilliseconds * 1000;
    return (0);
}
#endif

static __attribute__((unused))
void *EFL_Malloc(uint32_t size){
    return malloc(size);
}

static __attribute__((unused))
void EFL_Free(void *ptr){
    free(ptr);
}

static __attribute__((unused))
const char* randomId(char** val) {
    if(val==NULL) return NULL;
    if(*val==NULL) *val = (char*)(EFL_Malloc(5));
    time_t t = time(NULL);
    srand(t);
    unsigned short v = (unsigned short)(rand()*0x7FFF) % 0xFFF;
#ifndef WIN32
    sprintf(*val, "%04x", v);
#else
    sprintf_s(*val,4,"%04x", v);
#endif
    return *val;
}

static __attribute__((unused))
const char* getTimeStr(char* buff) {
    unsigned int msec, hour, min, sec;
#ifdef WIN32
    struct timeval tv;
    efl_gettimeofday(&tv, NULL);
    msec = (tv.tv_usec / 1000) % 1000;
    sec = tv.tv_sec % 60;
    min = tv.tv_sec % 3600 / 60;
    hour = (tv.tv_sec % 86400 / 3600 + 8) % 24;
#else
    struct timeval tv;
    gettimeofday(&tv,NULL);
    msec = (tv.tv_usec / 1000) % 1000;
    sec = tv.tv_sec % 60;
    min = tv.tv_sec % 3600 / 60;
    hour = (tv.tv_sec % 86400 / 3600 + 8) % 24;
#endif
    if(time_baes_ == NULL) {
        time_baes_ = (char*) malloc(12);
#ifndef WIN32
        time_t time_seconds = time(NULL);
        struct tm t_tm;
        localtime_r(&time_seconds, &t_tm);
        sprintf(time_baes_, "%04d-%02d-%02d", 1900 + t_tm.tm_year, t_tm.tm_mon + 1, t_tm.tm_mday);
#else
        SYSTEMTIME wtm;
        GetLocalTime(&wtm);
        sprintf_s(time_baes_, 12, "%04d-%02d-%02d", wtm.wYear, wtm.wMonth, wtm.wDay);
#endif
    }
#ifndef WIN32
    sprintf(buff, "[%s %02d:%02d:%02d %03d]", time_baes_, hour, min, sec, msec);
#else
    sprintf_s(buff, 28, "[%s %02d:%02d:%02d %03d]", time_baes_, hour, min, sec, msec);
#endif
    return buff;
}

//创建udp 套接字
static __attribute__((unused))
CMLogIntf *EFL_Create_NetIntf(CMLogIntf **intf, const char*key, const char* host, int port) {

#ifdef WIN32
    if (!inited) {
        WSADATA wsaData;
        WORD sockVersion = MAKEWORD(2, 2);
        if (WSAStartup(sockVersion, &wsaData)) //调用Windows Sockets DLL
        {
            printf("Winsock无法初始化!\n");
            WSACleanup();
            return 0;
        }
        inited = 1;
    }
#endif

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock==-1) {
        return NULL;
    }
#ifndef _WIN32
    struct timeval tv_out;
    tv_out.tv_sec = 1000;//每次读取数据等待时间为等待1秒
    tv_out.tv_usec = 0;
    if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out)) < 0) {
#else
        int time_out = 1000; //接收超时
    if (setsockopt(sock, SOL_SOCKET,SO_RCVTIMEO,(char*)&time_out,sizeof(time_out)) < 0) {
#endif
#ifdef _WIN32
        shutdown(sock, SD_BOTH);
#else
        shutdown(sock, SHUT_RDWR);
#endif
        return NULL;
    }

    *intf = (CMLogIntf *)EFL_Malloc(sizeof(CMLogIntf));
    (*intf)->net_intf_ = sock;
    struct sockaddr_in* addr = (struct sockaddr_in*)EFL_Malloc(sizeof(struct sockaddr_in));
    (*intf)->sock_addr_ = (void*)addr;

    memset(addr,0,sizeof(*addr));
    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = inet_addr(host);
    addr->sin_port = htons(port);
#ifndef WIN32
    (*intf)->log_key_ = strdup(key);
#else
    (*intf)->log_key_ = _strdup(key);
#endif
    strcpy((*intf)->log_buff_, sval_eff_log_sign_) ;
    (*intf)->log_id_ = NULL;
    randomId(&(*intf)->log_id_);
    pthread_mutex_init(&(*intf)->mutex_, NULL);
    (*intf)->log_pause_ = 0;
    return *intf;
}

static __attribute__((unused))
void EFL_Release_NetIntf(CMLogIntf *intf) {    
    if(intf==NULL) return;
    int sock = intf->net_intf_;
#ifdef _WIN32
    shutdown(sock, SD_BOTH);
    closesocket(sock);
#else
    shutdown(sock, SHUT_RDWR);
    close(sock);
#endif
    pthread_mutex_destroy(&intf->mutex_);
    EFL_Free(intf->sock_addr_);
    EFL_Free(intf->log_key_);
    EFL_Free(intf);
#ifdef WIN32
    if (inited) {
        WSACleanup();
        inited = 0;
    }
#endif
}

static __attribute__((unused))
void EFL_Send_Diagram(CMLogIntf *intf, char *buff, CMSize size) {
    if(intf==NULL) return;
    struct sockaddr_in* addr = (struct sockaddr_in*)intf->sock_addr_;
    size_t  n, tosend = size;
    char *p = buff;
    pthread_mutex_lock(&intf->mutex_);
    //发送分片
    while((n = sendto(intf->net_intf_, p, tosend,0,(struct sockaddr*)addr,sizeof(*addr))) < tosend && tosend>0) {
        p += n; tosend -= n;
    }
    pthread_mutex_unlock(&intf->mutex_);
}

typedef void (*NetDataHandler)(void* context, char* buff,  CMSize size, const char* ip, int port);

#ifdef WIN32

static __attribute__((unused))
void EFL_Read_Diagram(CMLogIntf *intf, void* context, char* buff,  CMSize size, NetDataHandler handler) {
    if(intf==NULL) return;
    struct sockaddr_in* addr = (struct sockaddr_in*)intf->sock_addr_;
    socklen_t addr_len=0;
    addr_len=sizeof(*addr);
    memset(buff, 0, size);
    int n=recvfrom(intf->net_intf_, buff, size, 0, (struct sockaddr*)addr, &addr_len);
    if( n==-1 ) {
        return;
    }
    int cp = ntohs(addr->sin_port);
    const char* ip = inet_ntoa(addr->sin_addr);
    if(handler!=NULL){
        handler(context, buff, size, ip, cp);
    }
}
#else
static __attribute__((unused))
void EFL_Read_Diagram(CMLogIntf *intf, void* context, char* buff,  CMSize size, NetDataHandler handler) {
    if(intf==NULL) return;
    struct sockaddr_in* addr = (struct sockaddr_in*)intf->sock_addr_;
    socklen_t addr_len=0;
    addr_len=sizeof(*addr);
    memset(buff, 0, size);
    int n=recvfrom(intf->net_intf_, buff, size, 0, (struct sockaddr*)addr, &addr_len);  //每秒钟返回一次
    if( n==-1 ) {
        return;
    }
    int cp = ntohs(addr->sin_port);
    pthread_mutex_lock(&intf->mutex_);
    const char* ip = inet_ntoa(addr->sin_addr);
    pthread_mutex_unlock(&intf->mutex_);
    if(handler!=NULL){
        handler(context, buff, size, ip, cp);
    }
}
#endif

static __attribute__((unused))
CMLogIntf* createLogger(CMLogIntf** logger, const char* key) {
    return EFL_Create_NetIntf(logger, key, sval_default_efflog_addr_, ival_default_efflog_port__);
}

static __attribute__((unused))
CMLogIntf* createUDLogger(CMLogIntf** logger, const char* key, const char* host, int port) {
    return EFL_Create_NetIntf(logger, key, host, port);
}

static __attribute__((unused))
void releaseLogger(CMLogIntf *logger) {
    EFL_Release_NetIntf(logger);
}

#define HANDLE_FMT(fmt, var) \
    va_list ap; \
    va_start(ap, fmt); \
    vsnprintf(var , MAX_LOG_BUFF_SIZE , fmt, ap); \
    va_end(ap)

#define ADD_KEY_BLOCK(var,len,buff) \
    {if(buff!=NULL){\
        size_t blen = strlen((buff));\
        if(blen>0){\
            var[(len)++]='[';\
            memcpy((var)+(len), (buff), blen);\
            len+=blen;\
            var[(len)++]=']';\
    }}}

#define ADD_VAL_BLOCK(var,len,buff) \
    {if(buff!=NULL){\
        size_t blen = strlen((buff));\
        if(blen>0) {\
            memcpy((var)+(len), (buff), blen);\
            len+=blen;\
    }}}

#define ADD_VAL_BLOCK_CONST(var,len,buff,deflen)  \
            memcpy((var)+(len), (buff), deflen);\
            len+=deflen

/*
#define ADD_VAL_BLOCK_CONST(var,len,buff,deflen)  \
    {if(buff!=NULL){\
        if(deflen>0) {\
            memcpy((var)+(len), (buff), deflen);\
            len+=deflen;\
    }}}
*/

static __attribute__((unused))
void __eff_log(CMLogIntf *logger, const char* action, const char* type, const char* fmt, ...) {
    if(logger!=NULL && logger->net_intf_>0){
        //获取固定时间
        char* var  = logger->log_buff_;
        size_t len = ival_eff_log_sign_len; //log 头部长度
        ADD_KEY_BLOCK(var,len,logger->log_id_);
        getTimeStr(var+len);
        len = strlen(var);
        //加入mod段
        ADD_KEY_BLOCK(var,len,logger->log_key_);
        //加入action段
        ADD_KEY_BLOCK(var,len,action);
        ADD_VAL_BLOCK(var,len,type);
        //加入type
        HANDLE_FMT(fmt, var + len);
        //清理type
        len = strlen(var);
        if(type!=NULL) {
            ADD_VAL_BLOCK_CONST(var, len, efflog_none, efflog_none_len);
        }
        ADD_VAL_BLOCK_CONST(var, len, sval_eff_log_sign_end_, ival_eff_log_sign_end_len);
        var[len] = 0;
        EFL_Send_Diagram(logger, var, len);
    }
}

#define EFLog(logger,action,pChr,...)  __eff_log((logger),(action),NULL,pChr, ##__VA_ARGS__)
#define EFWarn(logger,action,pChr,...)  __eff_log((logger),(action),efflog_WARN,pChr, ##__VA_ARGS__)
#define EFErr(logger,action,pChr,...)  __eff_log((logger),(action),efflog_ERR,pChr, ##__VA_ARGS__)
#define EFMark(logger,action,pChr,...)  __eff_log((logger),(action),efflog_Mark,pChr, ##__VA_ARGS__)
#define EFHigh(logger,action,pChr,...)  __eff_log((logger),(action),efflog_High,pChr, ##__VA_ARGS__)
#define EFInfo(logger,action,pChr,...)  __eff_log((logger),(action),efflog_Info,pChr, ##__VA_ARGS__)
#define EFExMark(logger,action,pChr,...)  __eff_log((logger),(action),efflog_ExMark,pChr, ##__VA_ARGS__)

#if defined(__cplusplus)
}
#endif

#endif //INC_0_LIBDEVCPP_EFFLOG_H
