use std::ops::RangeInclusive;

use crate::model::{MAX_BANKS, MAX_PATTERNS};

#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum PatternsColumn {
    #[default]
    Banks,
    Patterns,
}

#[derive(Clone, Copy, Default)]
pub struct PatternsNav {
    pub column: PatternsColumn,
    pub bank_cursor: usize,
    pub pattern_cursor: usize,
    pub bank_anchor: Option<usize>,
    pub pattern_anchor: Option<usize>,
}

impl PatternsNav {
    pub fn move_left(&mut self) {
        self.clear_selection();
        self.column = PatternsColumn::Banks;
    }

    pub fn move_right(&mut self) {
        self.clear_selection();
        self.column = PatternsColumn::Patterns;
    }

    pub fn move_up(&mut self) {
        match self.column {
            PatternsColumn::Banks => {
                self.bank_cursor = (self.bank_cursor + MAX_BANKS - 1) % MAX_BANKS;
            }
            PatternsColumn::Patterns => {
                self.pattern_cursor = (self.pattern_cursor + MAX_PATTERNS - 1) % MAX_PATTERNS;
            }
        }
    }

    pub fn move_down(&mut self) {
        match self.column {
            PatternsColumn::Banks => {
                self.bank_cursor = (self.bank_cursor + 1) % MAX_BANKS;
            }
            PatternsColumn::Patterns => {
                self.pattern_cursor = (self.pattern_cursor + 1) % MAX_PATTERNS;
            }
        }
    }

    pub fn move_up_clamped(&mut self) {
        match self.column {
            PatternsColumn::Banks => {
                self.bank_cursor = self.bank_cursor.saturating_sub(1);
            }
            PatternsColumn::Patterns => {
                self.pattern_cursor = self.pattern_cursor.saturating_sub(1);
            }
        }
    }

    pub fn move_down_clamped(&mut self) {
        match self.column {
            PatternsColumn::Banks => {
                self.bank_cursor = (self.bank_cursor + 1).min(MAX_BANKS - 1);
            }
            PatternsColumn::Patterns => {
                self.pattern_cursor = (self.pattern_cursor + 1).min(MAX_PATTERNS - 1);
            }
        }
    }

    pub fn selected_bank(&self) -> usize {
        self.bank_cursor
    }

    pub fn selected_pattern(&self) -> usize {
        self.pattern_cursor
    }

    pub fn bank_selection_range(&self) -> Option<RangeInclusive<usize>> {
        let anchor = self.bank_anchor?;
        let a = anchor.min(self.bank_cursor);
        let b = anchor.max(self.bank_cursor);
        Some(a..=b)
    }

    pub fn pattern_selection_range(&self) -> Option<RangeInclusive<usize>> {
        let anchor = self.pattern_anchor?;
        let a = anchor.min(self.pattern_cursor);
        let b = anchor.max(self.pattern_cursor);
        Some(a..=b)
    }

    pub fn selected_banks(&self) -> Vec<usize> {
        match self.bank_selection_range() {
            Some(range) => range.collect(),
            None => vec![self.bank_cursor],
        }
    }

    pub fn selected_patterns(&self) -> Vec<usize> {
        match self.pattern_selection_range() {
            Some(range) => range.collect(),
            None => vec![self.pattern_cursor],
        }
    }

    pub fn clear_selection(&mut self) {
        self.bank_anchor = None;
        self.pattern_anchor = None;
    }

    pub fn has_selection(&self) -> bool {
        match self.column {
            PatternsColumn::Banks => self.bank_anchor.is_some(),
            PatternsColumn::Patterns => self.pattern_anchor.is_some(),
        }
    }
}
