// ------------------------------------------------------------------------------------------------
#include "Logger.hpp"
#include "Base/Utility.hpp"

// ------------------------------------------------------------------------------------------------
#include <ctime>
#include <cerrno>
#include <cstring>
#include <cstdarg>

// ------------------------------------------------------------------------------------------------
#include <sqrat.h>

// ------------------------------------------------------------------------------------------------
#ifdef SQMOD_OS_WINDOWS

// ------------------------------------------------------------------------------------------------
#include <Windows.h>

// ------------------------------------------------------------------------------------------------
namespace {

/* ------------------------------------------------------------------------------------------------
 * Common windows colors.
*/
enum
{
    LC_NORMAL           = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
    LC_WHITE            = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
    LC_GRAY             = FOREGROUND_INTENSITY,
    LC_LIGHT_BLUE       = FOREGROUND_INTENSITY | FOREGROUND_BLUE,
    LC_DARK_BLUE        = FOREGROUND_BLUE,
    LC_LIGHT_GREEN      = FOREGROUND_INTENSITY | FOREGROUND_GREEN,
    LC_DARK_GREEN       = FOREGROUND_GREEN,
    LC_LIGHT_CYAN       = FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE,
    LC_DARK_CYAN        = FOREGROUND_GREEN | FOREGROUND_BLUE,
    LC_LIGHT_YELLOW     = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN,
    LC_DARK_YELLOW      = FOREGROUND_RED | FOREGROUND_GREEN,
    LC_LIGHT_RED        = FOREGROUND_INTENSITY | FOREGROUND_RED,
    LC_DARK_RED         = FOREGROUND_RED,
    LC_LIGHT_MAGENTA    = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE,
    LC_DARK_MAGENTA     = FOREGROUND_RED | FOREGROUND_BLUE
};

/* ------------------------------------------------------------------------------------------------
 * Logging colors.
*/
enum
{
    LC_DBG  = LC_LIGHT_BLUE,
    LC_USR  = LC_GRAY,
    LC_SCS  = LC_LIGHT_GREEN,
    LC_INF  = LC_LIGHT_CYAN,
    LC_WRN  = LC_LIGHT_YELLOW,
    LC_ERR  = LC_LIGHT_RED,
    LC_FTL  = LC_LIGHT_MAGENTA
};

/* ------------------------------------------------------------------------------------------------
 * Identify the associated message color.
*/
inline WORD GetLevelColor(BYTE level)
{
    switch (level)
    {
        case SqMod::LOGL_DBG: return LC_DBG;
        case SqMod::LOGL_USR: return LC_USR;
        case SqMod::LOGL_SCS: return LC_SCS;
        case SqMod::LOGL_INF: return LC_INF;
        case SqMod::LOGL_WRN: return LC_WRN;
        case SqMod::LOGL_ERR: return LC_ERR;
        case SqMod::LOGL_FTL: return LC_FTL;
        default: return LC_NORMAL;
    }
}

} // Namespace::

#endif // SQMOD_OS_WINDOWS

