// DAW Backend - Phase 6: Hierarchical Tracks
//
// A DAW backend with timeline-based playback, clips, audio pool, effects, and hierarchical track groups.
// Supports multiple tracks, mixing, per-track volume/mute/solo, shared audio data, effect chains, and nested groups.
// Uses lock-free command queues, cpal for audio I/O, and symphonia for audio file decoding.

pub mod audio;
pub mod command;
pub mod dsp;
pub mod effects;
pub mod io;
pub mod tui;

// Re-export commonly used types
pub use audio::{
    AudioClipInstanceId, AudioPool, AudioTrack, AutomationLane, AutomationLaneId, AutomationPoint, BufferPool, Clip, ClipId, CurveType, Engine, EngineController,
    Metatrack, MidiClip, MidiClipId, MidiClipInstance, MidiClipInstanceId, MidiEvent, MidiTrack, ParameterId, PoolAudioFile, Project, RecordingState, RenderContext, Track, TrackId,
    TrackNode,
};
pub use audio::node_graph::{GraphPreset, AudioGraph, PresetMetadata, SerializedConnection, SerializedNode};
pub use command::{AudioEvent, Command, OscilloscopeData};
pub use command::types::AutomationKeyframeData;
pub use io::{load_midi_file, AudioFile, WaveformChunk, WaveformChunkKey, WaveformPeak, WavWriter};

use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};

/// Trait for emitting audio events to external systems (UI, logging, etc.)
/// This allows the DAW backend to remain framework-agnostic
pub trait EventEmitter: Send + Sync {
    /// Emit an audio event
    fn emit(&self, event: AudioEvent);
}

/// Simple audio system that handles cpal initialization internally
pub struct AudioSystem {
    pub controller: EngineController,
    pub stream: cpal::Stream,
    pub sample_rate: u32,
    pub channels: u32,
    /// Event receiver for polling audio events (only present when no EventEmitter is provided)
    pub event_rx: Option<rtrb::Consumer<AudioEvent>>,
    /// Consumer for recording audio mirror (streams recorded samples to UI for live waveform)
    recording_mirror_rx: Option<rtrb::Consumer<f32>>,
}

