use std::collections::{BTreeMap, BTreeSet};

/// Top-level song with tempo, sound definitions, and tracks.
#[derive(Debug, Clone)]
pub struct Song {
    /// Tempo in beats per minute.
    pub tempo: f32,
    /// Paths of `.track` files imported for their sound definitions.
    pub imports: Vec<String>,
    /// All sounds available to tracks (locally defined and imported).
    pub sounds: BTreeMap<String, Sound>,
    /// Names of sounds that were defined directly in this song's source
    /// (not pulled in via `@import`).
    pub local_sound_names: BTreeSet<String>,
    /// Tracks in display order.
    pub tracks: Vec<Track>,
}

impl Default for Song {
    fn default() -> Self {
        Self {
            tempo: 120.0,
            imports: Vec::new(),
            sounds: BTreeMap::new(),
            local_sound_names: BTreeSet::new(),
            tracks: Vec::new(),
        }
    }
}

impl Song {
    /// Returns true when the named sound is defined locally (not imported).
    pub fn is_local_sound(&self, name: &str) -> bool {
        self.local_sound_names.contains(name)
    }

    /// Multiplies every track's volume by `factor`.
    pub fn with_volume(mut self, factor: f32) -> Self {
        for track in &mut self.tracks {
            track.volume *= factor;
        }
        self
    }
}

/// Basic waveform shape used by a single harmonic.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Waveform {
    /// Pure sine wave.
    Sine,
    /// Triangle wave (warmer than sine).
    Triangle,
    /// Soft square wave (buzzy, tanh-shaped).
    SoftSquare,
}

/// One spectral component of a sound.
///
/// The harmonic plays at `multiplier × fundamental_frequency`, scaled by
/// `weight`. Multiple harmonics sum to form the timbre.
#[derive(Debug, Clone, Copy)]
pub struct Harmonic {
    /// Waveform of this component.
    pub waveform: Waveform,
    /// Frequency ratio relative to the note's fundamental
    /// (1.0 = fundamental, 2.0 = octave, 1.5 = fifth, 0.5 = sub).
    pub multiplier: f32,
    /// Mix weight (relative loudness within the timbre).
    pub weight: f32,
}

/// Shape of an envelope segment.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Curve {
    /// Straight ramp from 0 to 1 (or 1 to 0).
    #[default]
    Linear,
    /// First-quarter sine wave: starts/ends with smooth tangent.
    Sine,
}

/// One envelope segment: how long it takes and what shape its ramp has.
#[derive(Debug, Clone, Copy, Default)]
pub struct Envelope {
    /// Length of the segment in seconds.
    pub duration: f32,
    /// Ramp shape.
    pub curve: Curve,
}

/// Waveform mix and envelope describing one synthesizable instrument voice.
#[derive(Debug, Clone)]
pub struct Sound {
    /// Spectral components summed to produce the voice.
    pub harmonics: Vec<Harmonic>,
    /// Tanh-saturation amount applied to the mixed signal.
    pub distortion: f32,
    /// Attack envelope at note start (`None` = no fade-in).
    pub attack: Option<Envelope>,
    /// Decay envelope at note end (`None` = no fade-out).
    pub decay: Option<Envelope>,
    /// Voice volume multiplier applied after envelope and distortion.
    pub volume: f32,
}

impl Default for Sound {
    fn default() -> Self {
        Self {
            harmonics: Vec::new(),
            distortion: 0.0,
            attack: None,
            decay: None,
            volume: 1.0,
        }
    }
}

impl Sound {
    /// Classic chiptune lead: triangle-heavy with subtle harmonics.
    pub fn chiptune() -> Self {
        Self {
            harmonics: vec![
                Harmonic {
                    waveform: Waveform::Triangle,
                    multiplier: 1.0,
                    weight: 0.6,
                },
                Harmonic {
                    waveform: Waveform::SoftSquare,
                    multiplier: 1.0,
                    weight: 0.2,
                },
                Harmonic {
                    waveform: Waveform::Sine,
                    multiplier: 2.0,
                    weight: 0.1,
                },
                Harmonic {
                    waveform: Waveform::Triangle,
                    multiplier: 0.5,
                    weight: 0.1,
                },
            ],
            volume: 0.05,
            ..Self::default()
        }
    }

    /// Overdriven guitar with rich harmonics.
    pub fn guitar() -> Self {
        Self {
            harmonics: vec![
                Harmonic {
                    waveform: Waveform::Sine,
                    multiplier: 1.0,
                    weight: 0.3,
                },
                Harmonic {
                    waveform: Waveform::Triangle,
                    multiplier: 1.0,
                    weight: 0.3,
                },
                Harmonic {
                    waveform: Waveform::SoftSquare,
                    multiplier: 1.0,
                    weight: 0.2,
                },
                Harmonic {
                    waveform: Waveform::Sine,
                    multiplier: 2.0,
                    weight: 0.15,
                },
                Harmonic {
                    waveform: Waveform::Sine,
                    multiplier: 1.5,
                    weight: 0.1,
                },
            ],
            distortion: 3.0,
            volume: 0.05,
            ..Self::default()
        }
    }

