#include "IconManager.h"
#include "../utils/Logger.h"

// NanoSVG — header-only SVG parser and rasteriser.
// Define implementation macros in exactly one .cpp file.
#define NANOSVG_IMPLEMENTATION
#include <nanosvg.h>
#define NANOSVGRAST_IMPLEMENTATION
#include <nanosvgrast.h>

// OpenGL (via GLAD)
#include "../utils/GLExt.h"
#include <glad/gl.h>

#include <cstring>

namespace UsdLayerManager {

// ---------------------------------------------------------------------------
// helpers
// ---------------------------------------------------------------------------

static const char* IconFilename(Icon icon) {
    switch (icon) {
        case Icon::Eye:         return "eye.svg";
        case Icon::EyeSlash:    return "eye-slash.svg";
        case Icon::Link:        return "link.svg";
        case Icon::LinkSlash:   return "link-slash.svg";
        case Icon::Globe:       return "globe.svg";
        case Icon::Cube:        return "cube.svg";
        case Icon::Camera:      return "camera.svg";
        case Icon::Lightbulb:   return "lightbulb.svg";
        case Icon::FolderOpen:  return "folder-open.svg";
        case Icon::ObjectGroup: return "object-group.svg";
        case Icon::Swatchbook:  return "swatchbook.svg";
        case Icon::Code:        return "code.svg";
        case Icon::LayerGroup:  return "layer-group.svg";
        case Icon::CircleDot:   return "circle-dot.svg";
        case Icon::ToolSelect:  return "cursor.svg";
        case Icon::ToolMove:    return "arrows-move.svg";
        case Icon::ToolRotate:  return "arrows-rotate.svg";
        case Icon::ToolScale:   return "arrows-scale.svg";
        case Icon::WorldSpace:  return "world-space.svg";
        case Icon::LocalSpace:  return "local-space.svg";
        case Icon::Grid:        return "grid.svg";
        case Icon::Antialias:   return "antialias.svg";
        case Icon::LayoutSingle: return "layout-single.svg";
        case Icon::LayoutHSplit: return "layout-hsplit.svg";
        case Icon::LayoutVSplit: return "layout-vsplit.svg";
        case Icon::LayoutQuad:   return "layout-quad.svg";
        case Icon::SkipBack:     return "skip-back.svg";
        case Icon::StepBack:     return "step-back.svg";
        case Icon::PlayBack:     return "play-back.svg";
        case Icon::Play:         return "play.svg";
        case Icon::Pause:        return "pause.svg";
        case Icon::StepForward:  return "step-forward.svg";
        case Icon::SkipEnd:      return "skip-end.svg";
        case Icon::Loop:         return "loop.svg";
        case Icon::Bounce:       return "bounce.svg";
        case Icon::Refresh:      return "arrows-rotate.svg";
        case Icon::FilePlus:     return "file-plus.svg";
        case Icon::Pen:          return "pen.svg";
        case Icon::Settings:     return "gear.svg";
        case Icon::ChevronUp:    return "chevron-up.svg";
        case Icon::ChevronDown:  return "chevron-down.svg";
        default:                return nullptr;
    }
}

static constexpr Icon kAllIcons[] = {
    Icon::Eye, Icon::EyeSlash,
    Icon::Link, Icon::LinkSlash,
    Icon::Globe, Icon::Cube, Icon::Camera, Icon::Lightbulb,
    Icon::FolderOpen, Icon::ObjectGroup, Icon::Swatchbook,
    Icon::Code, Icon::LayerGroup, Icon::CircleDot,
    Icon::ToolSelect, Icon::ToolMove, Icon::ToolRotate, Icon::ToolScale,
    Icon::WorldSpace, Icon::LocalSpace, Icon::Grid, Icon::Antialias,
    Icon::LayoutSingle, Icon::LayoutHSplit, Icon::LayoutVSplit, Icon::LayoutQuad,
    Icon::SkipBack, Icon::StepBack, Icon::PlayBack, Icon::Play, Icon::Pause,
    Icon::StepForward, Icon::SkipEnd, Icon::Loop, Icon::Bounce,
    Icon::Refresh, Icon::FilePlus, Icon::Pen, Icon::Settings,
    Icon::ChevronUp, Icon::ChevronDown,
};

// ---------------------------------------------------------------------------
// IconManager
// ---------------------------------------------------------------------------

IconManager::IconManager() {}

IconManager::~IconManager() {
    Shutdown();
}

bool IconManager::Initialize(const std::string& iconDir, int sizePixels) {
    m_sizePixels = sizePixels;
    CreateFallback();

    bool allOk = true;
    for (Icon ic : kAllIcons) {
        const char* filename = IconFilename(ic);
        if (!filename) continue;
        std::string path = iconDir;
        if (!path.empty() && path.back() != '/' && path.back() != '\\')
            path += '/';
        path += filename;
        if (!LoadSVG(ic, path)) {
            LOG_WARNING(std::string("IconManager: failed to load ") + path);
            allOk = false;
        }
    }
    return allOk;
}

void IconManager::Shutdown() {
    for (auto& kv : m_textures) {
        GLuint tex = static_cast<GLuint>(static_cast<uintptr_t>(kv.second));
        if (tex) glDeleteTextures(1, &tex);
    }
    m_textures.clear();

    if (m_fallback != ImTextureID_Invalid) {
        GLuint tex = static_cast<GLuint>(static_cast<uintptr_t>(m_fallback));
        glDeleteTextures(1, &tex);
        m_fallback = ImTextureID_Invalid;
    }
}

ImTextureID IconManager::Get(Icon icon) const {
    auto it = m_textures.find(static_cast<int>(icon));
    if (it != m_textures.end()) return it->second;
    return m_fallback;
}

// ---------------------------------------------------------------------------
// private
// ---------------------------------------------------------------------------

void IconManager::CreateFallback() {
    // 1×1 transparent pixel
    unsigned char pixel[4] = { 0, 0, 0, 0 };
    GLuint tex = 0;
    glGenTextures(1, &tex);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
    glBindTexture(GL_TEXTURE_2D, 0);
    m_fallback = static_cast<ImTextureID>(static_cast<uintptr_t>(tex));
}

bool IconManager::LoadSVG(Icon icon, const std::string& path) {
    // NanoSVG parses from a mutable char buffer.
    NSVGimage* svg = nsvgParseFromFile(path.c_str(), "px", 96.0f);
    if (!svg) return false;
    if (svg->width <= 0.0f || svg->height <= 0.0f) {
        nsvgDelete(svg);
        return false;
    }

    // Override every shape's fill/stroke to opaque white so the icons render
    // correctly on the dark ImGui theme.  NanoSVG stores colours as 0xAABBGGRR;
    // default fills use NSVG_RGB(0,0,0) which has alpha=0 in the high byte.
    // We must force alpha=0xFF (fully opaque) — not preserve the SVG alpha —
    // otherwise the rasteriser multiplies by alpha=0 and produces transparent pixels.
    for (NSVGshape* shape = svg->shapes; shape != nullptr; shape = shape->next) {
        if (shape->fill.type == NSVG_PAINT_COLOR) {
            // Force opaque white: RGB channels from existing colour are irrelevant,
            // just make every filled shape a solid white mask.
            shape->fill.color = 0xFFFFFFFF;
        }
        if (shape->stroke.type == NSVG_PAINT_COLOR) {
            shape->stroke.color = 0xFFFFFFFF;
        }
    }

    // Rasterise at target size.
    NSVGrasterizer* rast = nsvgCreateRasterizer();
    if (!rast) { nsvgDelete(svg); return false; }

    int w = m_sizePixels;
    int h = m_sizePixels;
    float scale = static_cast<float>(w) / svg->width;

    std::vector<unsigned char> pixels(static_cast<size_t>(w * h * 4), 0);
    nsvgRasterize(rast, svg, 0.0f, 0.0f, scale, pixels.data(), w, h, w * 4);

    nsvgDeleteRasterizer(rast);
    nsvgDelete(svg);

    // Upload to OpenGL.
    GLuint tex = 0;
    glGenTextures(1, &tex);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
    glBindTexture(GL_TEXTURE_2D, 0);

    m_textures[static_cast<int>(icon)] =
        static_cast<ImTextureID>(static_cast<uintptr_t>(tex));

    return true;
}

} // namespace UsdLayerManager
