#include "Utility.h"

#ifndef NDEBUG

namespace Utility
{
namespace Debug
{
ImGuiLogger openGLLogger;
}
} // namespace Utility

void Utility::Debug::ImGuiLogger::Clear()
{
    Buf.clear();
    LineOffsets.clear();
}

void Utility::Debug::ImGuiLogger::AddLog(const char *fmt, ...) IM_FMTARGS(2)
{
    int old_size = Buf.size();
    va_list args;
    va_start(args, fmt);
    Buf.appendfv(fmt, args);
    va_end(args);
    for (int new_size = Buf.size(); old_size < new_size; old_size++)
        if (Buf[old_size] == '\n')
            LineOffsets.push_back(old_size);
    ScrollToBottom = true;
}

void Utility::Debug::ImGuiLogger::Draw(const char *title, bool *p_open)
{
    ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
    ImGui::Begin(title, p_open);
    if (ImGui::Button("Clear"))
        Clear();
    ImGui::SameLine();
    bool copy = ImGui::Button("Copy");
    ImGui::SameLine();
    ImGui::Checkbox("Errors", &ShowError);
    ImGui::SameLine();
    ImGui::Checkbox("Deprecated Behavior", &ShowDeprecatedBehavior);
    ImGui::SameLine();
    ImGui::Checkbox("Undefined Behavior", &ShowUndefinedBehavior);
    ImGui::SameLine();
    ImGui::Checkbox("Portability", &ShowPortability);
    ImGui::Checkbox("Performance", &ShowPerformance);
    ImGui::SameLine();
    ImGui::Checkbox("Marker", &ShowMarker);
    ImGui::SameLine();
    ImGui::Checkbox("Push Group", &ShowPushGroup);
    ImGui::SameLine();
    ImGui::Checkbox("Pop Group", &ShowPopGroup);
    ImGui::SameLine();
    ImGui::Checkbox("Other", &ShowOther);

    Filter.Draw("Filter", -100.0f);
    ImGui::Separator();
    ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
    if (copy)
        ImGui::LogToClipboard();

    if (Filter.IsActive())
    {
        const char *buf_begin = Buf.begin();
        const char *line = buf_begin;
        for (int line_no = 0; line != NULL; line_no++)
        {
            const char *line_end = (line_no < LineOffsets.Size) ? buf_begin + LineOffsets[line_no] : NULL;
            if (Filter.PassFilter(line, line_end))
                ImGui::TextUnformatted(line, line_end);
            line = line_end && line_end[1] ? line_end + 1 : NULL;
        }
    }
    else
    {
        ImGui::TextUnformatted(Buf.begin());
    }

    if (ScrollToBottom)
        ImGui::SetScrollHere(1.0f);
    ScrollToBottom = false;
    ImGui::EndChild();
    ImGui::End();
}

#endif //NDEBUG

void APIENTRY Utility::openglCallbackFunction(GLenum source,
                                              GLenum type,
                                              GLuint id,
                                              GLenum severity,
                                              GLsizei length,
                                              const GLchar *message,
                                              const void *userParam)
{

    std::string _severity;
    switch (severity)
    {
    case GL_DEBUG_SEVERITY_LOW:
        _severity = "LOW";
        break;
    case GL_DEBUG_SEVERITY_MEDIUM:
        _severity = "MEDIUM";
        break;
    case GL_DEBUG_SEVERITY_HIGH:
        _severity = "HIGH";
        break;
    default:
        _severity = "NOTIFICATION";
        break;
    }

    switch (type)
    {
    case GL_DEBUG_TYPE_ERROR:
        if (Utility::Debug::openGLLogger.ShowError)
        {
            Debug::openGLLogger.AddLog("ERROR: %s ID: %d SEVERITY: %s\n", message, id, _severity.c_str());
        }
        break;
    case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
        if (Utility::Debug::openGLLogger.ShowDeprecatedBehavior)
        {
            Debug::openGLLogger.AddLog("DEPRECATED_BEHAVIOR: %s ID: %d SEVERITY: %s\n", message, id, _severity.c_str());
        }
        break;
    case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
        if (Utility::Debug::openGLLogger.ShowUndefinedBehavior)
        {
            Debug::openGLLogger.AddLog("UNDEFINED_BEHAVIOR: %s ID: %d SEVERITY: %s\n", message, id, _severity.c_str());
        }
        break;
    case GL_DEBUG_TYPE_PORTABILITY:
        if (Utility::Debug::openGLLogger.ShowPortability)
        {
            Debug::openGLLogger.AddLog("PORTATBILITY: %s ID: %d SEVERITY: %s\n", message, id, _severity.c_str());
        }
        break;
    case GL_DEBUG_TYPE_PERFORMANCE:
        if (Utility::Debug::openGLLogger.ShowPerformance)
        {
            Debug::openGLLogger.AddLog("PERFORMANCE: %s ID: %d SEVERITY: %s\n", message, id, _severity.c_str());
        }
        break;
    case GL_DEBUG_TYPE_OTHER:
        if (Utility::Debug::openGLLogger.ShowOther)
        {
            Debug::openGLLogger.AddLog("OTHER: %s ID: %d SEVERITY: %s\n", message, id, _severity.c_str());
        }
        break;
    }
}

static const char *Errornames[] = {"OpenGL", "SDL", "GLEW", "Other"};
void Utility::printWarning(ERROR_TYPE type, std::string msg)
{
    std::cout << "Warning[" << Errornames[type] << "]: " << msg << std::endl;
}

void Utility::printError(ERROR_TYPE type, std::string msg)
{
    std::cout << "Error[" << Errornames[type] << "]: " << msg << std::endl;
}
void Utility::printInfo(ERROR_TYPE type, std::string msg)
{
    std::cout << "Info[" << Errornames[type] << "]: " << msg << std::endl;
}