#pragma once

#include "../core/UsdSceneRenderer.h"
#include "../core/ViewportCamera.h"
#include "../core/CommandHistory.h"
#include "TransformManipulator.h"
#include "IconManager.h"

#include <pxr/usd/usd/stage.h>
#include <pxr/usd/sdf/path.h>
#include <imgui.h>

#include <functional>
#include <memory>
#include <vector>
#include <string>

namespace UsdLayerManager {

/// Serialisable snapshot of per-tile render settings.
/// Color correction is global (AppPreferences); render delegate is per-tile.
struct ViewportTileSettings {
    std::string renderDelegate;         ///< empty = use AppPreferences default on first load
    bool        showGrid         = true;
    bool        showCameraGuide  = false;
    std::string aov              = "color";
    bool        aaEnabled        = false;
    float       bgColorR         = 0.15f;
    float       bgColorG         = 0.15f;
    float       bgColorB         = 0.15f;
    int         bboxMode         = 0;
    bool        ambientLightOnly = true;
    bool        domeLightEnabled = false;
    int         shadingMode      = 0;
    // CullStyle::Nothing — matches usdview's default (viewSettingsDataModel
    // cullBackfaces=False -> CULL_STYLE_NOTHING).  BackUnlessDoubleSided drops
    // back faces on geometry that isn't authored doubleSided, which hdEmbree
    // applies to occlusion rays as well, so interiors shade as single-sided.
    int         cullStyle        = 1; // CullStyle::Nothing
    bool        showGuides       = false;
    bool        showProxy        = true;
    bool        showRender       = false;
};

/// Named orthographic view directions.
/// When m_orthoView != None the tile renders an orthographic camera locked to
/// that world-space direction; the free-camera orbital state (center + dist)
/// is reused for pan and zoom so each tile keeps an independent view.
enum class OrthoView {
    None,    ///< Not an ortho view — free camera or USD camera prim
    Top,
    Bottom,
    Front,
    Back,
    Left,
    Right,
};

/// A single viewport tile.
///
/// Owns its own ViewportCamera, UsdSceneRenderer, and all per-tile settings
/// (grid, AA, background colour, bbox mode, render delegate).  Selection state
/// and the TransformManipulator are owned by the ViewportPanel container and
/// passed in per frame so they can be shared across tiles.
class ViewportTile {
public:
    ViewportTile();
    ~ViewportTile();

    // ── Setup (called once by container) ────────────────────────────────────
    void SetStage(pxr::UsdStageRefPtr stage);
    void SetIconManager(IconManager* icons)  { m_iconManager = icons; }
    void SetCommandHistory(CommandHistory* h);

    /// displayTime — frame used for all evaluation (render, cameras, bboxes).
    /// editTime    — frame writes are authored at (Default unless Auto-Key).
    void SetTimeCodes(pxr::UsdTimeCode displayTime, pxr::UsdTimeCode editTime);

    // ── Selection sync ───────────────────────────────────────────────────────
    /// Called by the container to broadcast the authoritative selection.
    /// Updates the local shadow copy and pushes it into the renderer highlight.
    void SetSelectedPaths(const pxr::SdfPathVector& paths,
                          const std::string&         primaryPath);

    // ── Per-frame render ─────────────────────────────────────────────────────
    /// Render this tile inside an ImGui child window.
    ///
    /// @param tileIndex    Unique index used to disambiguate ImGui IDs.
    /// @param pos          Screen-space top-left of this tile's area.
    /// @param size         Pixel dimensions of this tile's area.
    /// @param isFocused    If true the transform gizmo renders here.
    /// @param manipulator  Shared manipulator owned by the container.
    /// @param dividerActive When true a split-handle drag is active (or the
    ///                      mouse is over one), so rect-selection is suppressed.
    void Render(int tileIndex, ImVec2 pos, ImVec2 size,
                bool isFocused, TransformManipulator& manipulator,
                bool dividerActive = false);

    // ── Pick callbacks (assigned by container after construction) ────────────
    std::function<void(const std::string&)>              OnPrimPicked;
    std::function<void(const std::vector<std::string>&)> OnPrimsPickedRect;

    // ── Per-frame state queries ──────────────────────────────────────────────
    /// True when the user clicked LMB inside this tile during the last Render.
    bool WasClickedThisFrame()  const { return m_wasClickedThisFrame;  }
    /// True when the mouse was hovering this tile during the last Render.
    bool IsHoveredThisFrame()   const { return m_wasHoveredThisFrame;  }

