//! Project, Bank, Pattern, and Step structs with serialization.

use std::path::PathBuf;

use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::{DEFAULT_LENGTH, MAX_BANKS, MAX_PATTERNS, MAX_STEPS};

/// Speed multiplier for a pattern, expressed as a rational fraction.
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct PatternSpeed {
    pub num: u8,
    pub denom: u8,
}

impl PatternSpeed {
    pub const EIGHTH: Self = Self { num: 1, denom: 8 };
    pub const FIFTH: Self = Self { num: 1, denom: 5 };
    pub const QUARTER: Self = Self { num: 1, denom: 4 };
    pub const THIRD: Self = Self { num: 1, denom: 3 };
    pub const HALF: Self = Self { num: 1, denom: 2 };
    pub const TWO_THIRDS: Self = Self { num: 2, denom: 3 };
    pub const NORMAL: Self = Self { num: 1, denom: 1 };
    pub const DOUBLE: Self = Self { num: 2, denom: 1 };
    pub const QUAD: Self = Self { num: 4, denom: 1 };
    pub const OCTO: Self = Self { num: 8, denom: 1 };

    const PRESETS: &[Self] = &[
        Self::EIGHTH,
        Self::FIFTH,
        Self::QUARTER,
        Self::THIRD,
        Self::HALF,
        Self::TWO_THIRDS,
        Self::NORMAL,
        Self::DOUBLE,
        Self::QUAD,
        Self::OCTO,
    ];

    /// Return the speed as a floating-point multiplier.
    pub fn multiplier(&self) -> f64 {
        self.num as f64 / self.denom as f64
    }

    /// Format as a human-readable label (e.g. "2x", "1/4x").
    pub fn label(&self) -> String {
        if self.denom == 1 {
            format!("{}x", self.num)
        } else {
            format!("{}/{}x", self.num, self.denom)
        }
    }

    /// Return the next faster preset, or self if already at maximum.
    pub fn next(&self) -> Self {
        let current = self.multiplier();
        Self::PRESETS
            .iter()
            .find(|p| p.multiplier() > current + 0.0001)
            .copied()
            .unwrap_or(*self)
    }

    /// Return the next slower preset, or self if already at minimum.
    pub fn prev(&self) -> Self {
        let current = self.multiplier();
        Self::PRESETS
            .iter()
            .rev()
            .find(|p| p.multiplier() < current - 0.0001)
            .copied()
            .unwrap_or(*self)
    }

    /// Parse a speed label like "2x" or "1/4x" into a `PatternSpeed`.
    pub fn from_label(s: &str) -> Option<Self> {
        let s = s.trim().trim_end_matches('x');
        if let Some((num, denom)) = s.split_once('/') {
            let num: u8 = num.parse().ok()?;
            let denom: u8 = denom.parse().ok()?;
            if denom == 0 {
                return None;
            }
            return Some(Self { num, denom });
        }
        if let Ok(val) = s.parse::<f64>() {
            if val <= 0.0 || val > 255.0 {
                return None;
            }
            if (val - val.round()).abs() < 0.0001 {
                return Some(Self {
                    num: val.round() as u8,
                    denom: 1,
                });
            }
            for denom in 1..=16u8 {
                let num = val * denom as f64;
                if (num - num.round()).abs() < 0.0001 && (1.0..=255.0).contains(&num) {
                    return Some(Self {
                        num: num.round() as u8,
                        denom,
                    });
                }
            }
        }
        None
    }
}

impl Default for PatternSpeed {
    fn default() -> Self {
        Self::NORMAL
    }
}

impl Serialize for PatternSpeed {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        (self.num, self.denom).serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for PatternSpeed {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        #[derive(Deserialize)]
        #[serde(untagged)]
        enum SpeedFormat {
            Tuple((u8, u8)),
            Legacy(String),
        }

        match SpeedFormat::deserialize(deserializer)? {
            SpeedFormat::Tuple((num, denom)) => Ok(Self { num, denom }),
            SpeedFormat::Legacy(s) => Ok(match s.as_str() {
                "Eighth" => Self::EIGHTH,
                "Quarter" => Self::QUARTER,
                "Half" => Self::HALF,
                "Normal" => Self::NORMAL,
                "Double" => Self::DOUBLE,
                "Quad" => Self::QUAD,
                "Octo" => Self::OCTO,
                _ => Self::NORMAL,
            }),
        }
    }
}