impl AudioSystem {
    /// Initialize the audio system with default input and output devices
    ///
    /// # Arguments
    /// * `event_emitter` - Optional event emitter for pushing events to external systems
    /// * `buffer_size` - Audio buffer size in frames (128, 256, 512, 1024, etc.)
    ///                   Smaller = lower latency but higher CPU usage. Default: 256
    ///
    /// # Environment Variables
    /// * `DAW_AUDIO_DEBUG=1` - Enable audio callback timing diagnostics. Logs:
    ///   - Device and config info at startup
    ///   - First 10 callback buffer sizes (to detect ALSA buffer variance)
    ///   - Per-overrun timing breakdown (command vs render time)
    ///   - Periodic (~5s) timing summaries (avg/worst/overrun rate)
    pub fn new(
        event_emitter: Option<std::sync::Arc<dyn EventEmitter>>,
        buffer_size: u32,
    ) -> Result<Self, String> {
        let host = cpal::default_host();

        // Get output device
        let output_device = host
            .default_output_device()
            .ok_or("No output device available")?;

        let default_output_config = output_device.default_output_config().map_err(|e| e.to_string())?;
        let sample_rate = default_output_config.sample_rate();
        let channels = default_output_config.channels() as u32;
        let _debug_audio = std::env::var("DAW_AUDIO_DEBUG").map_or(false, |v| v == "1");

        eprintln!("[AUDIO] Device: {:?}, format={:?}, rate={}, channels={}",
            output_device.description().map(|d| d.name().to_string()).unwrap_or_default(), default_output_config.sample_format(), sample_rate, channels);

        // Create queues
        let (command_tx, command_rx) = rtrb::RingBuffer::new(512); // Larger buffer for MIDI + UI commands
        let (event_tx, event_rx) = rtrb::RingBuffer::new(256);
        let (query_tx, query_rx) = rtrb::RingBuffer::new(16); // Smaller buffer for synchronous queries
        let (query_response_tx, query_response_rx) = rtrb::RingBuffer::new(16);

        // Create input ringbuffer for recording (large buffer for audio samples)
        // Buffer size: 10 seconds of audio at 48kHz stereo = 48000 * 2 * 10 = 960000 samples
        let input_buffer_size = (sample_rate * channels * 10) as usize;
        let (mut input_tx, input_rx) = rtrb::RingBuffer::new(input_buffer_size);

        // Create mirror ringbuffer for streaming recorded audio to UI (live waveform)
        let (mirror_tx, mirror_rx) = rtrb::RingBuffer::new(input_buffer_size);

        // Create engine
        let mut engine = Engine::new(sample_rate, channels, command_rx, event_tx, query_rx, query_response_tx);
        engine.set_input_rx(input_rx);
        engine.set_recording_mirror_tx(mirror_tx);
        let controller = engine.get_controller(command_tx, query_tx, query_response_rx);

        // Initialize MIDI input manager for external MIDI devices
        // Create a separate command channel for MIDI input
        let (midi_command_tx, midi_command_rx) = rtrb::RingBuffer::new(256);
        match io::MidiInputManager::new(midi_command_tx) {
            Ok(midi_manager) => {
                println!("MIDI input initialized successfully");
                engine.set_midi_input_manager(midi_manager);
                engine.set_midi_command_rx(midi_command_rx);
            }
            Err(e) => {
                eprintln!("Warning: Failed to initialize MIDI input: {}", e);
                eprintln!("External MIDI controllers will not be available");
            }
        }

        // Build output stream
        let mut output_config: cpal::StreamConfig = default_output_config.into();

        // WASAPI shared mode on Windows does not support fixed buffer sizes.
        // Use the device default on Windows; honor the requested size on other platforms.
        if cfg!(target_os = "windows") {
            output_config.buffer_size = cpal::BufferSize::Default;
        } else {
            output_config.buffer_size = cpal::BufferSize::Fixed(buffer_size);
        }

        let mut output_buffer = vec![0.0f32; 16384];

        let output_stream = output_device
            .build_output_stream(
                &output_config,
                move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
                    let buf = &mut output_buffer[..data.len()];
                    buf.fill(0.0);
                    engine.process(buf);
                    data.copy_from_slice(buf);
                },
                |err| eprintln!("Output stream error: {}", err),
                None,
            )
            .map_err(|e| format!("Failed to build output stream: {e:?}"))?;

        // Get input device
        let input_device = match host.default_input_device() {
            Some(device) => device,
            None => {
                eprintln!("Warning: No input device available, recording will be disabled");
                // Start output stream and return without input
                output_stream.play().map_err(|e| e.to_string())?;

                // Spawn emitter thread if provided
                if let Some(emitter) = event_emitter {
                    Self::spawn_emitter_thread(event_rx, emitter);
                }

                return Ok(Self {
                    controller,
                    stream: output_stream,
                    sample_rate,
                    channels,
                    event_rx: None, // No event receiver when audio device unavailable
                    recording_mirror_rx: None,
                });
            }
        };

        // Get input config using the device's default (most compatible)
        let input_config = match input_device.default_input_config() {
            Ok(config) => {
                let cfg: cpal::StreamConfig = config.into();
                cfg
            }
            Err(e) => {
                eprintln!("Warning: Could not get input config: {}, recording will be disabled", e);
                output_stream.play().map_err(|e| e.to_string())?;

                if let Some(emitter) = event_emitter {
                    Self::spawn_emitter_thread(event_rx, emitter);
                }

                return Ok(Self {
                    controller,
                    stream: output_stream,
                    sample_rate,
                    channels,
                    event_rx: None,
                    recording_mirror_rx: None,
                });
            }
        };

