/// Pane implementations for the editor
///
/// Each pane type has its own module with implementation details.
/// Panes can hold local state and access shared state through SharedPaneState.

use eframe::egui;
use lightningbeam_core::{pane::PaneType, tool::Tool};
use uuid::Uuid;

// Type alias for node paths (matches main.rs)
pub type NodePath = Vec<usize>;

/// Handler information for view actions (zoom, pan, etc.)
/// Used for two-phase dispatch: register during render, execute after
#[derive(Clone)]
pub struct ViewActionHandler {
    pub priority: u32,
    pub pane_path: NodePath,
    pub zoom_center: egui::Vec2,
}

/// Clip type for drag-and-drop operations
/// Distinguishes between different clip/layer type combinations
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DragClipType {
    /// Vector animation clip
    Vector,
    /// Video clip
    Video,
    /// Sampled audio clip (WAV, MP3, etc.)
    AudioSampled,
    /// MIDI clip
    AudioMidi,
    /// Static image asset
    Image,
    /// Effect (shader-based visual effect)
    Effect,
}

/// Information about an asset being dragged from the Asset Library
#[derive(Debug, Clone)]
pub struct DraggingAsset {
    /// The clip ID being dragged
    pub clip_id: Uuid,
    /// Type of clip (determines compatible layer types)
    pub clip_type: DragClipType,
    /// Display name
    pub name: String,
    /// Duration in seconds
    #[allow(dead_code)] // Populated during drag, consumed when drag-and-drop features expand
    pub duration: f64,
    /// Dimensions (width, height) for vector/video clips, None for audio
    pub dimensions: Option<(f64, f64)>,
    /// Optional linked audio clip ID (for video clips with extracted audio)
    pub linked_audio_clip_id: Option<Uuid>,
}

pub mod toolbar;
pub mod stage;
pub mod timeline;
pub mod infopanel;
pub mod outliner;
pub mod piano_roll;
pub mod virtual_piano;
pub mod node_editor;
pub mod node_graph;
pub mod preset_browser;
pub mod asset_library;
pub mod shader_editor;

/// Which color mode is active for the eyedropper tool
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorMode {
    Fill,
    Stroke,
}

impl Default for ColorMode {
    fn default() -> Self {
        ColorMode::Fill
    }
}

/// Helper functions for layer/clip type matching and creation

/// Check if a clip type matches a layer type
pub fn layer_matches_clip_type(layer: &lightningbeam_core::layer::AnyLayer, clip_type: DragClipType) -> bool {
    use lightningbeam_core::layer::*;
    match (layer, clip_type) {
        (AnyLayer::Vector(_), DragClipType::Vector) => true,
        (AnyLayer::Vector(_), DragClipType::Image) => true, // Images go on vector layers as shapes
        (AnyLayer::Video(_), DragClipType::Video) => true,
        (AnyLayer::Audio(audio), DragClipType::AudioSampled) => {
            audio.audio_layer_type == AudioLayerType::Sampled
        }
        (AnyLayer::Audio(audio), DragClipType::AudioMidi) => {
            audio.audio_layer_type == AudioLayerType::Midi
        }
        (AnyLayer::Effect(_), DragClipType::Effect) => true,
        _ => false,
    }
}

/// Create a new layer of the appropriate type for a clip
pub fn create_layer_for_clip_type(clip_type: DragClipType, name: &str) -> lightningbeam_core::layer::AnyLayer {
    use lightningbeam_core::layer::*;
    use lightningbeam_core::effect_layer::EffectLayer;
    match clip_type {
        DragClipType::Vector => AnyLayer::Vector(VectorLayer::new(name)),
        DragClipType::Video => AnyLayer::Video(VideoLayer::new(name)),
        DragClipType::AudioSampled => AnyLayer::Audio(AudioLayer::new_sampled(name)),
        DragClipType::AudioMidi => AnyLayer::Audio(AudioLayer::new_midi(name)),
        // Images are placed as shapes on vector layers, not their own layer type
        DragClipType::Image => AnyLayer::Vector(VectorLayer::new(name)),
        DragClipType::Effect => AnyLayer::Effect(EffectLayer::new(name)),
    }
}

