#pragma once
#include <trantor/utils/NonCopyable.h>
#include <trantor/utils/Date.h>
#include <trantor/exports.h>
#include <thread>
#include <mutex>
#include <string>
#include <condition_variable>
#include <sstream>
#include <memory>
#include <queue>
namespace trantor
{
using StringPtr = std::shared_ptr<std::string>;
using StringPtrQueue = std::queue<StringPtr>;
class TRANTOR_EXPORT AsyncFileLogger : NonCopyable
{
  public:
    void output(const char *msg, const uint64_t len);
    void flush();
    void startLogging();
    void setFileSizeLimit(uint64_t limit)
    {
        sizeLimit_ = limit;
    }
    void setMaxFiles(size_t maxFiles)
    {
        maxFiles_ = maxFiles;
    }
    void setSwitchOnLimitOnly(bool flag = true)
    {
        switchOnLimitOnly_ = flag;
    }
    void setFileName(const std::string &baseName,
                     const std::string &extName = ".log",
                     const std::string &path = "./")
    {
        fileBaseName_ = baseName;
        extName[0] == '.' ? fileExtName_ = extName
                          : fileExtName_ = std::string(".") + extName;
        filePath_ = path;
        if (filePath_.length() == 0)
            filePath_ = "./";
        if (filePath_[filePath_.length() - 1] != '/')
            filePath_ = filePath_ + "/";
    }
    ~AsyncFileLogger();
    AsyncFileLogger();
  protected:
    std::mutex mutex_;
    std::condition_variable cond_;
    StringPtr logBufferPtr_;
    StringPtr nextBufferPtr_;
    StringPtrQueue writeBuffers_;
    StringPtrQueue tmpBuffers_;
    void writeLogToFile(const StringPtr buf);
    std::unique_ptr<std::thread> threadPtr_;
    bool stopFlag_{false};
    void logThreadFunc();
    std::string filePath_{"./"};
    std::string fileBaseName_{"trantor"};
    std::string fileExtName_{".log"};
    uint64_t sizeLimit_{20 * 1024 * 1024};
    bool switchOnLimitOnly_{false};  
    size_t maxFiles_{0};
    class LoggerFile : NonCopyable
    {
      public:
        LoggerFile(const std::string &filePath,
                   const std::string &fileBaseName,
                   const std::string &fileExtName,
                   bool switchOnLimitOnly = false,
                   size_t maxFiles = 0);
        ~LoggerFile();
        void writeLog(const StringPtr buf);
        void open();
        void switchLog(bool openNewOne);
        uint64_t getLength();
        explicit operator bool() const
        {
            return fp_ != nullptr;
        }
        void flush();
      protected:
        void initFilenameQueue();
        void deleteOldFiles();
        FILE *fp_{nullptr};
        Date creationDate_;
        std::string fileFullName_;
        std::string filePath_;
        std::string fileBaseName_;
        std::string fileExtName_;
        static uint64_t fileSeq_;
        bool switchOnLimitOnly_{false};  
        size_t maxFiles_{0};
        std::deque<std::string> filenameQueue_;
    };
    std::unique_ptr<LoggerFile> loggerFilePtr_;
    uint64_t lostCounter_{0};
    void swapBuffer();
};
}  