#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>

#include <chrono>
#include <cstdarg>

// Begin Todo JULIET debug output
#include <cinttypes>

#ifdef JULIET_WIN32
#include <Core/HAL/Win32.h>
#include <debugapi.h>
#endif
// End Todo

namespace Juliet
{
    namespace
    {
        LogManager       LogManagerSingleton;
        constexpr size_t kLogBufferSize = 1024;
    } // namespace

    LogManager::Entry::Entry(std::string& value, LogLevel level, LogCategory category)
      : Value(value)
      , Level(level)
      , Category(category)
    {
        // TODO Juliet clock
        Time = std::chrono::system_clock::now();
    }

    LogManager::LogManager()
      : IsInitialized(false)
    {
    }

    void LogManager::Init()
    {
        IsInitialized = true;

        Log(LogLevel::Message, LogCategory::Engine, "Initializing Log Manager");
    }

    void LogManager::Shutdown()
    {
        Log(LogLevel::Message, LogCategory::Engine, "Shutting down Log Manager");

        Entries.clear();

        IsInitialized = false;
    }

    void LogManager::AddEntry(Entry&& entry)
    {
        if (Entries.size() >= kLogBufferSize)
        {
            Entries.pop_front();
        }

        Entries.push_back(std::move(entry));
    }

    void LogManager::OutputLog(Entry& entry)
    {
        // TODO Juliet Output io for each platform

        // {:%F} is YYYY-MM-DD, {:%T} is HH:MM:SS.ffffff
        std::string timestamp = std::format("[{:%F %T}] ", entry.Time);
        std::string fullMessage = timestamp + entry.Value + "\n";

#ifdef JULIET_WIN32
        OutputDebugStringA(fullMessage.c_str());
#endif
        printf("%s", fullMessage.c_str());
    }

    void InitializeLogManager()
    {
#ifdef JULIET_WIN32
        SetConsoleOutputCP(CP_UTF8);
        SetConsoleCP(CP_UTF8);
#endif

        LogManagerSingleton.Init();
    }

    void ShutdownLogManager()
    {
        LogManagerSingleton.Shutdown();
    }

    void Log(LogLevel level, LogCategory category, const char* fmt, va_list args)
    {
        // TODO : Revisit, copy from https://github.com/Eclmist/Ether/blob/develop/src/common/logging/loggingmanager.cpp
        char formattedBuffer[4096];

        (void)vsprintf_s(formattedBuffer, fmt, args); // Cast to void to ignore the return type. TODO : Juliet format function

        std::string formattedText(formattedBuffer);

        std::stringstream ss(formattedText);
        std::string       individualLine;

        while (std::getline(ss, individualLine, '\n'))
        {
            LogManager::Entry entry(individualLine, level, category);
            LogManagerSingleton.OutputLog(entry);

            if (LogManagerSingleton.GetIsInitialized())
            {
                LogManagerSingleton.AddEntry(std::move(entry));
            }
        }
    }

    void Log(LogLevel level, LogCategory category, const char* fmt, ...)
    {
        va_list args;
        va_start(args, fmt);
        Log(level, category, fmt, args);
        va_end(args);
    }

    void LogMessage(LogCategory category, const char* fmt, ...)
    {
        va_list args;
        va_start(args, fmt);
        Log(LogLevel::Message, category, fmt, args);
        va_end(args);
    }

    void LogWarning(LogCategory category, const char* fmt, ...)
    {
        va_list args;
        va_start(args, fmt);
        Log(LogLevel::Warning, category, fmt, args);
        va_end(args);
    }

    void LogError(LogCategory category, const char* fmt, ...)
    {
        va_list args;
        va_start(args, fmt);
        Log(LogLevel::Error, category, fmt, args);
        va_end(args);
    }
} // namespace Juliet
