#pragma once

#include "ImGuiContext.h"
#include "IconManager.h"
#include "StageEditorPanel.h"
#include "SceneHierarchyPanel.h"
#include "RenderLayerPanel.h"
#include "ViewportPanel.h"
#include "PropertyPanel.h"
#include "TimelinePanel.h"
#include "CurveEditorPanel.h"
#include "MaterialEditorPanel.h"
#include "../core/UsdStageManager.h"
#include "../core/LayerManager.h"
#include "../core/RenderLayerManager.h"
#include "../core/PropertyManager.h"
#include "../core/MaterialManager.h"
#include "../core/CommandHistory.h"
#include "../utils/MovieEncoder.h"
#include <memory>
#include <string>

namespace UsdLayerManager {

/// Global (non-per-viewport) rendering preferences persisted to preferences.ini.
struct AppPreferences {
    std::string renderDelegate      = "HdStormRendererPlugin";
    int         colorCorrectionMode = 1; ///< ColorCorrectionMode cast to int (1 = sRGB)
    std::string ocioDisplay;
    std::string ocioView;
    std::string ocioColorSpace;
    std::string ocioLook;
    float materialBrowserWidth = 220.0f; ///< Material Editor left-column width
    float materialPreviewWidth = 320.0f; ///< Material Editor right-column width
    /// When true, per-node pin display mode (Hypershade-style "show all" vs
    /// "show connected only") and revealed hidden pins are authored into USD
    /// customData so they survive reopening the material; when false, that
    /// state is session-only.
    bool keepGraphNodeViewSettingsInUsd = false;
    /// Auto-save the active render layer's SdfLayer shortly after each edit.
    bool autoSaveRenderLayer = true;
};

class Application {
public:
    Application();
    ~Application();

    bool Initialize(const std::string& windowTitle = "USD Layer Manager", int width = 1280, int height = 720);
    void Run();
    void Shutdown();

private:
    void Update();
    void RenderUI();
    void RenderMenuBar();
    void RenderStatusBar();
    void RenderStageInfo();
    void RefreshManagers();

    // File operations
    void OpenUsdFile();
    void CreateNewUsdFile();
    void SaveUsdFile();
    void SaveUsdFileAs();
    void CloseUsdFile();

    // Stage editing operations
    void AddReferenceToStage();
    void CreatePrimOnStage(const std::string& typeName);

    /// Explicitly saves every render-layer SdfLayer, since at most one is
    /// ever in the stage's composition (unmuted) at a time and
    /// UsdStage::Save() only walks layers currently in composition — the
    /// rest would otherwise be silently dropped on save.
    void SaveDirtyRenderLayers();

    /// Per-frame debounce driving the active render layer's auto-save.
    /// Polls SdfLayer::IsDirty() rather than hooking CommandHistory because
    /// many write paths (PropertyPanel's live transform/attribute widgets,
    /// TransformManipulator's gizmo drags, MaterialEditorPanel's shader input
    /// widgets) author straight to the ambient edit target without going
    /// through a command — a command hook would silently miss them.
    void TickRenderLayerAutoSave(float deltaTime);
    /// Saves the active render layer's SdfLayer if it's dirty. Returns true
    /// only when a save actually happened.
    bool FlushActiveRenderLayerSave();
    /// Saves one render layer by identifier if it's dirty; no-op for an empty
    /// id (Default active) or a layer whose previous save failed.
    bool SaveRenderLayerIfDirty(const std::string& layerId);

    // Playblast
    void CapturePlayblastFrame();

    // Preferences
    void LoadPreferences();
    void SavePreferences();
    /// Reconcile persisted OCIO display/view names against the active config.
    /// Falls back to the config defaults when a name doesn't exist, so a stale
    /// or mistyped preference can't silently disable color correction.
    void ValidateOcioPreferences();
    void ApplyPrefsToAllViewports();
    void RenderPreferencesDialog();

    std::unique_ptr<ImGuiContext> m_imguiContext;
    std::unique_ptr<IconManager> m_iconManager;
    std::unique_ptr<UsdStageManager> m_stageManager;
    std::unique_ptr<LayerManager> m_layerManager;
    std::unique_ptr<RenderLayerManager> m_renderLayerManager;
    std::unique_ptr<PropertyManager> m_propertyManager;
    std::unique_ptr<MaterialManager> m_materialManager;
    CommandHistory m_commandHistory;
    std::unique_ptr<StageEditorPanel> m_stageEditorPanel;
    std::unique_ptr<SceneHierarchyPanel> m_sceneHierarchyPanel;
    std::unique_ptr<RenderLayerPanel> m_renderLayerPanel;
    std::unique_ptr<ViewportPanel> m_viewportPanel;
    std::unique_ptr<PropertyPanel> m_propertyPanel;
    std::unique_ptr<TimelinePanel> m_timelinePanel;
    std::unique_ptr<CurveEditorPanel> m_curveEditorPanel;
    std::unique_ptr<MaterialEditorPanel> m_materialEditorPanel;
    bool m_showDemoWindow;
    bool m_showStageInfo;
    bool m_showStageEditor;
    bool m_showRenderLayerPanel;
    bool m_showSceneHierarchy;
    bool m_showViewport;
    bool m_showPropertyPanel;
    bool m_showTimeline;
    bool m_showCurveEditor;
    bool m_showMaterialEditor;
    bool m_running;

    MovieEncoder   m_movieEncoder;
    std::string    m_viewportSettingsPath;
    std::string    m_prefsPath;
    AppPreferences m_prefs;
    bool           m_showPreferences = false;

    // Render-layer auto-save state (see TickRenderLayerAutoSave).
    float       m_renderLayerAutoSaveTimer = 0.0f;
    std::string m_renderLayerAutoSaveLastActiveId;
    // Identifier of a render layer whose Save() failed — retried only after the
    // active layer changes, so a read-only file can't spam the log every tick.
    std::string m_renderLayerAutoSaveFailedId;

    // OCIO edit buffers for Preferences dialog
    char m_prefOcioDisplayBuf[128]    = {};
    char m_prefOcioViewBuf[128]       = {};
    char m_prefOcioColorSpaceBuf[128] = {};
    char m_prefOcioLookBuf[128]       = {};
    bool m_prefOcioFieldsSynced       = false;
};

} // namespace UsdLayerManager
