#include "../src/utils/Logger.h"
#include "../src/core/UsdSceneRenderer.h"
#include "../src/core/ViewportCamera.h"
#include "../src/utils/GLExt.h"
#include <pxr/usd/usd/stage.h>
#include <pxr/usd/usd/primRange.h>
#include <pxr/usd/usdGeom/cube.h>
#include <pxr/usd/usdGeom/sphere.h>
#include <pxr/usd/usdGeom/xform.h>
#include <pxr/usdImaging/usdImagingGL/engine.h>
#include <pxr/base/plug/registry.h>
#include <Windows.h>
#include <iostream>
#include <cmath>
#include <string>
#include <vector>

using namespace UsdLayerManager;
using namespace pxr;

static HWND g_hwnd = nullptr;
static HDC g_hdc = nullptr;
static HGLRC g_hglrc = nullptr;

static bool CreateGLContext() {
    WNDCLASSEXW wc = { sizeof(wc), CS_OWNDC, DefWindowProcW, 0L, 0L,
                        GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr,
                        L"RendererDiag", nullptr };
    RegisterClassExW(&wc);
    g_hwnd = CreateWindowW(wc.lpszClassName, L"RendererDiag", WS_OVERLAPPEDWINDOW,
                           100, 100, 800, 600, nullptr, nullptr, wc.hInstance, nullptr);
    if (!g_hwnd) return false;
    g_hdc = GetDC(g_hwnd);
    PIXELFORMATDESCRIPTOR pfd = {};
    pfd.nSize = sizeof(pfd); pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 32; pfd.cDepthBits = 24;
    int pf = ChoosePixelFormat(g_hdc, &pfd);
    SetPixelFormat(g_hdc, pf, &pfd);
    g_hglrc = wglCreateContext(g_hdc);
    wglMakeCurrent(g_hdc, g_hglrc);
    return true;
}

static void DestroyGLContext() {
    if (g_hglrc) { wglMakeCurrent(nullptr, nullptr); wglDeleteContext(g_hglrc); }
    if (g_hdc) { ReleaseDC(g_hwnd, g_hdc); }
    if (g_hwnd) { DestroyWindow(g_hwnd); UnregisterClassW(L"RendererDiag", GetModuleHandle(nullptr)); }
}

static void RegisterPluginsManually() {
    char exePath[MAX_PATH];
    GetModuleFileNameA(nullptr, exePath, MAX_PATH);
    std::string exeDir(exePath);
    size_t lastSlash = exeDir.find_last_of("\\/");
    if (lastSlash != std::string::npos) exeDir = exeDir.substr(0, lastSlash);

    std::string pluginPath = exeDir + "\\usd";
    SetEnvironmentVariableA("PXR_PLUGINPATH_NAME", pluginPath.c_str());
    std::cout << "PXR_PLUGINPATH_NAME=" << pluginPath << std::endl;

    std::vector<std::string> pluginPaths;
    WIN32_FIND_DATAA findData;
    std::string searchPattern = pluginPath + "\\*";
    HANDLE hFind = FindFirstFileA(searchPattern.c_str(), &findData);
    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                std::string dirName(findData.cFileName);
                if (dirName != "." && dirName != "..") {
                    std::string plugInfoPath = pluginPath + "\\" + dirName + "\\resources\\plugInfo.json";
                    DWORD attrs = GetFileAttributesA(plugInfoPath.c_str());
                    if (attrs != INVALID_FILE_ATTRIBUTES && !(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
                        pluginPaths.push_back(plugInfoPath);
                    }
                }
            }
        } while (FindNextFileA(hFind, &findData));
        FindClose(hFind);
    }

    std::cout << "Found " << pluginPaths.size() << " plugInfo.json files" << std::endl;
    for (const auto& p : pluginPaths) {
        std::cout << "  " << p << std::endl;
    }

    if (!pluginPaths.empty()) {
        auto& registry = PlugRegistry::GetInstance();
        auto registered = registry.RegisterPlugins(pluginPaths);
        std::cout << "Registered " << registered.size() << " plugins" << std::endl;
    }
}

int main() {
    std::cout << "=== USD Renderer Diagnostic ===" << std::endl;

    RegisterPluginsManually();

    Logger::Instance().SetLogLevel(LogLevel::Info);

    if (!CreateGLContext()) {
        std::cerr << "FATAL: Cannot create GL context" << std::endl;
        return 1;
    }
    GL::InitExtensions();

    UsdStageRefPtr stage = UsdStage::CreateInMemory();
    UsdGeomCube cube = UsdGeomCube::Define(stage, SdfPath("/TestCube"));
    cube.GetSizeAttr().Set(50.0);
    cube.GetDisplayColorAttr().Set(VtVec3fArray{{1.0f, 0.0f, 0.0f}});
    cube.GetDisplayOpacityAttr().Set(VtFloatArray{1.0f});

    std::cout << "\n--- Creating UsdImagingGLEngine ---" << std::endl;

    SdfPathVector excludedPaths;
    UsdImagingGLEngine::Parameters params;
    params.rootPath = stage->GetPseudoRoot().GetPath();
    params.excludedPaths = excludedPaths;

    auto engine = std::make_shared<UsdImagingGLEngine>(params);
    if (!engine) {
        std::cerr << "FATAL: UsdImagingGLEngine creation failed" << std::endl;
        return 1;
    }

    auto plugins = engine->GetRendererPlugins();
    std::cout << "Renderer plugins found: " << plugins.size() << std::endl;
    for (const auto& p : plugins) {
        std::string dn = engine->GetRendererDisplayName(p);
        std::cout << "  Plugin: " << p.GetText() << " -> " << dn << std::endl;
    }

    TfToken currentRenderer = engine->GetCurrentRendererId();
    std::cout << "Current renderer: " << (currentRenderer.IsEmpty() ? "(empty)" : currentRenderer.GetText()) << std::endl;

    if (currentRenderer.IsEmpty() && !plugins.empty()) {
        engine->SetRendererPlugin(plugins[0]);
        currentRenderer = engine->GetCurrentRendererId();
        std::cout << "Forced renderer: " << (currentRenderer.IsEmpty() ? "(still empty!)" : currentRenderer.GetText()) << std::endl;
    }

    if (plugins.empty()) {
        std::cerr << "\nFATAL: No renderer plugins found!" << std::endl;
        return 1;
    }

    std::cout << "\n--- Testing UsdSceneRenderer ---" << std::endl;

    UsdSceneRenderer renderer;
    renderer.SetStage(stage);

    ViewportCamera camera;
    camera.SetStage(stage);
    GfRange3d bounds = renderer.ComputeStageBounds();
    std::cout << "Bounds empty: " << (bounds.IsEmpty() ? "yes" : "no") << std::endl;
    if (!bounds.IsEmpty()) {
        camera.FrameBoundingBox(bounds);
    }
    camera.SetAspectRatio(800.0f / 600.0f);

    renderer.SetCameraState(camera.GetViewMatrix(), camera.GetProjectionMatrix());
    renderer.Render(800, 600);

    uint32_t texId = renderer.GetColorTextureID();
    std::cout << "Color texture ID after render: " << texId << std::endl;
    std::cout << (texId != 0 ? "RENDER OK - texture produced" : "RENDER FAILED - no texture") << std::endl;

    DestroyGLContext();
    return 0;
}
