#include "ShaderManager.h"
#include "../RHI/RHIDevice.h"

// Embedded shader bytecode
#include "vs_color.bin.h"
#include "fs_color.bin.h"
#include "vs_sprite.bin.h"
#include "fs_sprite.bin.h"

namespace grove {

ShaderManager::~ShaderManager() {
    // Note: shutdown() must be called explicitly with device before destruction
    // We can't call it here because we don't have the device reference
}

void ShaderManager::init(rhi::IRHIDevice& device, const std::string& rendererName) {
    if (m_initialized) {
        return;
    }

    loadBuiltinShaders(device, rendererName);
    m_initialized = true;
}

void ShaderManager::shutdown(rhi::IRHIDevice& device) {
    for (auto& [name, handle] : m_programs) {
        if (handle.isValid()) {
            device.destroy(handle);
        }
    }
    m_programs.clear();
    m_initialized = false;
}

rhi::ShaderHandle ShaderManager::getProgram(const std::string& name) const {
    auto it = m_programs.find(name);
    if (it != m_programs.end()) {
        return it->second;
    }
    return rhi::ShaderHandle{}; // Invalid handle
}

bool ShaderManager::hasProgram(const std::string& name) const {
    return m_programs.find(name) != m_programs.end();
}

void ShaderManager::loadBuiltinShaders(rhi::IRHIDevice& device, const std::string& rendererName) {
    // Select shader bytecode based on renderer
    const uint8_t* vsData = nullptr;
    uint32_t vsSize = 0;
    const uint8_t* fsData = nullptr;
    uint32_t fsSize = 0;

    if (rendererName == "OpenGL") {
        vsData = vs_drawstress_glsl;
        vsSize = sizeof(vs_drawstress_glsl);
        fsData = fs_drawstress_glsl;
        fsSize = sizeof(fs_drawstress_glsl);
    } else if (rendererName == "OpenGL ES") {
        vsData = vs_drawstress_essl;
        vsSize = sizeof(vs_drawstress_essl);
        fsData = fs_drawstress_essl;
        fsSize = sizeof(fs_drawstress_essl);
    } else if (rendererName == "Vulkan") {
        vsData = vs_drawstress_spv;
        vsSize = sizeof(vs_drawstress_spv);
        fsData = fs_drawstress_spv;
        fsSize = sizeof(fs_drawstress_spv);
    } else if (rendererName == "Direct3D 11" || rendererName == "Direct3D 12") {
        vsData = vs_drawstress_dx11;
        vsSize = sizeof(vs_drawstress_dx11);
        fsData = fs_drawstress_dx11;
        fsSize = sizeof(fs_drawstress_dx11);
    } else if (rendererName == "Metal") {
        vsData = vs_drawstress_mtl;
        vsSize = sizeof(vs_drawstress_mtl);
        fsData = fs_drawstress_mtl;
        fsSize = sizeof(fs_drawstress_mtl);
    } else {
        // Fallback to Vulkan (most common in WSL2)
        vsData = vs_drawstress_spv;
        vsSize = sizeof(vs_drawstress_spv);
        fsData = fs_drawstress_spv;
        fsSize = sizeof(fs_drawstress_spv);
    }

    // Create color shader via RHI
    rhi::ShaderDesc shaderDesc;
    shaderDesc.vsData = vsData;
    shaderDesc.vsSize = vsSize;
    shaderDesc.fsData = fsData;
    shaderDesc.fsSize = fsSize;

    rhi::ShaderHandle colorProgram = device.createShader(shaderDesc);

    if (colorProgram.isValid()) {
        m_programs["color"] = colorProgram;
        m_programs["debug"] = colorProgram;
    }

    // Load sprite instancing shader
    loadSpriteShader(device, rendererName);
}

void ShaderManager::loadSpriteShader(rhi::IRHIDevice& device, const std::string& rendererName) {
    const uint8_t* vsData = nullptr;
    uint32_t vsSize = 0;
    const uint8_t* fsData = nullptr;
    uint32_t fsSize = 0;

    if (rendererName == "OpenGL") {
        vsData = vs_sprite_glsl;
        vsSize = sizeof(vs_sprite_glsl);
        fsData = fs_sprite_glsl;
        fsSize = sizeof(fs_sprite_glsl);
    } else if (rendererName == "Vulkan") {
        vsData = vs_sprite_spv;
        vsSize = sizeof(vs_sprite_spv);
        fsData = fs_sprite_spv;
        fsSize = sizeof(fs_sprite_spv);
    } else if (rendererName == "Metal") {
        vsData = vs_sprite_mtl;
        vsSize = sizeof(vs_sprite_mtl);
        fsData = fs_sprite_mtl;
        fsSize = sizeof(fs_sprite_mtl);
    } else if (rendererName == "Direct3D 11" || rendererName == "Direct3D 12") {
        vsData = vs_sprite_dx11;
        vsSize = sizeof(vs_sprite_dx11);
        fsData = fs_sprite_dx11;
        fsSize = sizeof(fs_sprite_dx11);
    } else {
        // Fallback to Vulkan (most common in WSL2)
        vsData = vs_sprite_spv;
        vsSize = sizeof(vs_sprite_spv);
        fsData = fs_sprite_spv;
        fsSize = sizeof(fs_sprite_spv);
    }

    rhi::ShaderDesc shaderDesc;
    shaderDesc.vsData = vsData;
    shaderDesc.vsSize = vsSize;
    shaderDesc.fsData = fsData;
    shaderDesc.fsSize = fsSize;

    rhi::ShaderHandle spriteProgram = device.createShader(shaderDesc);

    if (spriteProgram.isValid()) {
        m_programs["sprite"] = spriteProgram;
    }
}

} // namespace grove
