#pragma once
#include <drogon/HttpRequest.h>
#include <drogon/HttpResponse.h>
#include <drogon/plugins/Plugin.h>
#include <trantor/utils/AsyncFileLogger.h>
#include <vector>
namespace drogon
{
namespace plugin
{
class DROGON_EXPORT AccessLogger : public drogon::Plugin<AccessLogger>
{
  public:
    AccessLogger()
    {
    }
    void initAndStart(const Json::Value &config) override;
    void shutdown() override;
  private:
    trantor::AsyncFileLogger asyncFileLogger_;
    int logIndex_{0};
    bool useLocalTime_{true};
    bool showMicroseconds_{true};
    bool useCustomTimeFormat_{false};
    std::string timeFormat_;
    static bool useRealIp_;
    std::regex exemptRegex_;
    bool regexFlag_{false};
    using LogFunction = std::function<void(trantor::LogStream &,
                                           const drogon::HttpRequestPtr &,
                                           const drogon::HttpResponsePtr &)>;
    std::vector<LogFunction> logFunctions_;
    void logging(trantor::LogStream &stream,
                 const drogon::HttpRequestPtr &req,
                 const drogon::HttpResponsePtr &resp);
    void createLogFunctions(std::string format);
    LogFunction newLogFunction(const std::string &placeholder);
    std::map<std::string, LogFunction> logFunctionMap_;
    static void outputReqPath(trantor::LogStream &,
                              const drogon::HttpRequestPtr &,
                              const drogon::HttpResponsePtr &);
    static void outputReqQuery(trantor::LogStream &,
                               const drogon::HttpRequestPtr &,
                               const drogon::HttpResponsePtr &);
    static void outputReqURL(trantor::LogStream &,
                             const drogon::HttpRequestPtr &,
                             const drogon::HttpResponsePtr &);
    static void outputVersion(trantor::LogStream &,
                              const drogon::HttpRequestPtr &,
                              const drogon::HttpResponsePtr &);
    static void outputReqLine(trantor::LogStream &,
                              const drogon::HttpRequestPtr &,
                              const drogon::HttpResponsePtr &);
    void outputDate(trantor::LogStream &,
                    const drogon::HttpRequestPtr &,
                    const drogon::HttpResponsePtr &) const;
    void outputReqDate(trantor::LogStream &,
                       const drogon::HttpRequestPtr &,
                       const drogon::HttpResponsePtr &) const;
    static void outputRemoteAddr(trantor::LogStream &,
                                 const drogon::HttpRequestPtr &,
                                 const drogon::HttpResponsePtr &);
    static void outputLocalAddr(trantor::LogStream &,
                                const drogon::HttpRequestPtr &,
                                const drogon::HttpResponsePtr &);
    static void outputReqLength(trantor::LogStream &,
                                const drogon::HttpRequestPtr &,
                                const drogon::HttpResponsePtr &);
    static void outputRespLength(trantor::LogStream &,
                                 const drogon::HttpRequestPtr &,
                                 const drogon::HttpResponsePtr &);
    static void outputMethod(trantor::LogStream &,
                             const drogon::HttpRequestPtr &,
                             const drogon::HttpResponsePtr &);
    static void outputThreadNumber(trantor::LogStream &,
                                   const drogon::HttpRequestPtr &,
                                   const drogon::HttpResponsePtr &);
    static void outputReqHeader(trantor::LogStream &stream,
                                const drogon::HttpRequestPtr &req,
                                const std::string &headerName);
    static void outputReqCookie(trantor::LogStream &stream,
                                const drogon::HttpRequestPtr &req,
                                const std::string &cookie);
    static void outputRespHeader(trantor::LogStream &stream,
                                 const drogon::HttpResponsePtr &resp,
                                 const std::string &headerName);
    static void outputStatusString(trantor::LogStream &,
                                   const drogon::HttpRequestPtr &,
                                   const drogon::HttpResponsePtr &);
    static void outputStatusCode(trantor::LogStream &,
                                 const drogon::HttpRequestPtr &,
                                 const drogon::HttpResponsePtr &);
    static void outputProcessingTime(trantor::LogStream &,
                                     const drogon::HttpRequestPtr &,
                                     const drogon::HttpResponsePtr &);
    static void outputRespContentType(trantor::LogStream &,
                                      const drogon::HttpRequestPtr &,
                                      const drogon::HttpResponsePtr &);
};
}  
}  