    /// Deep bass: sine-heavy with sub-octave warmth.
    pub fn bass() -> Self {
        Self {
            harmonics: vec![
                Harmonic {
                    waveform: Waveform::Sine,
                    multiplier: 1.0,
                    weight: 0.5,
                },
                Harmonic {
                    waveform: Waveform::Triangle,
                    multiplier: 1.0,
                    weight: 0.3,
                },
                Harmonic {
                    waveform: Waveform::Triangle,
                    multiplier: 0.5,
                    weight: 0.2,
                },
            ],
            volume: 0.06,
            ..Self::default()
        }
    }

    /// Buzzy bass: distorted with grit.
    pub fn bass_dirty() -> Self {
        Self {
            harmonics: vec![
                Harmonic {
                    waveform: Waveform::Sine,
                    multiplier: 1.0,
                    weight: 0.4,
                },
                Harmonic {
                    waveform: Waveform::Triangle,
                    multiplier: 1.0,
                    weight: 0.2,
                },
                Harmonic {
                    waveform: Waveform::SoftSquare,
                    multiplier: 1.0,
                    weight: 0.1,
                },
                Harmonic {
                    waveform: Waveform::Triangle,
                    multiplier: 0.5,
                    weight: 0.3,
                },
            ],
            distortion: 2.0,
            volume: 0.05,
            ..Self::default()
        }
    }
}

/// One track in a song: mix settings, clips, and an effect chain.
#[derive(Debug, Clone)]
pub struct Track {
    /// Display name.
    pub name: String,
    /// Per-track volume multiplier (1.0 = unity).
    pub volume: f32,
    /// Stereo pan from -1.0 (left) to 1.0 (right).
    pub pan: f32,
    /// Track is silenced.
    pub muted: bool,
    /// Track is in solo mode (silences all non-solo tracks).
    pub solo: bool,
    /// If set, the track's content repeats every N beats.
    pub loop_length: Option<f32>,
    /// Notes and audio clips on this track.
    pub clips: Vec<Clip>,
    /// Effects applied to the rendered track buffer in order.
    pub effects: Vec<Effect>,
}

/// Audio effect applied to a track's rendered output.
#[derive(Debug, Clone, Copy)]
pub enum Effect {
    /// One-pole lowpass at the given cutoff in Hz.
    Lowpass {
        /// Cutoff frequency in Hz.
        cutoff: f32,
    },
    /// One-pole highpass at the given cutoff in Hz.
    Highpass {
        /// Cutoff frequency in Hz.
        cutoff: f32,
    },
    /// Echo with delay (seconds), feedback (0..1), and wet/dry mix (0..1).
    Echo {
        /// Delay length in seconds.
        delay: f32,
        /// Feedback fraction fed back into the delay line.
        feedback: f32,
        /// Wet mix (0 = dry, 1 = full echo).
        mix: f32,
    },
    /// Tremolo: amplitude modulation by a sine LFO.
    Tremolo {
        /// Rate of the LFO in Hz.
        rate: f32,
        /// Modulation depth (0 = off, 1 = full).
        depth: f32,
    },
    /// Bitcrusher: quantize samples to `bits` levels.
    Bitcrusher {
        /// Bit depth (1..16).
        bits: u8,
    },
}

impl Track {
    /// Creates an empty track with the given name and default mix settings.
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            volume: 1.0,
            pan: 0.0,
            muted: false,
            solo: false,
            loop_length: None,
            clips: Vec::new(),
            effects: Vec::new(),
        }
    }
}

/// A single clip on a track.
#[derive(Debug, Clone)]
pub enum Clip {
    /// Synthesized note using a named sound at the given pitch.
    Note {
        /// Name of the sound to synthesize this note with.
        sound: String,
        /// Beat position where the note starts.
        start: f32,
        /// Pitch in semitones relative to A4 (0 = 440Hz, 12 = 880Hz).
        pitch: f32,
        /// Length in beats.
        duration: f32,
    },
    /// Pattern of notes/holds/pauses spread evenly over `total_duration`.
    Pattern {
        /// Name of the sound for all notes in the pattern.
        sound: String,
        /// Beat position where the pattern starts.
        start: f32,
        /// Steps played in order; each consumes `total_duration / steps.len()` beats.
        steps: Vec<PatternStep>,
        /// Total length of the pattern in beats.
        total_duration: f32,
    },
    /// External audio file played at the given beat position.
    Audio {
        /// Path to the audio file (relative to the song file).
        file: String,
        /// Beat position where the file begins playing.
        start: f32,
    },
}

/// One slot inside a pattern.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PatternStep {
    /// Start a new note at the given pitch (semitones relative to A4).
    Pitch(f32),
    /// Continue holding the previous note for one more slot.
    Hold,
    /// Silence for one slot.
    Pause,
}

impl Clip {
    /// Returns the start beat of this clip.
    pub fn start(&self) -> f32 {
        match self {
            Self::Note { start, .. } | Self::Pattern { start, .. } | Self::Audio { start, .. } => {
                *start
            }
        }
    }
}

/// Converts a pitch in semitones (0 = A4) to a frequency in Hz.
pub fn pitch_to_frequency(pitch: f32) -> f32 {
    440.0 * 2.0_f32.powf(pitch / 12.0)
}

/// Returns true if the file name ends in a known audio file extension
/// (`.wav`, `.mp3`, `.ogg`, `.flac`).
pub fn is_audio_filename(name: &str) -> bool {
    let lower = name.to_ascii_lowercase();
    lower.ends_with(".wav")
        || lower.ends_with(".mp3")
        || lower.ends_with(".ogg")
        || lower.ends_with(".flac")
}