/// Quantization grid for launching patterns.
#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
pub enum LaunchQuantization {
    Immediate,
    Beat,
    #[default]
    Bar,
    Bars2,
    Bars4,
    Bars8,
}

impl LaunchQuantization {
    /// Human-readable label for display.
    pub fn label(&self) -> &'static str {
        match self {
            Self::Immediate => "Immediate",
            Self::Beat => "Beat",
            Self::Bar => "1 Bar",
            Self::Bars2 => "2 Bars",
            Self::Bars4 => "4 Bars",
            Self::Bars8 => "8 Bars",
        }
    }

    /// Cycle to the next longer quantization, clamped at `Bars8`.
    pub fn next(&self) -> Self {
        match self {
            Self::Immediate => Self::Beat,
            Self::Beat => Self::Bar,
            Self::Bar => Self::Bars2,
            Self::Bars2 => Self::Bars4,
            Self::Bars4 => Self::Bars8,
            Self::Bars8 => Self::Bars8,
        }
    }

    /// Cycle to the next shorter quantization, clamped at `Immediate`.
    pub fn prev(&self) -> Self {
        match self {
            Self::Immediate => Self::Immediate,
            Self::Beat => Self::Immediate,
            Self::Bar => Self::Beat,
            Self::Bars2 => Self::Bar,
            Self::Bars4 => Self::Bars2,
            Self::Bars8 => Self::Bars4,
        }
    }
}

/// How a pattern synchronizes when launched: restart or phase-lock.
#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
pub enum SyncMode {
    #[default]
    Reset,
    PhaseLock,
}

impl SyncMode {
    /// Human-readable label for display.
    pub fn label(&self) -> &'static str {
        match self {
            Self::Reset => "Reset",
            Self::PhaseLock => "Phase-Lock",
        }
    }

    /// Toggle between Reset and PhaseLock.
    pub fn toggle(&self) -> Self {
        match self {
            Self::Reset => Self::PhaseLock,
            Self::PhaseLock => Self::Reset,
        }
    }
}

/// What happens when a pattern finishes: loop, stop, or chain to another.
#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
pub enum FollowUp {
    #[default]
    Loop,
    Stop,
    Chain { bank: usize, pattern: usize },
}

impl FollowUp {
    /// Human-readable label for display.
    pub fn label(&self) -> &'static str {
        match self {
            Self::Loop => "Loop",
            Self::Stop => "Stop",
            Self::Chain { .. } => "Chain",
        }
    }

    /// Cycle forward through follow-up modes.
    pub fn next_mode(&self) -> Self {
        match self {
            Self::Loop => Self::Stop,
            Self::Stop => Self::Chain { bank: 0, pattern: 0 },
            Self::Chain { .. } => Self::Loop,
        }
    }

    /// Cycle backward through follow-up modes.
    pub fn prev_mode(&self) -> Self {
        match self {
            Self::Loop => Self::Chain { bank: 0, pattern: 0 },
            Self::Stop => Self::Loop,
            Self::Chain { .. } => Self::Stop,
        }
    }
}

fn is_default_follow_up(f: &FollowUp) -> bool {
    *f == FollowUp::default()
}

/// Single step in a pattern, holding a Forth script and optional metadata.
#[derive(Clone, Serialize, Deserialize)]
pub struct Step {
    pub active: bool,
    pub script: String,
    #[serde(default)]
    pub source: Option<u8>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
}

impl Step {
    /// True if all fields are at their default values.
    pub fn is_default(&self) -> bool {
        self.active && self.script.is_empty() && self.source.is_none() && self.name.is_none()
    }

    /// True if the script is non-empty.
    pub fn has_content(&self) -> bool {
        !self.script.is_empty()
    }
}

impl Default for Step {
    fn default() -> Self {
        Self {
            active: true,
            script: String::new(),
            source: None,
            name: None,
        }
    }
}

