#pragma once

#include <string>
#include <map>
#include <memory>
#include <pthread.h>
#include "mongoose.h"
#include "LockQueue.hpp"
#include <time.h>
#include <functional>
#include <sstream>

using namespace std;

#ifndef _WIN64
#define Logi(fmt, ...) {time_t timep; time(&timep); struct tm *p = localtime(&timep); printf("%4d-%02d-%02d %02d:%02d:%02d [%llu][I] ", 1900+p->tm_year, 1+p->tm_mon, p->tm_mday, p-> tm_hour, p->tm_min, p->tm_sec, (unsigned long long)pthread_self());printf(fmt, ##__VA_ARGS__);printf("\n");fflush(stdout);}
#define Logw(fmt, ...) {time_t timep; time(&timep); struct tm *p = localtime(&timep); printf("%4d-%02d-%02d %02d:%02d:%02d [%llu][W] ", 1900+p->tm_year, 1+p->tm_mon, p->tm_mday, p-> tm_hour, p->tm_min, p->tm_sec, (unsigned long long)pthread_self());printf(fmt, ##__VA_ARGS__);printf("\n");fflush(stdout);}
#define Loge(fmt, ...) {time_t timep; time(&timep); struct tm *p = localtime(&timep); printf("%4d-%02d-%02d %02d:%02d:%02d [%llu][E] ", 1900+p->tm_year, 1+p->tm_mon, p->tm_mday, p-> tm_hour, p->tm_min, p->tm_sec, (unsigned long long)pthread_self());printf(fmt, ##__VA_ARGS__);printf("\n");fflush(stdout);}
#else
#define Logi(fmt, ...) {time_t timep; time(&timep); struct tm *p = localtime(&timep); printf("%4d-%02d-%02d %02d:%02d:%02d [I] ", 1900+p->tm_year, 1+p->tm_mon, p->tm_mday, p-> tm_hour, p->tm_min, p->tm_sec );printf(fmt, ##__VA_ARGS__);printf("\n");fflush(stdout);}
#define Logw(fmt, ...) {time_t timep; time(&timep); struct tm *p = localtime(&timep); printf("%4d-%02d-%02d %02d:%02d:%02d [W] ", 1900+p->tm_year, 1+p->tm_mon, p->tm_mday, p-> tm_hour, p->tm_min, p->tm_sec );printf(fmt, ##__VA_ARGS__);printf("\n");fflush(stdout);}
#define Loge(fmt, ...) {time_t timep; time(&timep); struct tm *p = localtime(&timep); printf("%4d-%02d-%02d %02d:%02d:%02d [E] ", 1900+p->tm_year, 1+p->tm_mon, p->tm_mday, p-> tm_hour, p->tm_min, p->tm_sec );printf(fmt, ##__VA_ARGS__);printf("\n");fflush(stdout);}

#endif
#define W_HTTP_GET      (1 << 0)
#define W_HTTP_POST     (1 << 1)
#define W_HTTP_PUT      (1 << 2)
#define W_HTTP_DELETE   (1 << 3)
#define W_HTTP_HEAD     (1 << 4)
#define W_HTTP_ALL      0xFF

using HttpChunkQueue = LockQueue<string *>;
using HttpSendQueue = LockQueue<string *>;

struct HttpReqMsg
{
   mg_connection *httpConnection = nullptr;
   string method; // GET POST PUT DELETE
   string uri;
   map<string, string> querys; // the params in uri
   string proto; // http version
   // the params in header, all key letters are converted to lowercase, eg "Content-Length" change to "content-length"
   map<string, string> headers;
   string body;
   int64_t totalBodySize;
   shared_ptr<HttpChunkQueue> chunkQueue;
   shared_ptr<HttpSendQueue> sendQueue;
   int64_t recvChunkSize = 0;
   bool finishRecvChunk = false;
   bool isKeepingAlive = false;
   int64_t lastKeepAliveTime = 0;
};

using HttpCbFun = std::function<void(shared_ptr<HttpReqMsg> &)>;
using HttpFilterFun = std::function<bool(shared_ptr<HttpReqMsg> &)>;

class IHttpServer
{
public:
    IHttpServer(){}
    virtual ~IHttpServer(){}
    virtual bool init(int maxEventThreadNum) = 0;
    virtual bool startHttp(string strip, int port) = 0;
    virtual bool startHttps(int port, string certPath, string keyPath) = 0;
    virtual bool stop() = 0;
    virtual bool run() = 0;
    virtual bool isRunning() = 0;
    virtual void addHttpApi(const string &uri, HttpCbFun fun, int httpMethods) = 0;
    virtual void addChunkHttpApi(const string &uri, HttpCbFun fun, int httpMethods) = 0;
    virtual void setHttpFilter(HttpFilterFun filter) = 0;
    virtual void forceCloseHttpConnection(shared_ptr<HttpReqMsg> httpMsg) = 0;
    virtual void httpReplyJson(shared_ptr<HttpReqMsg> httpMsg, int httpCode, string head, string body) = 0;
    virtual void addSendMsgToQueue(shared_ptr<HttpReqMsg> httpMsg, const char* data, int len) = 0;
    virtual void addSendMsgToQueue(shared_ptr<HttpReqMsg> httpMsg, string *sendMsg) = 0;
    virtual string formJsonBody(int code, string message, string body="") = 0;
    virtual bool isClientDisconnect(shared_ptr<HttpReqMsg> httpMsg) = 0;
    virtual shared_ptr<string> deQueueHttpChunk(shared_ptr<HttpReqMsg> httpMsg) = 0;
    virtual bool addStaticWebDir(const string &dir, const string &header = "") = 0;
};