// ------------------------------------------------------------------------------------------------
namespace SqMod {

/* ------------------------------------------------------------------------------------------------
 * Identify the message prefix.
*/
static inline CCStr GetLevelTag(Uint8 level)
{
    switch (level)
    {
        case LOGL_DBG:  return "[DBG]";
        case LOGL_USR:  return "[USR]";
        case LOGL_SCS:  return "[SCS]";
        case LOGL_INF:  return "[INF]";
        case LOGL_WRN:  return "[WRN]";
        case LOGL_ERR:  return "[ERR]";
        case LOGL_FTL:  return "[FTL]";
        default:        return "[UNK]";
    }
}

#ifndef SQMOD_OS_WINDOWS

/* ------------------------------------------------------------------------------------------------
 * Identify the message prefix and color.
*/
static inline CCStr GetColoredLevelTag(Uint8 level)
{
    switch (level)
    {
        case LOGL_DBG:  return "\033[21;94m[DBG]\033[0m";
        case LOGL_USR:  return "\033[21;37m[USR]\033[0m";
        case LOGL_SCS:  return "\033[21;92m[SCS]\033[0m";
        case LOGL_INF:  return "\033[21;96m[INF]\033[0m";
        case LOGL_WRN:  return "\033[21;93m[WRN]\033[0m";
        case LOGL_ERR:  return "\033[21;91m[ERR]\033[0m";
        case LOGL_FTL:  return "\033[21;95m[FTL]\033[0m";
        default:        return "\033[21;0m[UNK]\033[0m";
    }
}

/* ------------------------------------------------------------------------------------------------
 * Identify the message prefix and color.
*/
static inline CCStr GetColoredLevelTagDim(Uint8 level)
{
    switch (level)
    {
        case LOGL_DBG:  return "\033[21;94m[DBG]\033[2m";
        case LOGL_USR:  return "\033[21;37m[USR]\033[2m";
        case LOGL_SCS:  return "\033[21;92m[SCS]\033[2m";
        case LOGL_INF:  return "\033[21;96m[INF]\033[2m";
        case LOGL_WRN:  return "\033[21;93m[WRN]\033[2m";
        case LOGL_ERR:  return "\033[21;91m[ERR]\033[2m";
        case LOGL_FTL:  return "\033[21;95m[FTL]\033[2m";
        default:        return "\033[21;0m[UNK]\033[0m";
    }
}

#endif // SQMOD_OS_WINDOWS

/* ------------------------------------------------------------------------------------------------
 * Output a logging message to the console window.
*/
static inline void OutputConsoleMessage(Uint8 level, bool sub, CCStr tms, CCStr msg)
{
#ifdef SQMOD_OS_WINDOWS
    HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO csb_state;
    GetConsoleScreenBufferInfo(hstdout, &csb_state);
    SetConsoleTextAttribute(hstdout, GetLevelColor(level));
    if (tms)
    {
        std::printf("%s %s ", GetLevelTag(level), tms);
    }
    else
    {
        std::printf("%s ", GetLevelTag(level));
    }
    SetConsoleTextAttribute(hstdout, sub ? LC_NORMAL : LC_WHITE);
    std::printf("%s\n", msg);
    SetConsoleTextAttribute(hstdout, csb_state.wAttributes);
#else
    if (tms)
    {
        std::printf("%s %s %s\033[0m\n",
            sub ? GetColoredLevelTagDim(level) : GetColoredLevelTag(level), tms, msg);
    }
    else
    {
        std::printf("%s %s\033[0m\n",
            sub ? GetColoredLevelTagDim(level) : GetColoredLevelTag(level), msg);
    }
#endif // SQMOD_OS_WINDOWS
}

/* ------------------------------------------------------------------------------------------------
 * Identify the associated message color.
*/
static inline CCStr GetTimeStampStr()
{
    static CharT tmbuff[80];
    std::time_t t = std::time(nullptr);
    std::strftime(tmbuff, sizeof(tmbuff), "%Y-%m-%d %H:%M:%S", std::localtime(&t));
    // Return the resulted buffer
    return tmbuff;
}

// ------------------------------------------------------------------------------------------------
Logger Logger::s_Inst;

// ------------------------------------------------------------------------------------------------
Logger::Logger()
    : m_Buffer()
    , m_ConsoleLevels(LOGL_ANY)
    , m_LogFileLevels(~LOGL_DBG)
    , m_ConsoleTime(false)
    , m_LogFileTime(true)
    , m_File(nullptr)
    , m_Filename()
{
    /* ... */
}

// ------------------------------------------------------------------------------------------------
Logger::~Logger()
{
    Close();
}

// ------------------------------------------------------------------------------------------------
void Logger::Close()
{
    // Is there a file handle to close?
    if (m_File)
    {
        // Flush buffered data
        std::fflush(m_File);
        // Close the file handle
        std::fclose(m_File);
        // Prevent further use of this file handle
        m_File = nullptr;
    }
}

// ------------------------------------------------------------------------------------------------
void Logger::SetLogFilename(CCStr filename)
{
    // Close the current logging file, if any
    Close();
    // Clear the current name
    m_Filename.clear();
    // Was there a name specified?
    if (!filename || *filename == '\0')
    {
        return; // We're done here!
    }
    // Make sure the internal buffer has some memory
    m_Buffer.Adjust(1024);
    // Obtain the current time for generating the filename
    const std::time_t t = std::time(nullptr);
    // Generate the filename using the current time-stamp
    if (std::strftime(m_Buffer.Data(), m_Buffer.Size(), filename, std::localtime(&t)) > 0)
    {
        m_Filename.assign(m_Buffer.Data());
    }
    else
    {
        return; // We're done here!
    }
    // Attempt to open the file for writing
    m_File = std::fopen(m_Filename.c_str(), "w");
    // See if the file could be opened
    if (!m_File)
    {
        OutputError("Unable to open the log file (%s) : %s", m_Filename.c_str(), std::strerror(errno));
    }
}

// ------------------------------------------------------------------------------------------------
void Logger::Initialize(CCStr filename)
{
    // Close the logging file
    Close();
    // Allocate some memory in the buffer
    m_Buffer.Adjust(1024);
    // Set the log file name and open the file if necessary
    SetLogFilename(filename);
}

// ------------------------------------------------------------------------------------------------
void Logger::Terminate()
{
    // Release all the buffer resources and references
    m_Buffer.ResetAll();
}

// ------------------------------------------------------------------------------------------------
void Logger::Proccess(Uint8 level, bool sub)
{
    // Obtain the time-stamp if necessary
    CCStr tms = (m_ConsoleTime || m_LogFileTime) ? GetTimeStampStr() : nullptr;
    // Are we allowed to send this message level to console?
    if (m_ConsoleLevels & level)
    {
        OutputConsoleMessage(level, sub, (m_ConsoleTime ? tms : nullptr), m_Buffer.Get());
    }
    // Are we allowed to write it to a file?
    if (m_File && (m_LogFileLevels & level))
    {
        // Write the level tag
        std::fputs(GetLevelTag(level), m_File);
        std::fputc(' ', m_File);
        // Should we include the time-stamp?
        if (m_LogFileTime && tms)
        {
            std::fputs(tms, m_File);
            std::fputc(' ', m_File);
        }
        // Write the message
        std::fputs(m_Buffer.Get(), m_File);
        // Append a new line
        std::fputc('\n', m_File);
    }
}

// ------------------------------------------------------------------------------------------------
void Logger::Send(Uint8 level, bool sub, CCStr fmt, va_list args)
{
    // Is this level even allowed?
    if ((m_ConsoleLevels & level) || (m_LogFileLevels & level))
    {
        // Generate the message in the buffer
        m_Buffer.WriteF(0, fmt, args);
        // Process the message in the buffer
        Proccess(level, sub);
    }
}

// ------------------------------------------------------------------------------------------------
void Logger::Write(Uint8 level, bool sub, CCStr fmt, ...)
{
    if ((m_ConsoleLevels & level) || (m_LogFileLevels & level))
    {
        // Initialize the variable argument list
        va_list args;
        va_start(args, fmt);
        // Generate the message in the buffer
        m_Buffer.WriteF(0, fmt, args);
        // Finalize the variable argument list
        va_end(args);
        // Process the message in the buffer
        Proccess(level, sub);
    }
}

// ------------------------------------------------------------------------------------------------
void Logger::Debug(CCStr fmt, ...)
{
    // Initialize the variable argument list
    va_list args;
    va_start(args, fmt);
    // Forward the call to the actual debug function
    Debug(fmt, args);
    // Finalize the variable argument list
    va_end(args);
}

void Logger::Debug(CCStr fmt, va_list args)
{
    using namespace Sqrat;
    // Retrieve the default Squirrel VM
    HSQUIRRELVM vm = DefaultVM::Get();
    // Used to acquire
    SQStackInfos si;
    // Write the message to the buffer
    Int32 ret = m_Buffer.WriteF(0, fmt, args);
    // Obtain information about the current stack level
    if (SQ_SUCCEEDED(sq_stackinfos(vm, 1, &si)))
    {
        m_Buffer.WriteF(ret, "\n[\n=>Location: %s\n=>Line: %d\n=>Function: %s\n]"
                , si.source ? si.source : _SC("unknown")
                , si.line
                , si.funcname ? si.funcname : _SC("unknown"));
    }
    else
    {
        m_Buffer.WriteF(ret, "\n[\n=>Location: unknown\n=>Line: unknown\n=>Function: unknown\n]");
    }
    // Process the message in the buffer
    Proccess(LOGL_ERR, true);
    // Begin the traceback process
    ret = m_Buffer.WriteF(0, "Traceback:\n[\n");
    // Traceback the function call
    for (Int32 level = 1; SQ_SUCCEEDED(sq_stackinfos(vm, level, &si)); ++level)
    {
        ret += m_Buffer.WriteF(ret, "=> [%d] %s (%d) [%s]\n", level
                                    , si.source ? si.source : _SC("unknown")
                                    , si.line
                                    , si.funcname ? si.funcname : _SC("unknown"));
    }
    // End the function call traceback
    m_Buffer.WriteF(ret, "]");
    // Process the message in the buffer
    Proccess(LOGL_INF, true);
    // Temporary variables to retrieve stack information
    CSStr s_ = 0, name = 0;
    SQInteger i_, seq = 0;
    SQFloat f_;
    SQUserPointer p_;
    // Begin the local variables information
    ret = m_Buffer.WriteF(0, "Locals:\n[\n");
    // Process each stack level
    for (Int32 level = 0; level < 10; level++)
    {
        seq = 0;
        // Display all locals in the current stack level
        while((name = sq_getlocal(vm, level, seq)))
        {
            ++seq;
            switch(sq_gettype(vm, -1))
            {
                case OT_NULL:
                    ret += m_Buffer.WriteF(ret, "=> [%d] NULL [%s] : ...\n", level, name);
                    break;
                case OT_INTEGER:
                    sq_getinteger(vm, -1, &i_);
                    ret += m_Buffer.WriteF(ret, "=> [%d] INTEGER [%s] : %d\n", level, name, i_);
                    break;
                case OT_FLOAT:
                    sq_getfloat(vm, -1, &f_);
                    ret += m_Buffer.WriteF(ret, "=> [%d] FLOAT [%s] : %f\n", level, name, f_);
                    break;
                case OT_USERPOINTER:
                    sq_getuserpointer(vm, -1, &p_);
                    ret += m_Buffer.WriteF(ret, "=> [%d] USERPOINTER [%s] : %p\n", level, name, p_);
                    break;
                case OT_STRING:
                    sq_getstring(vm, -1, &s_);
                    ret += m_Buffer.WriteF(ret, "=> [%d] STRING [%s] : %s\n", level, name, s_);
                    break;
                case OT_TABLE:
                    ret += m_Buffer.WriteF(ret, "=> [%d] TABLE [%s] : ...\n", level, name);
                    break;
                case OT_ARRAY:
                    ret += m_Buffer.WriteF(ret, "=> [%d] ARRAY [%s] : ...\n", level, name);
                    break;
                case OT_CLOSURE:
                    ret += m_Buffer.WriteF(ret, "=> [%d] CLOSURE [%s] : ...\n", level, name);
                    break;
                case OT_NATIVECLOSURE:
                    ret += m_Buffer.WriteF(ret, "=> [%d] NATIVECLOSURE [%s] : ...\n", level, name);
                    break;
                case OT_GENERATOR:
                    ret += m_Buffer.WriteF(ret, "=> [%d] GENERATOR [%s] : ...\n", level, name);
                    break;
                case OT_USERDATA:
                    ret += m_Buffer.WriteF(ret, "=> [%d] USERDATA [%s] : ...\n", level, name);
                    break;
                case OT_THREAD:
                    ret += m_Buffer.WriteF(ret, "=> [%d] THREAD [%s] : ...\n", level, name);
                    break;
                case OT_CLASS:
                    ret += m_Buffer.WriteF(ret, "=> [%d] CLASS [%s] : ...\n", level, name);
                    break;
                case OT_INSTANCE:
                    ret += m_Buffer.WriteF(ret, "=> [%d] INSTANCE [%s] : ...\n", level, name);
                    break;
                case OT_WEAKREF:
                    ret += m_Buffer.WriteF(ret, "=> [%d] WEAKREF [%s] : ...\n", level, name);
                    break;
                case OT_BOOL:
                    sq_getinteger(vm, -1, &i_);
                    ret += m_Buffer.WriteF(ret, "=> [%d] BOOL [%s] : %s\n", level, name, i_ ? _SC("true") : _SC("false"));
                    break;
                default:
                    ret += m_Buffer.WriteF(ret, "=> [%d] UNKNOWN [%s] : ...\n", level, name);
                break;
            }
            sq_pop(vm, 1);
        }
    }
    // End the variables information
    m_Buffer.WriteF(ret, "]");
    // Process the message in the buffer
    Proccess(LOGL_INF, true);
}

// ------------------------------------------------------------------------------------------------
#define SQMOD_LOG(N_, L_, S_) /*
*/ void N_(CCStr fmt, ...) /*
*/ { /*
*/  va_list args; /*
*/  va_start(args, fmt); /*
*/  Logger::Get().Send(L_, S_, fmt, args); /*
*/  va_end(args); /*
*/ } /*
*/

// ------------------------------------------------------------------------------------------------
SQMOD_LOG(LogDbg, LOGL_DBG, false)
SQMOD_LOG(LogUsr, LOGL_USR, false)
SQMOD_LOG(LogScs, LOGL_SCS, false)
SQMOD_LOG(LogInf, LOGL_INF, false)
SQMOD_LOG(LogWrn, LOGL_WRN, false)
SQMOD_LOG(LogErr, LOGL_ERR, false)
SQMOD_LOG(LogFtl, LOGL_FTL, false)

// ------------------------------------------------------------------------------------------------
SQMOD_LOG(LogSDbg, LOGL_DBG, true)
SQMOD_LOG(LogSUsr, LOGL_USR, true)
SQMOD_LOG(LogSScs, LOGL_SCS, true)
SQMOD_LOG(LogSInf, LOGL_INF, true)
SQMOD_LOG(LogSWrn, LOGL_WRN, true)
SQMOD_LOG(LogSErr, LOGL_ERR, true)
SQMOD_LOG(LogSFtl, LOGL_FTL, true)

// ------------------------------------------------------------------------------------------------
#define SQMOD_CLOG(N_, L_, S_) /*
*/bool N_(bool c, CCStr fmt, ...) /*
*/ { /*
*/  if (!c) /*
*/  { /*
*/      return c; /*
*/  } /*
*/  va_list args; /*
*/  va_start(args, fmt); /*
*/  Logger::Get().Send(L_, S_, fmt, args); /*
*/  va_end(args); /*
*/  return c; /*
*/ } /*
*/

// ------------------------------------------------------------------------------------------------
SQMOD_CLOG(cLogDbg, LOGL_DBG, false)
SQMOD_CLOG(cLogUsr, LOGL_USR, false)
SQMOD_CLOG(cLogScs, LOGL_SCS, false)
SQMOD_CLOG(cLogInf, LOGL_INF, false)
SQMOD_CLOG(cLogWrn, LOGL_WRN, false)
SQMOD_CLOG(cLogErr, LOGL_ERR, false)
SQMOD_CLOG(cLogFtl, LOGL_FTL, false)

// ------------------------------------------------------------------------------------------------
SQMOD_CLOG(cLogSDbg, LOGL_DBG, true)
SQMOD_CLOG(cLogSUsr, LOGL_USR, true)
SQMOD_CLOG(cLogSScs, LOGL_SCS, true)
SQMOD_CLOG(cLogSInf, LOGL_INF, true)
SQMOD_CLOG(cLogSWrn, LOGL_WRN, true)
SQMOD_CLOG(cLogSErr, LOGL_ERR, true)
SQMOD_CLOG(cLogSFtl, LOGL_FTL, true)

// ------------------------------------------------------------------------------------------------
template < Uint8 L, bool S > static SQInteger LogBasicMessage(HSQUIRRELVM vm)
{
    const Int32 top = sq_gettop(vm);
    // Was the message value specified?
    if (top <= 1)
    {
        return sq_throwerror(vm, "Missing message value");
    }
    // Attempt to generate the string value
    StackStrF val(vm, 2);
    // Have we failed to retrieve the string?
    if (SQ_FAILED(val.mRes))
    {
        return val.mRes; // Propagate the error!
    }
    // Forward the resulted string value to the logger
    Logger::Get().Write(L, S, "%s", val.mPtr);
    // This function does not return a value
    return 0;
}

// ------------------------------------------------------------------------------------------------
static void SqLogClose()
{
    Logger::Get().Close();
}

// ------------------------------------------------------------------------------------------------
static void SqLogInitialize(CSStr filename)
{
    Logger::Get().Initialize(filename);
}

// ------------------------------------------------------------------------------------------------
static void SqLogToggleConsoleTime(bool toggle)
{
    Logger::Get().ToggleConsoleTime(toggle);
}

// ------------------------------------------------------------------------------------------------
static bool SqLogConsoleHasTime()
{
    return Logger::Get().ConsoleHasTime();
}

// ------------------------------------------------------------------------------------------------
static void SqLogToggleLogFileTime(bool toggle)
{
    Logger::Get().ToggleLogFileTime(toggle);
}

// ------------------------------------------------------------------------------------------------
static bool SqLogLogFileHasTime()
{
    return Logger::Get().LogFileHasTime();
}

// ------------------------------------------------------------------------------------------------
static void SqLogSetConsoleLevels(SQInteger level)
{
    Logger::Get().SetConsoleLevels(ConvTo< Uint8 >::From(level));
}

// ------------------------------------------------------------------------------------------------
static bool SqLogGetConsoleLevels()
{
    return Logger::Get().GetConsoleLevels();
}

// ------------------------------------------------------------------------------------------------
static void SqLogSetLogFileLevels(SQInteger level)
{
    Logger::Get().SetLogFileLevels(ConvTo< Uint8 >::From(level));
}

// ------------------------------------------------------------------------------------------------
static bool SqLogGetLogFileLevels()
{
    return Logger::Get().GetLogFileLevels();
}

// ------------------------------------------------------------------------------------------------
static void SqLogEnableConsoleLevel(SQInteger level)
{
    Logger::Get().EnableConsoleLevel(ConvTo< Uint8 >::From(level));
}

// ------------------------------------------------------------------------------------------------
static void SqLogDisableConsoleLevel(SQInteger level)
{
    Logger::Get().DisableConsoleLevel(ConvTo< Uint8 >::From(level));
}

// ------------------------------------------------------------------------------------------------
static void SqLogToggleConsoleLevel(SQInteger level, bool toggle)
{
    Logger::Get().ToggleConsoleLevel(ConvTo< Uint8 >::From(level), toggle);
}

// ------------------------------------------------------------------------------------------------
static void SqLogEnableLogFileLevel(SQInteger level)
{
    Logger::Get().EnableLogFileLevel(ConvTo< Uint8 >::From(level));
}

// ------------------------------------------------------------------------------------------------
static void SqLogDisableLogFileLevel(SQInteger level)
{
    Logger::Get().DisableLogFileLevel(ConvTo< Uint8 >::From(level));
}

// ------------------------------------------------------------------------------------------------
static void SqLogToggleLogFileLevel(SQInteger level, bool toggle)
{
    Logger::Get().ToggleLogFileLevel(ConvTo< Uint8 >::From(level), toggle);
}

// ------------------------------------------------------------------------------------------------
static const String & SqLogGetLogFilename()
{
    return Logger::Get().GetLogFilename();
}

// ------------------------------------------------------------------------------------------------
static void SqLogSetLogFilename(CSStr filename)
{
    Logger::Get().SetLogFilename(filename);
}

// ================================================================================================
void Register_Log(HSQUIRRELVM vm)
{
    RootTable(vm)
    .Bind(_SC("SqLog"), Table(vm)
        .SquirrelFunc(_SC("Dbg"), &LogBasicMessage< LOGL_DBG, false >)
        .SquirrelFunc(_SC("Usr"), &LogBasicMessage< LOGL_USR, false >)
        .SquirrelFunc(_SC("Scs"), &LogBasicMessage< LOGL_SCS, false >)
        .SquirrelFunc(_SC("Inf"), &LogBasicMessage< LOGL_INF, false >)
        .SquirrelFunc(_SC("Wrn"), &LogBasicMessage< LOGL_WRN, false >)
        .SquirrelFunc(_SC("Err"), &LogBasicMessage< LOGL_ERR, false >)
        .SquirrelFunc(_SC("Ftl"), &LogBasicMessage< LOGL_FTL, false >)
        .SquirrelFunc(_SC("SDbg"), &LogBasicMessage< LOGL_DBG, true >)
        .SquirrelFunc(_SC("SUsr"), &LogBasicMessage< LOGL_USR, true >)
        .SquirrelFunc(_SC("SScs"), &LogBasicMessage< LOGL_SCS, true >)
        .SquirrelFunc(_SC("SInf"), &LogBasicMessage< LOGL_INF, true >)
        .SquirrelFunc(_SC("SWrn"), &LogBasicMessage< LOGL_WRN, true >)
        .SquirrelFunc(_SC("SErr"), &LogBasicMessage< LOGL_ERR, true >)
        .SquirrelFunc(_SC("SFtl"), &LogBasicMessage< LOGL_FTL, true >)
        .Func(_SC("Close"), &SqLogClose)
        .Func(_SC("Initialize"), &SqLogInitialize)
        .Func(_SC("ToggleConsoleTime"), &SqLogToggleConsoleTime)
        .Func(_SC("ConsoleHasTime"), &SqLogConsoleHasTime)
        .Func(_SC("ToggleLogFileTime"), &SqLogToggleLogFileTime)
        .Func(_SC("LogFileHasTime"), &SqLogLogFileHasTime)
        .Func(_SC("SetConsoleLevels"), &SqLogSetConsoleLevels)
        .Func(_SC("GetConsoleLevels"), &SqLogGetConsoleLevels)
        .Func(_SC("SetLogFileLevels"), &SqLogSetLogFileLevels)
        .Func(_SC("GetLogFileLevels"), &SqLogGetLogFileLevels)
        .Func(_SC("EnableConsoleLevel"), &SqLogEnableConsoleLevel)
        .Func(_SC("DisableConsoleLevel"), &SqLogDisableConsoleLevel)
        .Func(_SC("ToggleConsoleLevel"), &SqLogToggleConsoleLevel)
        .Func(_SC("EnableLogFileLevel"), &SqLogEnableLogFileLevel)
        .Func(_SC("DisableLogFileLevel"), &SqLogDisableLogFileLevel)
        .Func(_SC("ToggleLogFileLevel"), &SqLogToggleLogFileLevel)
        .Func(_SC("GetLogFilename"), &SqLogGetLogFilename)
        .Func(_SC("SetLogFilename"), &SqLogSetLogFilename)
    );
}

} // Namespace:: SqMod