/// Sequence of steps with playback settings (speed, quantization, sync, follow-up).
#[derive(Clone)]
pub struct Pattern {
    pub steps: Vec<Step>,
    pub length: usize,
    pub speed: PatternSpeed,
    pub name: Option<String>,
    pub description: Option<String>,
    pub quantization: LaunchQuantization,
    pub sync_mode: SyncMode,
    pub follow_up: FollowUp,
}

#[derive(Serialize, Deserialize)]
struct SparseStep {
    i: usize,
    #[serde(default = "default_active", skip_serializing_if = "is_true")]
    active: bool,
    #[serde(default, skip_serializing_if = "String::is_empty")]
    script: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    source: Option<u8>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    name: Option<String>,
}

fn default_active() -> bool {
    true
}

fn is_true(v: &bool) -> bool {
    *v
}

#[derive(Serialize, Deserialize)]
struct SparsePattern {
    steps: Vec<SparseStep>,
    length: usize,
    #[serde(default)]
    speed: PatternSpeed,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    name: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    description: Option<String>,
    #[serde(default, skip_serializing_if = "is_default_quantization")]
    quantization: LaunchQuantization,
    #[serde(default, skip_serializing_if = "is_default_sync_mode")]
    sync_mode: SyncMode,
    #[serde(default, skip_serializing_if = "is_default_follow_up")]
    follow_up: FollowUp,
}

fn is_default_quantization(q: &LaunchQuantization) -> bool {
    *q == LaunchQuantization::default()
}

fn is_default_sync_mode(s: &SyncMode) -> bool {
    *s == SyncMode::default()
}

#[derive(Deserialize)]
struct LegacyPattern {
    steps: Vec<Step>,
    length: usize,
    #[serde(default)]
    speed: PatternSpeed,
    #[serde(default)]
    name: Option<String>,
    #[serde(default)]
    description: Option<String>,
    #[serde(default)]
    quantization: LaunchQuantization,
    #[serde(default)]
    sync_mode: SyncMode,
    #[serde(default)]
    follow_up: FollowUp,
}

impl Serialize for Pattern {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let sparse_steps: Vec<SparseStep> = self
            .steps
            .iter()
            .enumerate()
            .filter(|(_, step)| !step.is_default())
            .map(|(i, step)| SparseStep {
                i,
                active: step.active,
                script: step.script.clone(),
                source: step.source,
                name: step.name.clone(),
            })
            .collect();

        let sparse = SparsePattern {
            steps: sparse_steps,
            length: self.length,
            speed: self.speed,
            name: self.name.clone(),
            description: self.description.clone(),
            quantization: self.quantization,
            sync_mode: self.sync_mode,
            follow_up: self.follow_up,
        };
        sparse.serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for Pattern {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        #[derive(Deserialize)]
        #[serde(untagged)]
        enum PatternFormat {
            Sparse(SparsePattern),
            Legacy(LegacyPattern),
        }

        match PatternFormat::deserialize(deserializer)? {
            PatternFormat::Sparse(sparse) => {
                let mut steps: Vec<Step> = (0..MAX_STEPS).map(|_| Step::default()).collect();
                for ss in sparse.steps {
                    if ss.i < MAX_STEPS {
                        steps[ss.i] = Step {
                            active: ss.active,
                            script: ss.script,
                            source: ss.source,
                            name: ss.name,
                        };
                    }
                }
                Ok(Pattern {
                    steps,
                    length: sparse.length,
                    speed: sparse.speed,
                    name: sparse.name,
                    description: sparse.description,
                    quantization: sparse.quantization,
                    sync_mode: sparse.sync_mode,
                    follow_up: sparse.follow_up,
                })
            }
            PatternFormat::Legacy(legacy) => Ok(Pattern {
                steps: legacy.steps,
                length: legacy.length,
                speed: legacy.speed,
                name: legacy.name,
                description: legacy.description,
                quantization: legacy.quantization,
                sync_mode: legacy.sync_mode,
                follow_up: legacy.follow_up,
            }),
        }
    }
}

impl Default for Pattern {
    fn default() -> Self {
        Self {
            steps: (0..MAX_STEPS).map(|_| Step::default()).collect(),
            length: DEFAULT_LENGTH,
            speed: PatternSpeed::default(),
            name: None,
            description: None,
            quantization: LaunchQuantization::default(),
            sync_mode: SyncMode::default(),
            follow_up: FollowUp::default(),
        }
    }
}

