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

#ifndef _COMMON_LOG_H_
#define _COMMON_LOG_H_

#include "MFSPublic.h"

namespace mfs
{
    
enum LogType
{
    
#if (ANDROIDJNI==1)
    
    LogAndroid,
    
#endif
    
    LogConsole
};

enum LogLevel
{
    LogVerbose=0,
    LogDebug,
    LogInfo,
    LogWarn,
    LogError
};

struct EndLog
{};

class Log
{
public:
    Log(LogType type, LogLevel level): _type(type), _level(level)
    {}

    ~Log()
    {}

    static LogLevel getCurrentLevel(LogLevel level)
    {
        return currentLevel;
    }

    static void setCurrentLevel(LogLevel level)
    {
        currentLevel = level;
    }

    std::string filtersFileName(const std::string& filePath) const
    {
        if(currentLevel > _level)
        {
            return "";
        }
        auto pos = filePath.rfind('/');
        if(pos != std::string::npos)
        {
            return filePath.substr(pos + 1);
        }
        return filePath;
    }

    void flush();
    
    template <typename T>
    Log& operator<<(const T& value)
    {
        if(currentLevel <= _level)
        {
            _outStringStream << value;
        }
        return *this;
    }
    
    template <typename T>
    Log& operator<<(const std::vector<T>& data)
    {
        if(currentLevel <= _level)
        {
            _outStringStream << std::hex;
            _outStringStream << "[ ";
            for(auto element : data)
            {
                _outStringStream << static_cast<int>(element) << " ";
            }
            _outStringStream << "]";
            _outStringStream << std::dec;
        }
        return *this;
    }
    
    Log& operator<<(const EndLog& value)
    {
        if(currentLevel <= _level)
        {
            if(_type == LogConsole)
            {
                _outStringStream << "\n";
            }
            flush();
        }
        return *this;
    }
    
    Log(const Log &) = delete;
    
    Log& operator=(const Log&) = delete;
    
private:
    std::ostringstream _outStringStream;
    
#if (ANDROIDJNI==1)
    
    LogType _type = LogAndroid;
    
#else
    
    LogType _type = LogConsole;
    
#endif
    
    LogLevel _level = LogInfo;
    
    
#ifdef LOG_THREAD_SAFE
    
    static std::mutex _mutex;
    static std::atomic_int currentLevel;
    
#else

