use std::cell::{Cell, RefCell};
use std::ops::RangeInclusive;

use cagire_ratatui::Editor;

#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum EditorTarget {
    #[default]
    Step,
    Prelude,
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum PatternField {
    Length,
    Speed,
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ScriptField {
    Speed,
    Length,
}

#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum PatternPropsField {
    #[default]
    Name,
    Description,
    Length,
    Speed,
    Quantization,
    SyncMode,
    FollowUp,
    ChainBank,
    ChainPattern,
}

impl PatternPropsField {
    pub fn next(&self, follow_up_is_chain: bool) -> Self {
        match self {
            Self::Name => Self::Description,
            Self::Description => Self::Length,
            Self::Length => Self::Speed,
            Self::Speed => Self::Quantization,
            Self::Quantization => Self::SyncMode,
            Self::SyncMode => Self::FollowUp,
            Self::FollowUp if follow_up_is_chain => Self::ChainBank,
            Self::FollowUp => Self::FollowUp,
            Self::ChainBank => Self::ChainPattern,
            Self::ChainPattern => Self::ChainPattern,
        }
    }

    pub fn prev(&self, follow_up_is_chain: bool) -> Self {
        match self {
            Self::Name => Self::Name,
            Self::Description => Self::Name,
            Self::Length => Self::Description,
            Self::Speed => Self::Length,
            Self::Quantization => Self::Speed,
            Self::SyncMode => Self::Quantization,
            Self::FollowUp => Self::SyncMode,
            Self::ChainBank => Self::FollowUp,
            Self::ChainPattern if follow_up_is_chain => Self::ChainBank,
            Self::ChainPattern => Self::FollowUp,
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum EuclideanField {
    #[default]
    Pulses,
    Steps,
    Rotation,
}

impl EuclideanField {
    pub fn next(&self) -> Self {
        match self {
            Self::Pulses => Self::Steps,
            Self::Steps => Self::Rotation,
            Self::Rotation => Self::Rotation,
        }
    }

    pub fn prev(&self) -> Self {
        match self {
            Self::Pulses => Self::Pulses,
            Self::Steps => Self::Pulses,
            Self::Rotation => Self::Steps,
        }
    }
}

pub struct EditorContext {
    pub bank: usize,
    pub pattern: usize,
    pub step: usize,
    pub editor: Editor,
    pub selection_anchor: Option<usize>,
    pub copied_steps: Option<CopiedSteps>,
    pub show_stack: bool,
    pub stack_cache: RefCell<Option<StackCache>>,
    pub target: EditorTarget,
    pub steps_per_page: Cell<usize>,
    pub mouse_selecting: bool,
}

#[derive(Clone)]
pub struct StackCache {
    pub cursor_line: usize,
    pub lines_hash: u64,
    pub result: String,
}

#[derive(Clone)]
pub struct CopiedSteps {
    pub bank: usize,
    pub pattern: usize,
    pub steps: Vec<CopiedStepData>,
}

#[derive(Clone)]
pub struct CopiedStepData {
    pub script: String,
    pub active: bool,
    pub source: Option<u8>,
    pub original_index: usize,
    pub name: Option<String>,
}

impl EditorContext {
    pub fn selection_range(&self) -> Option<RangeInclusive<usize>> {
        let anchor = self.selection_anchor?;
        let a = anchor.min(self.step);
        let b = anchor.max(self.step);
        Some(a..=b)
    }

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

impl Default for EditorContext {
    fn default() -> Self {
        Self {
            bank: 0,
            pattern: 0,
            step: 0,
            editor: Editor::new(),
            selection_anchor: None,
            copied_steps: None,
            show_stack: false,
            stack_cache: RefCell::new(None),
            target: EditorTarget::default(),
            steps_per_page: Cell::new(32),
            mouse_selecting: false,
        }
    }
}

pub struct ScriptEditorState {
    pub editor: Editor,
    pub show_stack: bool,
    pub stack_cache: RefCell<Option<StackCache>>,
    pub dirty: bool,
    pub focused: bool,
    pub mouse_selecting: bool,
}

impl Default for ScriptEditorState {
    fn default() -> Self {
        Self {
            editor: Editor::new(),
            show_stack: false,
            stack_cache: RefCell::new(None),
            dirty: false,
            focused: true,
            mouse_selecting: false,
        }
    }
}