        let input_sample_rate = input_config.sample_rate;
        let input_channels = input_config.channels as u32;
        let output_sample_rate = sample_rate;
        let output_channels = channels;
        let needs_resample = input_sample_rate != output_sample_rate || input_channels != output_channels;

        if needs_resample {
            eprintln!("[AUDIO] Input device: {}Hz {}ch -> resampling to {}Hz {}ch",
                input_sample_rate, input_channels, output_sample_rate, output_channels);
        }

        // Build input stream with resampling if needed
        let input_stream = match input_device
            .build_input_stream(
                &input_config,
                move |data: &[f32], _: &cpal::InputCallbackInfo| {
                    if !needs_resample {
                        for &sample in data {
                            let _ = input_tx.push(sample);
                        }
                    } else {
                        // Resample: linear interpolation from input rate to output rate
                        let in_ch = input_channels as usize;
                        let out_ch = output_channels as usize;
                        let ratio = output_sample_rate as f64 / input_sample_rate as f64;
                        let in_frames = data.len() / in_ch;
                        let out_frames = (in_frames as f64 * ratio) as usize;

                        for i in 0..out_frames {
                            let src_pos = i as f64 / ratio;
                            let src_idx = src_pos as usize;
                            let frac = (src_pos - src_idx as f64) as f32;

                            for ch in 0..out_ch {
                                // Map output channel to input channel
                                let in_ch_idx = ch.min(in_ch - 1);

                                let s0 = if src_idx < in_frames {
                                    data[src_idx * in_ch + in_ch_idx]
                                } else {
                                    0.0
                                };
                                let s1 = if src_idx + 1 < in_frames {
                                    data[(src_idx + 1) * in_ch + in_ch_idx]
                                } else {
                                    s0
                                };

                                let _ = input_tx.push(s0 + frac * (s1 - s0));
                            }
                        }
                    }
                },
                |err| eprintln!("Input stream error: {}", err),
                None,
            ) {
            Ok(stream) => stream,
            Err(e) => {
                eprintln!("Warning: Could not build input stream: {}, recording will be disabled", e);
                output_stream.play().map_err(|e| e.to_string())?;

                if let Some(emitter) = event_emitter {
                    Self::spawn_emitter_thread(event_rx, emitter);
                }

                return Ok(Self {
                    controller,
                    stream: output_stream,
                    sample_rate,
                    channels,
                    event_rx: None,
                    recording_mirror_rx: None,
                });
            }
        };

        // Start both streams
        output_stream.play().map_err(|e| e.to_string())?;
        input_stream.play().map_err(|e| e.to_string())?;

        // Leak the input stream to keep it alive
        Box::leak(Box::new(input_stream));

        // Spawn emitter thread if provided, or store event_rx for manual polling
        let event_rx_option = if let Some(emitter) = event_emitter {
            Self::spawn_emitter_thread(event_rx, emitter);
            None
        } else {
            Some(event_rx)
        };

        Ok(Self {
            controller,
            stream: output_stream,
            sample_rate,
            channels,
            event_rx: event_rx_option,
            recording_mirror_rx: Some(mirror_rx),
        })
    }

    /// Take the recording mirror consumer for streaming recorded audio to UI
    pub fn take_recording_mirror_rx(&mut self) -> Option<rtrb::Consumer<f32>> {
        self.recording_mirror_rx.take()
    }

    /// Spawn a background thread to emit events from the ringbuffer
    fn spawn_emitter_thread(mut event_rx: rtrb::Consumer<AudioEvent>, emitter: std::sync::Arc<dyn EventEmitter>) {
        std::thread::spawn(move || {
            loop {
                // Wait for events and emit them
                if let Ok(event) = event_rx.pop() {
                    emitter.emit(event);
                } else {
                    // No events available, sleep briefly to avoid busy-waiting
                    std::thread::sleep(std::time::Duration::from_millis(1));
                }
            }
        });
    }
}