    static LogLevel currentLevel;

#endif
};

#if (ANDROIDJNI==1)

extern thread_local Log logDebugJni;
extern thread_local Log logJni;
extern thread_local Log logErrJni;

#endif

extern thread_local Log logVerboseConsole;
extern thread_local Log logDebugConsole;
extern thread_local Log logConsole;
extern thread_local Log logWarnConsole;
extern thread_local Log logErrConsole;

extern EndLog logEndl;

inline std::string filtersFileName(const std::string& filePath)
{
    auto pos = filePath.rfind('/');
    if(pos != std::string::npos)
    {
        return filePath.substr(pos + 1);
    }
    return filePath;
}

// 
inline std::string getTime()
{
    struct timeval tv;
	gettimeofday(&tv, nullptr);
	return std::to_string((long long)tv.tv_sec * 1000 + (long long)tv.tv_usec / 1000);
}

// 20-07-31 11:21:16.797
inline std::string getTimeStr()
{
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    auto t = std::chrono::system_clock::to_time_t(now);
    char timeStr[30] = {0};
    strftime(timeStr, 30, "%C-%m-%d %T", std::localtime(&t));
    char milliTime[50] = {0};
    long millisecond = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count() % 1000;
    sprintf(milliTime, "%s.%03ld", timeStr, millisecond);
    return milliTime;
}
inline std::string getTimeFileStr()
{
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    auto t = std::chrono::system_clock::to_time_t(now);
    char timeStr[30] = {0};
    strftime(timeStr, 30, "%y%m%d_%H%M%S", std::localtime(&t));
    char milliTime[50] = {0};
    long millisecond = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count() % 1000;
    sprintf(milliTime, "%s_%03ld", timeStr, millisecond);
    return milliTime;
}

#define RED   "\x1B[31m"
#define RESET "\x1B[0m"
#define YELLOW "\x1B[33m"

#define LOG_CURRENT_LEVEL mfs::Log::getCurrentLevel()
#define LOG_SET_CURRENT_LEVEL(level) mfs::Log::setCurrentLevel(level)

#if (ANDROIDJNI==1)
    
#define LOG_DEBUG mfs::logDebugJni<<"DEBUG ["<<mfs::logDebugJni.filtersFileName(__FILE__)<<":"<<__LINE__<<"] "<<__FUNCTION__<<"(): >>>> "
#define LOG_INFO mfs::logJni<<" INFO ["<< mfs::logJni.filtersFileName(__FILE__)<<":"<<__LINE__<<"] "<<__FUNCTION__<<"(): >>>> "
#define LOG_ERROR mfs::logErrJni<<"ERROR ["<< mfs::logErrJni.filtersFileName(__FILE__)<<":"<<__LINE__<<"] "<<__FUNCTION__<<"(): >>>> "
    
#else
    
#define LOG_VERBOSE mfs::logDebugConsole << RESET << "V " << mfs::getTimeStr() <<" ["<<mfs::logDebugConsole.filtersFileName(__FILE__)<<":"<<__LINE__<<"] "<<__FUNCTION__<<"(): >>>> "
#define LOG_DEBUG mfs::logDebugConsole << RESET << "D " << mfs::getTimeStr() <<" ["<<mfs::logDebugConsole.filtersFileName(__FILE__)<<":"<<__LINE__<<"] "<<__FUNCTION__<<"(): >>>> "
#define LOG_INFO mfs::logConsole << RESET << "I " << mfs::getTimeStr() <<" ["<<mfs::logConsole.filtersFileName(__FILE__)<<":"<<__LINE__<<"] "<<__FUNCTION__<<"(): >>>> "
#define LOG_ERROR mfs::logErrConsole << RED "E " << mfs::getTimeStr() <<" ["<<mfs::logErrConsole.filtersFileName(__FILE__)<<":"<<__LINE__<<"] "<<__FUNCTION__<<"(): >>>> " RESET
#define LOG_WARN mfs::logErrConsole << YELLOW "W " << mfs::getTimeStr() <<" ["<<mfs::logErrConsole.filtersFileName(__FILE__)<<":"<<__LINE__<<"] "<<__FUNCTION__<<"(): >>>> " RESET
 
#define LOG_VERBOSE2 mfs::logVerboseConsole << RESET "V " << mfs::getTimeStr() <<" "
#define LOG_DEBUG2 mfs::logDebugConsole << RESET "D " << mfs::getTimeStr() <<" "
#define LOG_INFO2 mfs::logConsole << RESET "I " << mfs::getTimeStr() <<" "
#define LOG_WARN2 mfs::logErrConsole << YELLOW "W " << mfs::getTimeStr() <<" " RESET
#define LOG_ERROR2 mfs::logErrConsole << RED "E " << mfs::getTimeStr() <<" " RESET
 
#endif
    
#define LOG_ENDL mfs::logEndl

#define LOG_START LOG_DEBUG << "start:"<<this << LOG_ENDL
#define LOG_FREE LOG_DEBUG << "free:"<<this << LOG_ENDL
#define LOG_ERRNO LOG_DEBUG << RED "LOG_ERRNO: " << errno << " " << strerror(errno) << RESET << LOG_ENDL

#define FORWORT_ASSERT(condition) do{if(!(condition)) {LOG_ERROR<<"FORWORT_ASSERT"<<LOG_ENDL;std::terminate();}}while(0)
#define FORWORT_ASSERT_RETURN(condition) do{if(!(condition)) {LOG_ERROR<<"FORWORT_ASSERT_RETURN: -1"<<LOG_ENDL;return -1;}}while(0)
#define FORWORT_ASSERT_RETURN_NO_LOG(condition) do{if(!(condition)) {return -1;}}while(0)
#define FORWORT_ASSERT_RETURN_LOG_MSG(condition, msg) do{if(!(condition)) {LOG_ERROR<<msg<<LOG_ENDL;return -1;}}while(0)
#define FORWORT_ASSERT_RETURN_LOG_ERR(condition) do{if(!(condition)) {LOG_ERRNO;return -1;}}while(0)

#define FORWORT_ASSERT_BREAK_LOG_MSG(condition,msg) if(!(condition)) {LOG_ERROR<<msg<<LOG_ENDL;break;}

#define FORWORT_ERRNO_ASSERT(value) do{if((value) == -1) {LOG_ERROR<<"FORWORT_ERRNO_ASSERT: "<<errno<<" "<<strerror(errno)<<LOG_ENDL;std::terminate();}}while(0)
#define FORWORT_ERRNO_RETURN(value) do{if((value) == -1) {LOG_ERROR<<"FORWORT_ERRNO_RETURN: -1 "<<errno<<" "<<strerror(errno)<<LOG_ENDL;return -1;}}while(0)
#define FORWORT_ERRNO_LOG(value) do{if((value) == -1) {LOG_ERROR<<"FORWORT_ERRNO_LOG: "<<errno<<" "<<strerror(errno)<<LOG_ENDL;}}while(0)

#define FORWORT_ERRNO_RETURN_CONDITION(condition) do{if(!(condition)) {LOG_ERROR<<"FORWORT_ERRNO_RETURN_CONDITION: "<<errno<<" "<<strerror(errno)<<LOG_ENDL;return -1;}}while(0)

#define logd(s) alogd((s), __FILE__,__LINE__,__FUNCTION__)
#define loge(s) aloge((s), __FILE__,__LINE__,__FUNCTION__)
#define logv(s) alogv((s), __FILE__,__LINE__,__FUNCTION__)

extern void setLogfile(const std::string& file);
// extern int openLogFile(const std::string& file);
// extern bool checkLogTime();
extern bool createdirectories(const std::string& path);
extern void autoLogSubpackage();

}

#endif
