//
//  CommonLog.cpp
//
//  Created by 改革丰富 on 2017/6/26.
//  Copyright © 2017年 改革丰富. All rights reserved.
//  email: 1026397500@qq.com
//  感谢您的使用，欢迎反馈BUG及改进意见到本人邮箱
//

#include "CommonLog.h"
#include "AppSetting.h"

namespace mfs
{
    
void Log::flush()
{
    
#if (ANDROIDJNI==1)
    
    if(_type == LogAndroid)
    {
        if(_level == LogDebug)
        {
            __android_log_print(ANDROID_LOG_DEBUG, "mfs::LogInfo", "%s", _outStringStream.str().c_str());
        }
        else if(_level == LogInfo)
        {
            __android_log_print(ANDROID_LOG_INFO, "mfs::LogInfo", "%s", _outStringStream.str().c_str());
        }
        else if(_level == LogError)
        {
            __android_log_print(ANDROID_LOG_ERROR, "mfs::LogError", "%s", _outStringStream.str().c_str());
        }
    }
    else if(_type == LogConsole)
        
#else
        
    if(_type == LogConsole)
        
#endif
        
    {
        
#ifdef LOG_THREAD_SAFE
        
        std::unique_lock<std::mutex> lock(_mutex);
        
#endif
        if(_level == LogError)
        {
            std::cerr << _outStringStream.str();
        }
        else
        {
            (std::cout << _outStringStream.str()).flush();
        }
    }
    _outStringStream.str("");
}

#ifdef LOG_THREAD_SAFE
    
std::mutex Log::_mutex;
std::atomic_int Log::currentLevel = {0}};

#else

LogLevel Log::currentLevel = LogDebug;

#endif

#if (ANDROIDJNI==1)

thread_local Log logDebugJni(LogAndroid, LogDebug);
thread_local Log logJni(LogAndroid, LogInfo);
thread_local Log logErrJni(LogAndroid, LogError);

#endif

thread_local Log logVerboseConsole(LogConsole, LogVerbose);
thread_local Log logDebugConsole(LogConsole, LogDebug);
thread_local Log logConsole(LogConsole, LogInfo);
thread_local Log logWarnConsole(LogConsole, LogWarn);
thread_local Log logErrConsole(LogConsole, LogError);

EndLog logEndl;


    static std::chrono::system_clock::time_point _logTimeStart;
    static fs::path _logfile;

    bool checkLogTime()
    {
        auto seconds = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - _logTimeStart).count();
        return seconds>60*AppSetting::Instance()->logMaxTime;
    }
    bool checkLogSize()
    {
        try {
            auto fsize = fs::file_size(_logfile);
            return fsize>AppSetting::Instance()->logMaxSize*1000*1000;
        } catch(fs::filesystem_error& e) {
            LOG_ERROR << e.what() << LOG_ENDL;
            return false;
        } ;
    }
    int openLogFile(const std::string& file)
    {
        LOG_DEBUG << "openLogFile" <<LOG_ENDL;
        _logTimeStart = std::chrono::system_clock::now();

        auto logFileName = file;
        int logFd = open(logFileName.c_str(), O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
        if(logFd == -1)
        {
            LOG_ERROR << "create log failed! "<< file << LOG_ENDL;
            return 1;
        }
#ifdef RELEASE
        dup2(logFd, STDOUT_FILENO);
        dup2(logFd, STDERR_FILENO);
#endif
        close(logFd);
        return 0;
    }
    
    bool createdirectories(const std::string& path)
    {		
        int len = path.length();
        char tmpDirPath[512] = { 0 };
        for (int i = 0; i < len; i++)
        {
            tmpDirPath[i] = path[i];
            if (tmpDirPath[i] == '\\' || tmpDirPath[i] == '/')
            {
                if (access(tmpDirPath, 0) == -1)
                {
                    int ret = mkdir(tmpDirPath,S_IRWXU|S_IRWXG|S_IRWXO);
                    if (ret == -1) return false;
                }
            }
        }
        return true;
    }

    void clearLogfile(const fs::path& file)
    {
        auto nowtime = std::chrono::system_clock::now();

        auto fileDst = file;
        fileDst.replace_extension("");
        if (!fs::exists(fileDst))
        {
            return;
        }
        
        for (auto& fe : fs::directory_iterator(fileDst))
        {
            auto fp = fe.path();
            auto temp = fp.filename();
            auto fileName = temp.replace_extension().string();

            auto pos = fileName.find("_");
            if (pos != fileName.npos)
            {
                auto timestr = fileName.substr(pos+1, 13);
                struct tm tm_ptr;
                strptime(timestr.c_str(), "%y%m%d_%H%M%S", &tm_ptr);
                tm_ptr.tm_isdst = -1;
                auto timet2 = mktime(&tm_ptr);
                auto ftime = std::chrono::system_clock::from_time_t(timet2);
                auto seconds = std::chrono::duration_cast<std::chrono::seconds>(nowtime-ftime).count();
                if (seconds > 60*60*AppSetting::Instance()->logHoldTime)
                {
                    // LOG_DEBUG << "remove time out file:"<<fp<< LOG_ENDL;
                    fs::remove(fp);
                }
            }
        }

    }
    
    void saveLogFile(const fs::path& file)
    {
        // createdirectories(file);
        if (!fs::exists(file) || fs::file_size(file)<=0)
        {
            return;
        }
        
        auto nowtime = std::chrono::system_clock::now();
        auto nowtimeT = std::chrono::system_clock::to_time_t(nowtime);
        auto localtm = std::localtime(&nowtimeT);
        char timeStr[30] = {0};
        strftime(timeStr, 30, "%y%m%d_%H%M%S", localtm);

        auto fileDst = file;//std::filesystem::path::concat(file, file.filename().string()+timeStr);
        fileDst.replace_extension("");
        fs::create_directories(fileDst);
        fileDst /= file.filename().replace_extension("").string()+"_"+timeStr+".log";

        auto iret = rename(file.c_str(), fileDst.c_str());
        if (iret!=0)
        {
            LOG_ERROR<<"log rename error:"<<iret<<"; "<<file<<" to "<< fileDst<<LOG_ENDL;
        }
    }
    void setLogfile(const std::string& fileName)
    {
        _logfile = AppSetting::Instance()->logDir;
        fs::create_directories(AppSetting::Instance()->logDir);
        _logfile /= fileName+".log";
        saveLogFile(_logfile);
        openLogFile(_logfile);
    }
    void autoLogSubpackage()
    {
#ifdef RELEASE
// #else
        if(checkLogTime() || checkLogSize()){
            saveLogFile(_logfile);
            openLogFile(_logfile);
        }
        clearLogfile(_logfile);
#endif
    }
}