/// Find an existing sampled audio track in the document
/// Returns the layer ID if found, None otherwise
pub fn find_sampled_audio_track(document: &lightningbeam_core::document::Document) -> Option<uuid::Uuid> {
    use lightningbeam_core::layer::*;
    for layer in &document.root.children {
        if let AnyLayer::Audio(audio_layer) = layer {
            if audio_layer.audio_layer_type == AudioLayerType::Sampled {
                return Some(audio_layer.layer.id);
            }
        }
    }
    None
}

/// Shared state that all panes can access
pub struct SharedPaneState<'a> {
    pub tool_icon_cache: &'a mut crate::ToolIconCache,
    #[allow(dead_code)] // Used by pane chrome rendering in main.rs
    pub icon_cache: &'a mut crate::IconCache,
    pub selected_tool: &'a mut Tool,
    pub fill_color: &'a mut egui::Color32,
    pub stroke_color: &'a mut egui::Color32,
    /// Tracks which color (fill or stroke) was last interacted with, for eyedropper tool
    pub active_color_mode: &'a mut ColorMode,
    pub pending_view_action: &'a mut Option<crate::menu::MenuAction>,
    /// Tracks the priority of the best fallback pane for view actions
    /// Lower number = higher priority. None = no fallback pane seen yet
    /// Priority order: Stage(0) > Timeline(1) > PianoRoll(2) > NodeEditor(3)
    pub fallback_pane_priority: &'a mut Option<u32>,
    pub theme: &'a crate::theme::Theme,
    /// Registry of handlers for the current pending action
    /// Panes register themselves here during render, execution happens after
    pub pending_handlers: &'a mut Vec<ViewActionHandler>,
    /// Action executor for immediate action execution (for shape tools to avoid flicker)
    /// Also provides read-only access to the document via action_executor.document()
    pub action_executor: &'a mut lightningbeam_core::action::ActionExecutor,
    /// Current selection state (mutable for tools to modify)
    pub selection: &'a mut lightningbeam_core::selection::Selection,
    /// Currently active layer ID
    pub active_layer_id: &'a mut Option<uuid::Uuid>,
    /// Current tool interaction state (mutable for tools to modify)
    pub tool_state: &'a mut lightningbeam_core::tool::ToolState,
    /// Actions to execute after rendering completes (two-phase dispatch)
    pub pending_actions: &'a mut Vec<Box<dyn lightningbeam_core::action::Action>>,
    /// Draw tool configuration
    pub draw_simplify_mode: &'a mut lightningbeam_core::tool::SimplifyMode,
    pub rdp_tolerance: &'a mut f64,
    pub schneider_max_error: &'a mut f64,
    /// Audio engine controller for playback control (wrapped in Arc<Mutex<>> for thread safety)
    pub audio_controller: Option<&'a std::sync::Arc<std::sync::Mutex<daw_backend::EngineController>>>,
    /// Video manager for video decoding and frame caching
    pub video_manager: &'a std::sync::Arc<std::sync::Mutex<lightningbeam_core::video::VideoManager>>,
    /// Mapping from Document layer UUIDs to daw-backend TrackIds
    pub layer_to_track_map: &'a std::collections::HashMap<Uuid, daw_backend::TrackId>,
    /// Global playback state
    pub playback_time: &'a mut f64,  // Current playback position in seconds
    pub is_playing: &'a mut bool,    // Whether playback is currently active
    /// Recording state
    pub is_recording: &'a mut bool,  // Whether recording is currently active
    pub recording_clips: &'a mut std::collections::HashMap<uuid::Uuid, u32>, // layer_id -> clip_id
    pub recording_start_time: &'a mut f64,  // Playback time when recording started
    pub recording_layer_id: &'a mut Option<uuid::Uuid>,  // Layer being recorded to
    /// Asset being dragged from Asset Library (for cross-pane drag-and-drop)
    pub dragging_asset: &'a mut Option<DraggingAsset>,
    // Tool-specific options for infopanel
    /// Stroke width for drawing tools (Draw, Rectangle, Ellipse, Line, Polygon)
    pub stroke_width: &'a mut f64,
    /// Whether to fill shapes when drawing (Rectangle, Ellipse, Polygon)
    pub fill_enabled: &'a mut bool,
    /// Fill gap tolerance for paint bucket tool
    pub paint_bucket_gap_tolerance: &'a mut f64,
    /// Number of sides for polygon tool
    pub polygon_sides: &'a mut u32,
    /// Cache of MIDI events for rendering (keyed by backend midi_clip_id).
    /// Mutable so panes can update the cache immediately on edits (avoiding 1-frame snap-back).
    /// NOTE: If an action later fails during execution, the cache may be out of sync with the
    /// backend. This is acceptable because MIDI note edits are simple and unlikely to fail.
    /// Undo/redo rebuilds affected entries from the backend to restore consistency.
    pub midi_event_cache: &'a mut std::collections::HashMap<u32, Vec<(f64, u8, u8, bool)>>,
    /// Audio pool indices that got new raw audio data this frame (for thumbnail invalidation)
    pub audio_pools_with_new_waveforms: &'a std::collections::HashSet<usize>,
    /// Raw audio samples for GPU waveform rendering (pool_index -> (samples, sample_rate, channels))
    pub raw_audio_cache: &'a std::collections::HashMap<usize, (Vec<f32>, u32, u32)>,
    /// Pool indices needing GPU waveform texture upload
    pub waveform_gpu_dirty: &'a mut std::collections::HashSet<usize>,
    /// Effect ID to load into shader editor (set by asset library, consumed by shader editor)
    pub effect_to_load: &'a mut Option<Uuid>,
    /// Queue for effect thumbnail requests (effect IDs to generate thumbnails for)
    pub effect_thumbnail_requests: &'a mut Vec<Uuid>,
    /// Cache of generated effect thumbnails (effect_id -> RGBA data)
    pub effect_thumbnail_cache: &'a std::collections::HashMap<Uuid, Vec<u8>>,
    /// Effect IDs whose thumbnails should be invalidated (e.g., after shader edit)
    pub effect_thumbnails_to_invalidate: &'a mut Vec<Uuid>,
    /// Surface texture format for GPU rendering (Rgba8Unorm or Bgra8Unorm depending on platform)
    pub target_format: wgpu::TextureFormat,
}

