use crate::model::{Bank, Pattern, PatternSpeed, Project};

#[derive(Debug, Clone, Copy)]
pub struct PatternEdit {
    pub bank: usize,
    pub pattern: usize,
}

impl PatternEdit {
    pub fn new(bank: usize, pattern: usize) -> Self {
        Self { bank, pattern }
    }
}

pub fn toggle_step(project: &mut Project, bank: usize, pattern: usize, step: usize) -> PatternEdit {
    if let Some(s) = project.pattern_at_mut(bank, pattern).step_mut(step) {
        s.active = !s.active;
    }
    PatternEdit::new(bank, pattern)
}

pub fn set_length(
    project: &mut Project,
    bank: usize,
    pattern: usize,
    length: usize,
) -> (PatternEdit, usize) {
    project.pattern_at_mut(bank, pattern).set_length(length);
    let actual = project.pattern_at(bank, pattern).length;
    (PatternEdit::new(bank, pattern), actual)
}

pub fn get_length(project: &Project, bank: usize, pattern: usize) -> usize {
    project.pattern_at(bank, pattern).length
}

pub fn increase_length(project: &mut Project, bank: usize, pattern: usize) -> (PatternEdit, usize) {
    let current = get_length(project, bank, pattern);
    set_length(project, bank, pattern, current + 1)
}

pub fn decrease_length(project: &mut Project, bank: usize, pattern: usize) -> (PatternEdit, usize) {
    let current = get_length(project, bank, pattern);
    set_length(project, bank, pattern, current.saturating_sub(1))
}

pub fn set_speed(
    project: &mut Project,
    bank: usize,
    pattern: usize,
    speed: PatternSpeed,
) -> PatternEdit {
    project.pattern_at_mut(bank, pattern).speed = speed;
    PatternEdit::new(bank, pattern)
}

pub fn increase_speed(project: &mut Project, bank: usize, pattern: usize) -> PatternEdit {
    let pat = project.pattern_at_mut(bank, pattern);
    pat.speed = pat.speed.next();
    PatternEdit::new(bank, pattern)
}

pub fn decrease_speed(project: &mut Project, bank: usize, pattern: usize) -> PatternEdit {
    let pat = project.pattern_at_mut(bank, pattern);
    pat.speed = pat.speed.prev();
    PatternEdit::new(bank, pattern)
}

pub fn set_step_script(
    project: &mut Project,
    bank: usize,
    pattern: usize,
    step: usize,
    script: String,
) -> PatternEdit {
    if let Some(s) = project.pattern_at_mut(bank, pattern).step_mut(step) {
        s.script = script;
    }
    PatternEdit::new(bank, pattern)
}

pub fn get_step_script(
    project: &Project,
    bank: usize,
    pattern: usize,
    step: usize,
) -> Option<String> {
    project
        .pattern_at(bank, pattern)
        .step(step)
        .map(|s| s.script.clone())
}

pub fn delete_step(project: &mut Project, bank: usize, pattern: usize, step: usize) -> PatternEdit {
    let pat = project.pattern_at_mut(bank, pattern);
    for s in &mut pat.steps {
        if s.source == Some(step as u8) {
            s.source = None;
            s.script.clear();
            s.name = None;
        }
    }

    set_step_script(project, bank, pattern, step, String::new());
    if let Some(s) = project.pattern_at_mut(bank, pattern).step_mut(step) {
        s.source = None;
        s.name = None;
    }
    PatternEdit::new(bank, pattern)
}

pub fn delete_steps(project: &mut Project, bank: usize, pattern: usize, steps: &[usize]) -> PatternEdit {
    for &step in steps {
        delete_step(project, bank, pattern, step);
    }
    PatternEdit::new(bank, pattern)
}

pub fn reset_pattern(project: &mut Project, bank: usize, pattern: usize) -> PatternEdit {
    project.banks[bank].patterns[pattern] = Pattern::default();
    PatternEdit::new(bank, pattern)
}

pub fn reset_bank(project: &mut Project, bank: usize) -> usize {
    project.banks[bank] = Bank::default();
    project.banks[bank].patterns.len()
}
