#pragma once

#include "rust/cxx.h"

#include <pxr/base/gf/matrix4d.h>
#include <pxr/base/gf/vec4f.h>
#include <pxr/imaging/glf/simpleLight.h>
#include <pxr/imaging/glf/simpleMaterial.h>
#include <pxr/imaging/hd/sceneIndex.h>
#include <pxr/imaging/hgi/hgi.h>
#include <pxr/usd/usd/stage.h>
#include <pxr/usdImaging/usdImaging/stageSceneIndex.h>
#include <pxr/usdImaging/usdImagingGL/engine.h>

#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>

namespace hydra_rs {

struct SceneIndex {
    // Owns the UsdStageRefPtr that backs the scene index — the imaging
    // delegate keeps a non-owning reference, so the stage must outlive it.
    pxr::UsdStageRefPtr stage_owner;
    pxr::HdSceneIndexBaseRefPtr scene_index;

    rust::String stage_root() const;
    size_t prim_count() const;
    std::unique_ptr<std::vector<std::string>> prim_paths() const;
};

std::unique_ptr<SceneIndex> populate_from_path(rust::Str usd_path);

// Render delegate plugin IDs registered in this process.
std::unique_ptr<std::vector<std::string>> list_render_delegate_ids();

// Stateful renderer. Holds a stage, an HGI, and a UsdImagingGLEngine across
// render() calls so a viewport can re-render at interactive rates without
// rebuilding any of that scaffolding. Construction order in the struct is
// load-bearing: hgi MUST destruct after engine, because engine references it.
struct Renderer {
    pxr::UsdStageRefPtr stage;
    pxr::HgiUniquePtr hgi;
    std::unique_ptr<pxr::UsdImagingGLEngine> engine;

    pxr::GfMatrix4d view_matrix;
    pxr::GfMatrix4d proj_matrix;

    pxr::GlfSimpleLightVector explicit_lights;
    pxr::GlfSimpleMaterial material;
    pxr::GfVec4f scene_ambient = pxr::GfVec4f(0.05f, 0.05f, 0.05f, 1.0f);
    pxr::GfVec4f clear_color = pxr::GfVec4f(0.1f, 0.1f, 0.15f, 1.0f);
    bool use_default_lighting = true;

    uint32_t width = 256;
    uint32_t height = 256;
    double frame = 0.0;
    bool default_frame = true;

    void set_size(uint32_t w, uint32_t h);
    void set_camera_matrices(rust::Slice<const float> view,
                             rust::Slice<const float> projection);
    void set_time(double time);
    void use_default_time();

    void clear_lights();
    void use_default_light();
    void add_distant_light(float dx, float dy, float dz,
                           float r, float g, float b, float intensity);
    void add_positional_light(float px, float py, float pz,
                              float r, float g, float b, float intensity);

    void set_clear_color(float r, float g, float b, float a);

    rust::String current_renderer() const;
    bool set_renderer_plugin(rust::Str plugin_id) const;

    std::unique_ptr<std::vector<uint8_t>> render_color() const;
};

std::unique_ptr<Renderer> create_renderer(rust::Str usd_path,
                                          rust::Str render_delegate_id);

// Convenience wrapper: spin up a renderer, render once, throw away.
std::unique_ptr<std::vector<uint8_t>> render_to_rgba(
    rust::Str usd_path,
    rust::Str render_delegate_id,
    uint32_t width,
    uint32_t height);

}  // namespace hydra_rs
