// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#include "qr_code.h"
#include "../schema/ui_schema.h"
#include "imgui.h"
#include "../../../libs/qrcode/QrCode.hpp"

#ifdef DRAGONX_USE_DX11
#include <d3d11.h>
#else
#include <SDL3/SDL_opengl.h>
#endif
#include <cstdint>
#include <vector>
#include "../../util/logger.h"

#ifdef DRAGONX_USE_DX11
// Forward-declared helper to get D3D11 device from ImGui backend state
static ID3D11Device* GetImGuiD3D11Device()
{
    // ImGui_ImplDX11 stores the device in its backend data
    // Access through the render state stored during init
    ImGuiIO& io = ImGui::GetIO();
    if (!io.BackendRendererUserData) return nullptr;
    // The first field in ImGui_ImplDX11_Data is the device pointer
    return *reinterpret_cast<ID3D11Device**>(io.BackendRendererUserData);
}
#endif

namespace dragonx {
namespace ui {

uintptr_t GenerateQRTexture(const char* data, int* out_width, int* out_height)
{
    if (!data || !data[0]) {
        return 0;
    }
    
    try {
        // Generate QR code
        qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(
            data, qrcodegen::QrCode::Ecc::MEDIUM);
        
        const auto& S = schema::UI();
        int qr_size = qr.getSize();
        auto qrE = S.drawElement("components.qr-code", "module-scale");
        int scale = qrE.size >= 0 ? (int)qrE.size : 4;
        auto qrB = S.drawElement("components.qr-code", "border-modules");
        int border = qrB.size >= 0 ? (int)qrB.size : 2;
        
        int img_size = (qr_size + border * 2) * scale;
        
        // Create pixel data (RGBA)
        std::vector<unsigned char> pixels(img_size * img_size * 4);
        
        for (int y = 0; y < img_size; y++) {
            for (int x = 0; x < img_size; x++) {
                int qx = x / scale - border;
                int qy = y / scale - border;
                
                bool is_black = false;
                if (qx >= 0 && qx < qr_size && qy >= 0 && qy < qr_size) {
                    is_black = qr.getModule(qx, qy);
                }
                
                int idx = (y * img_size + x) * 4;
                unsigned char color = is_black ? 0 : 255;
                pixels[idx + 0] = color;  // R
                pixels[idx + 1] = color;  // G
                pixels[idx + 2] = color;  // B
                pixels[idx + 3] = 255;    // A
            }
        }
        
#ifdef DRAGONX_USE_DX11
        // Create D3D11 texture + shader resource view
        ID3D11Device* device = GetImGuiD3D11Device();
        if (!device) return 0;

        D3D11_TEXTURE2D_DESC desc = {};
        desc.Width = img_size;
        desc.Height = img_size;
        desc.MipLevels = 1;
        desc.ArraySize = 1;
        desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        desc.SampleDesc.Count = 1;
        desc.Usage = D3D11_USAGE_DEFAULT;
        desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;

        D3D11_SUBRESOURCE_DATA initData = {};
        initData.pSysMem = pixels.data();
        initData.SysMemPitch = img_size * 4;

        ID3D11Texture2D* texture = nullptr;
        HRESULT hr = device->CreateTexture2D(&desc, &initData, &texture);
        if (FAILED(hr) || !texture) return 0;

        ID3D11ShaderResourceView* srv = nullptr;
        D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
        srvDesc.Format = desc.Format;
        srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
        srvDesc.Texture2D.MipLevels = 1;

        hr = device->CreateShaderResourceView(texture, &srvDesc, &srv);
        texture->Release();
        if (FAILED(hr) || !srv) return 0;

        if (out_width) *out_width = img_size;
        if (out_height) *out_height = img_size;

        // Return SRV pointer as uintptr_t (cast to ImTextureID when rendering)
        return (uintptr_t)srv;
#else
        // Create OpenGL texture
        GLuint texture;
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        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, img_size, img_size, 0, 
                     GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
        
        if (out_width) *out_width = img_size;
        if (out_height) *out_height = img_size;
        
        return texture;
#endif
        
    } catch (const std::exception& e) {
        DEBUG_LOGF("QR generation failed: %s\n", e.what());
        return 0;
    }
}

void FreeQRTexture(uintptr_t texture_id)
{
    if (texture_id) {
#ifdef DRAGONX_USE_DX11
        ID3D11ShaderResourceView* srv = (ID3D11ShaderResourceView*)texture_id;
        srv->Release();
#else
        GLuint tex = texture_id;
        glDeleteTextures(1, &tex);
#endif
    }
}

void RenderQRCode(uintptr_t texture_id, float size)
{
    if (texture_id) {
        ImGui::Image((ImTextureID)texture_id, ImVec2(size, size));
    } else {
        // Draw placeholder
        ImVec2 cursor = ImGui::GetCursorScreenPos();
        ImDrawList* draw_list = ImGui::GetWindowDrawList();
        
        draw_list->AddRectFilled(
            cursor,
            ImVec2(cursor.x + size, cursor.y + size),
            IM_COL32(200, 200, 200, 255)
        );
        draw_list->AddRect(
            cursor,
            ImVec2(cursor.x + size, cursor.y + size),
            IM_COL32(100, 100, 100, 255)
        );
        
        ImGui::Dummy(ImVec2(size, size));
    }
}

} // namespace ui
} // namespace dragonx