/// Trait for pane rendering
///
/// Panes implement this trait to provide custom rendering logic.
/// The header is optional and typically used for controls (e.g., Timeline playback).
/// The content area is the main body of the pane.
pub trait PaneRenderer {
    /// Render the optional header section with controls
    ///
    /// Returns true if a header was rendered, false if no header
    fn render_header(&mut self, _ui: &mut egui::Ui, _shared: &mut SharedPaneState) -> bool {
        false // Default: no header
    }

    /// Render the main content area
    fn render_content(
        &mut self,
        ui: &mut egui::Ui,
        rect: egui::Rect,
        path: &NodePath,
        shared: &mut SharedPaneState,
    );

    /// Get the display name of this pane
    #[allow(dead_code)] // Implemented by all panes, dispatch infrastructure complete
    fn name(&self) -> &str;
}

/// Enum wrapper for all pane implementations (enum dispatch pattern)
pub enum PaneInstance {
    Stage(stage::StagePane),
    Timeline(timeline::TimelinePane),
    Toolbar(toolbar::ToolbarPane),
    Infopanel(infopanel::InfopanelPane),
    Outliner(outliner::OutlinerPane),
    PianoRoll(piano_roll::PianoRollPane),
    VirtualPiano(virtual_piano::VirtualPianoPane),
    NodeEditor(node_editor::NodeEditorPane),
    PresetBrowser(preset_browser::PresetBrowserPane),
    AssetLibrary(asset_library::AssetLibraryPane),
    ShaderEditor(shader_editor::ShaderEditorPane),
}

impl PaneInstance {
    /// Create a new pane instance for the given type
    pub fn new(pane_type: PaneType) -> Self {
        match pane_type {
            PaneType::Stage => PaneInstance::Stage(stage::StagePane::new()),
            PaneType::Timeline => PaneInstance::Timeline(timeline::TimelinePane::new()),
            PaneType::Toolbar => PaneInstance::Toolbar(toolbar::ToolbarPane::new()),
            PaneType::Infopanel => PaneInstance::Infopanel(infopanel::InfopanelPane::new()),
            PaneType::Outliner => PaneInstance::Outliner(outliner::OutlinerPane::new()),
            PaneType::PianoRoll => PaneInstance::PianoRoll(piano_roll::PianoRollPane::new()),
            PaneType::VirtualPiano => PaneInstance::VirtualPiano(virtual_piano::VirtualPianoPane::new()),
            PaneType::NodeEditor => PaneInstance::NodeEditor(node_editor::NodeEditorPane::new()),
            PaneType::PresetBrowser => {
                PaneInstance::PresetBrowser(preset_browser::PresetBrowserPane::new())
            }
            PaneType::AssetLibrary => {
                PaneInstance::AssetLibrary(asset_library::AssetLibraryPane::new())
            }
            PaneType::ShaderEditor => {
                PaneInstance::ShaderEditor(shader_editor::ShaderEditorPane::new())
            }
        }
    }