impl Pattern {
    /// Borrow a step by index.
    pub fn step(&self, index: usize) -> Option<&Step> {
        self.steps.get(index)
    }

    /// Mutably borrow a step by index.
    pub fn step_mut(&mut self, index: usize) -> Option<&mut Step> {
        self.steps.get_mut(index)
    }

    /// Set the active length, clamped to `[1, MAX_STEPS]`.
    pub fn set_length(&mut self, length: usize) {
        let length = length.clamp(1, MAX_STEPS);
        while self.steps.len() < length {
            self.steps.push(Step::default());
        }
        self.length = length;
    }

    /// Follow the source chain from `index` to find the originating step.
    pub fn resolve_source(&self, index: usize) -> usize {
        let mut current = index;
        for _ in 0..self.steps.len() {
            if let Some(step) = self.steps.get(current) {
                if let Some(source) = step.source {
                    current = source as usize;
                } else {
                    return current;
                }
            } else {
                return index;
            }
        }
        index
    }

    /// Return the script at the resolved source of `index`.
    pub fn resolve_script(&self, index: usize) -> Option<&str> {
        let source_idx = self.resolve_source(index);
        self.steps.get(source_idx).map(|s| s.script.as_str())
    }

    /// Count active-length steps that have a script or a source reference.
    pub fn content_step_count(&self) -> usize {
        self.steps[..self.length]
            .iter()
            .filter(|s| s.has_content() || s.source.is_some())
            .count()
    }
}

/// Collection of patterns forming a bank.
#[derive(Clone, Serialize, Deserialize)]
pub struct Bank {
    pub patterns: Vec<Pattern>,
    #[serde(default)]
    pub name: Option<String>,
}

impl Bank {
    /// Count patterns that contain at least one non-empty step.
    pub fn content_pattern_count(&self) -> usize {
        self.patterns
            .iter()
            .filter(|p| p.content_step_count() > 0)
            .count()
    }
}

impl Default for Bank {
    fn default() -> Self {
        Self {
            patterns: (0..MAX_PATTERNS).map(|_| Pattern::default()).collect(),
            name: None,
        }
    }
}

/// Top-level project: banks, tempo, sample paths, and prelude script.
#[derive(Clone, Serialize, Deserialize)]
pub struct Project {
    pub banks: Vec<Bank>,
    #[serde(default)]
    pub sample_paths: Vec<PathBuf>,
    #[serde(default = "default_tempo")]
    pub tempo: f64,
    #[serde(default)]
    pub playing_patterns: Vec<(usize, usize)>,
    #[serde(default)]
    pub prelude: String,
    #[serde(default)]
    pub script: String,
    #[serde(default)]
    pub script_speed: PatternSpeed,
    #[serde(default = "default_script_length")]
    pub script_length: usize,
}

fn default_tempo() -> f64 {
    120.0
}

fn default_script_length() -> usize {
    16
}

impl Default for Project {
    fn default() -> Self {
        Self {
            banks: (0..MAX_BANKS).map(|_| Bank::default()).collect(),
            sample_paths: Vec::new(),
            tempo: default_tempo(),
            playing_patterns: Vec::new(),
            prelude: String::new(),
            script: String::new(),
            script_speed: PatternSpeed::default(),
            script_length: default_script_length(),
        }
    }
}

impl Project {
    /// Borrow a pattern by bank and pattern index.
    pub fn pattern_at(&self, bank: usize, pattern: usize) -> &Pattern {
        &self.banks[bank].patterns[pattern]
    }

    /// Mutably borrow a pattern by bank and pattern index.
    pub fn pattern_at_mut(&mut self, bank: usize, pattern: usize) -> &mut Pattern {
        &mut self.banks[bank].patterns[pattern]
    }

    /// Pad banks, patterns, and steps to their maximum sizes after deserialization.
    pub fn normalize(&mut self) {
        self.banks.resize_with(MAX_BANKS, Bank::default);
        for bank in &mut self.banks {
            bank.patterns.resize_with(MAX_PATTERNS, Pattern::default);
            for pattern in &mut bank.patterns {
                pattern.steps.resize_with(MAX_STEPS, Step::default);
            }
        }
    }
}
