/**
 * @file main.cpp
 * @brief StreamHub ImGui desktop client entry point.
 *
 * Usage: StreamHubClient [-host <ip>] [-port <N>]
 *
 * Defaults: host=127.0.0.1 port=8090
 */

#include "App.h"
#include "Icons.h"

#include "imgui.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_opengl3.h"
#include "implot.h"

#include <SDL.h>
#include <SDL_opengl.h>

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <unistd.h>

int main(int argc, char** argv) {
    /* ── Parse args ─────────────────────────────────────────────────── */
    std::string host = "127.0.0.1";
    uint16_t    port = 8090u;

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-host") == 0 && i + 1 < argc) {
            host = argv[++i];
        } else if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) {
            port = static_cast<uint16_t>(atoi(argv[++i]));
        } else if (strcmp(argv[i], "-h") == 0 ||
                   strcmp(argv[i], "--help") == 0) {
            printf("Usage: StreamHubClient [-host <ip>] [-port <N>]\n");
            return 0;
        }
    }

    /* ── SDL2 + OpenGL ──────────────────────────────────────────────── */
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
        fprintf(stderr, "SDL_Init error: %s\n", SDL_GetError());
        return 1;
    }

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);

    SDL_WindowFlags wf = static_cast<SDL_WindowFlags>(
        SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
    SDL_Window* window = SDL_CreateWindow(
        "StreamHub Client",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        1280, 800, wf);
    if (!window) {
        fprintf(stderr, "SDL_CreateWindow error: %s\n", SDL_GetError());
        return 1;
    }

    SDL_GLContext glCtx = SDL_GL_CreateContext(window);
    SDL_GL_MakeCurrent(window, glCtx);
    SDL_GL_SetSwapInterval(1);  /* vsync */

    /* ── ImGui setup ─────────────────────────────────────────────────── */
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImPlot::CreateContext();

    ImGuiIO& io = ImGui::GetIO();
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;

    /* ── Fonts: bundled resources (Fira Sans + Font Awesome) ─────────────── *
     * The font dir is resolved relative to the executable so both build-tree
     * and installed layouts work; the source tree is the last fallback.      */
    const float kFontSizePx = 17.0f;
    {
        std::string exeDir;
        {
            char buf[4096];
            ssize_t n = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
            if (n > 0) {
                buf[n] = '\0';
                if (char* slash = strrchr(buf, '/')) { *slash = '\0'; }
                exeDir = buf;
            }
        }
        const std::string fontDirCandidates[] = {
            exeDir + "/../share/streamhub-client/fonts",   /* installed   */
            exeDir + "/resources/fonts",                   /* build tree  */
            std::string(APP_RESOURCE_DIR) + "/fonts",      /* source tree */
        };
        auto findFont = [&](const char* file) -> std::string {
            for (const auto& d : fontDirCandidates) {
                std::string p = d + "/" + file;
                if (access(p.c_str(), R_OK) == 0) { return p; }
            }
            return "";
        };

        std::string uiFont = findFont("FiraSans-Regular.ttf");
        bool loaded = !uiFont.empty() &&
                      io.Fonts->AddFontFromFileTTF(uiFont.c_str(), kFontSizePx);
        if (loaded) {
            fprintf(stderr, "Font: loaded %s\n", uiFont.c_str());
        } else {
            io.Fonts->AddFontDefault();
            fprintf(stderr, "Font: bundled font not found, using built-in.\n");
        }

#ifdef HAVE_FONT_AWESOME
        /* Merge Font Awesome 6 solid glyphs into the base font */
        std::string faFont = findFont("fa-solid-900.ttf");
        static const ImWchar faRanges[] = { ICON_MIN_FA, ICON_MAX_16_FA, 0 };
        ImFontConfig faCfg;
        faCfg.MergeMode        = true;
        faCfg.PixelSnapH       = true;
        faCfg.GlyphMinAdvanceX = kFontSizePx * 0.9f;   /* monospace-ish icons */
        faCfg.GlyphOffset      = ImVec2(0.f, 1.f);
        if (faFont.empty() ||
            !io.Fonts->AddFontFromFileTTF(faFont.c_str(), kFontSizePx * 0.80f,
                                          &faCfg, faRanges)) {
            fprintf(stderr, "Font: failed to merge Font Awesome glyphs\n");
        }
#endif
    }

    ImGui::StyleColorsDark();
    ImGuiStyle& style = ImGui::GetStyle();
    style.WindowRounding    = 10.f;
    style.ChildRounding     = 8.f;
    style.FrameRounding     = 7.f;   /* rounded buttons/inputs */
    style.PopupRounding     = 8.f;
    style.GrabRounding      = 7.f;
    style.ScrollbarRounding = 12.f;
    style.TabRounding       = 7.f;
    style.FramePadding      = ImVec2(9.f, 5.f);
    style.ItemSpacing       = ImVec2(8.f, 6.f);
    style.ItemInnerSpacing  = ImVec2(6.f, 4.f);
    style.CellPadding       = ImVec2(8.f, 4.f);
    style.ScrollbarSize     = 11.f;
    style.WindowPadding     = ImVec2(10.f, 10.f);
    style.PopupBorderSize   = 1.f;
    style.FrameBorderSize   = 0.f;
    style.WindowBorderSize  = 1.f;
    style.SeparatorTextBorderSize = 2.f;
    style.WindowTitleAlign  = ImVec2(0.5f, 0.5f);

    /* ── Catppuccin Mocha palette ─────────────────────────────────────── */
    const ImVec4 kCrust    (0.067f,0.067f,0.106f,1.f);   /* #11111b */
    const ImVec4 kMantle   (0.094f,0.094f,0.145f,1.f);   /* #181825 */
    const ImVec4 kBase     (0.118f,0.118f,0.180f,1.f);   /* #1e1e2e */
    const ImVec4 kSurface0 (0.192f,0.196f,0.267f,1.f);   /* #313244 */
    const ImVec4 kSurface1 (0.271f,0.278f,0.353f,1.f);   /* #45475a */
    const ImVec4 kSurface2 (0.345f,0.357f,0.439f,1.f);   /* #585b70 */
    const ImVec4 kOverlay0 (0.424f,0.439f,0.525f,1.f);   /* #6c7086 */
    const ImVec4 kText     (0.804f,0.839f,0.957f,1.f);   /* #cdd6f4 */
    const ImVec4 kBlue     (0.537f,0.706f,0.980f,1.f);   /* #89b4fa */
    const ImVec4 kLavender (0.706f,0.745f,0.996f,1.f);   /* #b4befe */
    const ImVec4 kMauve    (0.796f,0.651f,0.969f,1.f);   /* #cba6f7 */
    const ImVec4 kPeach    (0.980f,0.702f,0.529f,1.f);   /* #fab387 */

    ImVec4* c = style.Colors;
    c[ImGuiCol_WindowBg]          = kBase;
    c[ImGuiCol_ChildBg]           = kMantle;
    c[ImGuiCol_PopupBg]           = kMantle;
    c[ImGuiCol_Border]            = kSurface0;
    c[ImGuiCol_BorderShadow]      = ImVec4(0.f,0.f,0.f,0.f);
    c[ImGuiCol_FrameBg]           = kSurface0;
    c[ImGuiCol_FrameBgHovered]    = kSurface1;
    c[ImGuiCol_FrameBgActive]     = kSurface1;
    c[ImGuiCol_TitleBg]           = kCrust;
    c[ImGuiCol_TitleBgActive]     = kMantle;
    c[ImGuiCol_TitleBgCollapsed]  = kCrust;
    c[ImGuiCol_MenuBarBg]         = kCrust;
    c[ImGuiCol_ScrollbarBg]       = kCrust;
    c[ImGuiCol_ScrollbarGrab]     = kSurface1;
    c[ImGuiCol_ScrollbarGrabHovered] = kSurface2;
    c[ImGuiCol_ScrollbarGrabActive]  = kBlue;
    c[ImGuiCol_CheckMark]         = kBlue;
    c[ImGuiCol_SliderGrab]        = ImVec4(kBlue.x,kBlue.y,kBlue.z,0.8f);
    c[ImGuiCol_SliderGrabActive]  = kBlue;
    c[ImGuiCol_Button]            = kSurface0;
    c[ImGuiCol_ButtonHovered]     = kSurface1;
    c[ImGuiCol_ButtonActive]      = ImVec4(kBlue.x,kBlue.y,kBlue.z,0.35f);
    c[ImGuiCol_Header]            = kSurface0;
    c[ImGuiCol_HeaderHovered]     = kSurface1;
    c[ImGuiCol_HeaderActive]      = ImVec4(kBlue.x,kBlue.y,kBlue.z,0.4f);
    c[ImGuiCol_Separator]         = kSurface0;
    c[ImGuiCol_SeparatorHovered]  = ImVec4(kBlue.x,kBlue.y,kBlue.z,0.6f);
    c[ImGuiCol_SeparatorActive]   = kBlue;
    c[ImGuiCol_ResizeGrip]        = ImVec4(kBlue.x,kBlue.y,kBlue.z,0.2f);
    c[ImGuiCol_ResizeGripHovered] = ImVec4(kBlue.x,kBlue.y,kBlue.z,0.6f);
    c[ImGuiCol_ResizeGripActive]  = kBlue;
    c[ImGuiCol_Tab]               = kSurface0;
    c[ImGuiCol_TabHovered]        = ImVec4(kBlue.x,kBlue.y,kBlue.z,0.3f);
    c[ImGuiCol_TabActive]         = kSurface1;
    c[ImGuiCol_TabUnfocused]      = kMantle;
    c[ImGuiCol_TabUnfocusedActive]= kSurface0;
    c[ImGuiCol_TableHeaderBg]     = kSurface0;
    c[ImGuiCol_TableBorderStrong] = kSurface1;
    c[ImGuiCol_TableBorderLight]  = kSurface0;
    c[ImGuiCol_TableRowBg]        = ImVec4(0.f,0.f,0.f,0.f);
    c[ImGuiCol_TableRowBgAlt]     = ImVec4(1.f,1.f,1.f,0.025f);
    c[ImGuiCol_Text]              = kText;
    c[ImGuiCol_TextDisabled]      = kOverlay0;
    c[ImGuiCol_TextSelectedBg]    = ImVec4(kBlue.x,kBlue.y,kBlue.z,0.35f);
    c[ImGuiCol_DragDropTarget]    = kPeach;
    c[ImGuiCol_NavHighlight]      = kLavender;
    c[ImGuiCol_ModalWindowDimBg]  = ImVec4(0.f,0.f,0.f,0.55f);
    (void)kMauve;

    // ImPlot style
    ImPlotStyle& ps = ImPlot::GetStyle();
    ps.PlotPadding     = ImVec2(8.f,8.f);
    ps.LabelPadding    = ImVec2(5.f,3.f);
    ps.LegendPadding   = ImVec2(8.f,8.f);
    ps.MinorAlpha      = 0.15f;
    ps.Colors[ImPlotCol_PlotBg]    = kCrust;
    ps.Colors[ImPlotCol_PlotBorder]= kSurface0;
    ps.Colors[ImPlotCol_AxisGrid]  = ImVec4(kSurface0.x,kSurface0.y,kSurface0.z,0.6f);
    ps.Colors[ImPlotCol_AxisTick]  = kSurface1;
    ps.Colors[ImPlotCol_AxisText]  = ImVec4(0.651f,0.678f,0.784f,1.f);
    ps.Colors[ImPlotCol_LegendBg]  = ImVec4(kMantle.x,kMantle.y,kMantle.z,0.9f);
    ps.Colors[ImPlotCol_LegendBorder] = kSurface0;
    ps.Colors[ImPlotCol_Crosshairs]   = ImVec4(kText.x,kText.y,kText.z,0.5f);

    ImGui_ImplSDL2_InitForOpenGL(window, glCtx);
    ImGui_ImplOpenGL3_Init("#version 330 core");

    /* ── Application ─────────────────────────────────────────────────── */
    StreamHubClient::App app;
    app.init(host, port);

    bool quit = false;
    while (!quit) {
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            ImGui_ImplSDL2_ProcessEvent(&event);
            if (event.type == SDL_QUIT) { quit = true; }
            if (event.type == SDL_KEYDOWN &&
                event.key.keysym.sym == SDLK_q &&
                (event.key.keysym.mod & KMOD_ALT)) {
                quit = true;
            }
        }

        /* ── New frame ───────────────────────────────────────────────── */
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplSDL2_NewFrame();
        ImGui::NewFrame();

        app.update();

        /* ── Render ──────────────────────────────────────────────────── */
        ImGui::Render();
        glViewport(0, 0, static_cast<int>(io.DisplaySize.x),
                         static_cast<int>(io.DisplaySize.y));
        glClearColor(0.118f, 0.118f, 0.180f, 1.f);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
        SDL_GL_SwapWindow(window);
    }

    /* ── Cleanup ─────────────────────────────────────────────────────── */
    app.shutdown();
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplSDL2_Shutdown();
    ImPlot::DestroyContext();
    ImGui::DestroyContext();
    SDL_GL_DeleteContext(glCtx);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}