    /// Get the pane type of this instance
    pub fn pane_type(&self) -> PaneType {
        match self {
            PaneInstance::Stage(_) => PaneType::Stage,
            PaneInstance::Timeline(_) => PaneType::Timeline,
            PaneInstance::Toolbar(_) => PaneType::Toolbar,
            PaneInstance::Infopanel(_) => PaneType::Infopanel,
            PaneInstance::Outliner(_) => PaneType::Outliner,
            PaneInstance::PianoRoll(_) => PaneType::PianoRoll,
            PaneInstance::VirtualPiano(_) => PaneType::VirtualPiano,
            PaneInstance::NodeEditor(_) => PaneType::NodeEditor,
            PaneInstance::PresetBrowser(_) => PaneType::PresetBrowser,
            PaneInstance::AssetLibrary(_) => PaneType::AssetLibrary,
            PaneInstance::ShaderEditor(_) => PaneType::ShaderEditor,
        }
    }
}

impl PaneRenderer for PaneInstance {
    fn render_header(&mut self, ui: &mut egui::Ui, shared: &mut SharedPaneState) -> bool {
        match self {
            PaneInstance::Stage(p) => p.render_header(ui, shared),
            PaneInstance::Timeline(p) => p.render_header(ui, shared),
            PaneInstance::Toolbar(p) => p.render_header(ui, shared),
            PaneInstance::Infopanel(p) => p.render_header(ui, shared),
            PaneInstance::Outliner(p) => p.render_header(ui, shared),
            PaneInstance::PianoRoll(p) => p.render_header(ui, shared),
            PaneInstance::VirtualPiano(p) => p.render_header(ui, shared),
            PaneInstance::NodeEditor(p) => p.render_header(ui, shared),
            PaneInstance::PresetBrowser(p) => p.render_header(ui, shared),
            PaneInstance::AssetLibrary(p) => p.render_header(ui, shared),
            PaneInstance::ShaderEditor(p) => p.render_header(ui, shared),
        }
    }

    fn render_content(
        &mut self,
        ui: &mut egui::Ui,
        rect: egui::Rect,
        path: &NodePath,
        shared: &mut SharedPaneState,
    ) {
        match self {
            PaneInstance::Stage(p) => p.render_content(ui, rect, path, shared),
            PaneInstance::Timeline(p) => p.render_content(ui, rect, path, shared),
            PaneInstance::Toolbar(p) => p.render_content(ui, rect, path, shared),
            PaneInstance::Infopanel(p) => p.render_content(ui, rect, path, shared),
            PaneInstance::Outliner(p) => p.render_content(ui, rect, path, shared),
            PaneInstance::PianoRoll(p) => p.render_content(ui, rect, path, shared),
            PaneInstance::VirtualPiano(p) => p.render_content(ui, rect, path, shared),
            PaneInstance::NodeEditor(p) => p.render_content(ui, rect, path, shared),
            PaneInstance::PresetBrowser(p) => p.render_content(ui, rect, path, shared),
            PaneInstance::AssetLibrary(p) => p.render_content(ui, rect, path, shared),
            PaneInstance::ShaderEditor(p) => p.render_content(ui, rect, path, shared),
        }
    }

    fn name(&self) -> &str {
        match self {
            PaneInstance::Stage(p) => p.name(),
            PaneInstance::Timeline(p) => p.name(),
            PaneInstance::Toolbar(p) => p.name(),
            PaneInstance::Infopanel(p) => p.name(),
            PaneInstance::Outliner(p) => p.name(),
            PaneInstance::PianoRoll(p) => p.name(),
            PaneInstance::VirtualPiano(p) => p.name(),
            PaneInstance::NodeEditor(p) => p.name(),
            PaneInstance::PresetBrowser(p) => p.name(),
            PaneInstance::AssetLibrary(p) => p.name(),
            PaneInstance::ShaderEditor(p) => p.name(),
        }
    }
}