    // ── Forwarding accessors ─────────────────────────────────────────────────
    ViewportCamera&   GetCamera()   { return m_camera;   }
    UsdSceneRenderer& GetRenderer() { return m_renderer; }

    void FrameScene();

    /// Snapshot all current render settings into a portable struct.
    ViewportTileSettings GetSettings() const;
    /// Apply a previously-saved settings snapshot (safe to call before first Render).
    void ApplySettings(const ViewportTileSettings& s);

private:
    // ── Render sub-functions ─────────────────────────────────────────────────
    pxr::GfCamera ResolveCamera();
    /// Build an orthographic GfCamera from the current center/dist state.
    pxr::GfCamera BuildOrthoCamera() const;
    void          HandleInput(bool isFocused, TransformManipulator& manipulator,
                              bool dividerActive);
    void          DrawSelectionRect();
    void          RenderContextMenu(int tileIndex);
    void          RenderCompactToolbar(int tileIndex);
    void          RenderManipulatorOverlay(TransformManipulator& manipulator);

    // ── Camera helpers ───────────────────────────────────────────────────────
    void          RefreshCameraList();
    void          TrySwitchToFreeCamera();
    void          InitCameraNavigation();
    pxr::GfVec3d  ComputeGizmoPivot() const;

    // ── Core components ──────────────────────────────────────────────────────
    ViewportCamera      m_camera;
    UsdSceneRenderer    m_renderer;
    IconManager*        m_iconManager    = nullptr;
    CommandHistory*     m_commandHistory = nullptr;

    // ── Timeline time codes (synced by container) ────────────────────────────
    pxr::UsdTimeCode m_displayTime = pxr::UsdTimeCode::Default();
    pxr::UsdTimeCode m_editTime    = pxr::UsdTimeCode::Default();

    // ── Stage + camera list ──────────────────────────────────────────────────
    pxr::UsdStageRefPtr       m_stage;
    std::vector<pxr::SdfPath> m_cameraPaths;
    int                       m_selectedCameraIndex = 0;
    bool                      m_cameraListDirty     = true;

    // ── Camera navigation mouse state ────────────────────────────────────────
    float m_lastMouseX  = 0.f;
    float m_lastMouseY  = 0.f;
    bool  m_isOrbiting  = false;
    bool  m_isPanning   = false;
    bool  m_isDollying  = false;

    // ── Rect selection state ─────────────────────────────────────────────────
    bool   m_isRectSelecting  = false;
    bool   m_rectDragStarted  = false;
    ImVec2 m_rectAnchor       = {0.f, 0.f};
    ImVec2 m_rectCurrent      = {0.f, 0.f};
    static constexpr float kRectDragThreshold = 5.0f;

    // ── Viewport dimensions (updated each Render) ────────────────────────────
    int    m_viewWidth      = 0;
    int    m_viewHeight     = 0;
    ImVec2 m_imageScreenPos = {0.f, 0.f};

    // ── Selection shadow (synced by container) ───────────────────────────────
    pxr::SdfPathVector m_selectedSdfPaths;
    std::string        m_selectedPrimPath;

    // ── GfCamera cache ───────────────────────────────────────────────────────
    pxr::GfCamera m_lastComputedGfCamera;
    bool          m_hasLastGfCamera          = false;

    // ── Free-camera saved state (before switching to a USD cam prim) ─────────
    pxr::GfCamera m_savedFreeCameraState;
    bool          m_hasSavedFreeCameraState  = false;

    // ── USD camera prim navigation state ────────────────────────────────────
    bool         m_isDrivingUsdCamPrim = false;
    pxr::SdfPath m_drivenUsdCamPath;

    // ── Orthographic view ────────────────────────────────────────────────────
    OrthoView m_orthoView = OrthoView::None;

    // ── Per-frame interaction flags ──────────────────────────────────────────
    bool m_wasClickedThisFrame  = false;
    bool m_wasHoveredThisFrame  = false;

    // ── Renderer settings dialog ─────────────────────────────────────────────
    bool                                   m_showRendererSettings    = false;
    pxr::TfToken                           m_cachedSettingsRendererId;
    pxr::UsdImagingGLRendererSettingsList  m_cachedRendererSettings;
    void RenderRendererSettingsDialog(int tileIndex);
};

} // namespace UsdLayerManager
