use super::CyclicEnum;

#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum OptionsFocus {
    #[default]
    ColorScheme,
    HueRotation,
    RefreshRate,
    RuntimeHighlight,
    ShowScope,
    ShowSpectrum,
    ShowLissajous,
    GainBoost,
    NormalizeViz,
    ShowCompletion,
    ShowPreview,
    PerformanceMode,
    Font,
    ZoomFactor,
    WindowSize,
    LinkEnabled,
    StartStopSync,
    Quantum,
    MidiOutput0,
    MidiOutput1,
    MidiOutput2,
    MidiOutput3,
    MidiInput0,
    MidiInput1,
    MidiInput2,
    MidiInput3,
    ResetOnboarding,
    LoadDemoOnStartup,
}

impl CyclicEnum for OptionsFocus {
    const VARIANTS: &'static [Self] = &[
        Self::ColorScheme,
        Self::HueRotation,
        Self::RefreshRate,
        Self::RuntimeHighlight,
        Self::ShowScope,
        Self::ShowSpectrum,
        Self::ShowLissajous,
        Self::GainBoost,
        Self::NormalizeViz,
        Self::ShowCompletion,
        Self::ShowPreview,
        Self::PerformanceMode,
        Self::Font,
        Self::ZoomFactor,
        Self::WindowSize,
        Self::LinkEnabled,
        Self::StartStopSync,
        Self::Quantum,
        Self::MidiOutput0,
        Self::MidiOutput1,
        Self::MidiOutput2,
        Self::MidiOutput3,
        Self::MidiInput0,
        Self::MidiInput1,
        Self::MidiInput2,
        Self::MidiInput3,
        Self::ResetOnboarding,
        Self::LoadDemoOnStartup,
    ];
}

const PLUGIN_ONLY: &[OptionsFocus] = &[
    OptionsFocus::Font,
    OptionsFocus::ZoomFactor,
    OptionsFocus::WindowSize,
];

const STANDALONE_ONLY: &[OptionsFocus] = &[
    OptionsFocus::LinkEnabled,
    OptionsFocus::StartStopSync,
    OptionsFocus::Quantum,
    OptionsFocus::MidiOutput0,
    OptionsFocus::MidiOutput1,
    OptionsFocus::MidiOutput2,
    OptionsFocus::MidiOutput3,
    OptionsFocus::MidiInput0,
    OptionsFocus::MidiInput1,
    OptionsFocus::MidiInput2,
    OptionsFocus::MidiInput3,
    OptionsFocus::ResetOnboarding,
    OptionsFocus::LoadDemoOnStartup,
];

/// Section layout: header line, divider line, then option lines.
/// Each entry gives the raw line offsets assuming ALL sections are visible
/// (plugin mode with Font/Zoom/Window shown).
const FULL_LAYOUT: &[(OptionsFocus, usize)] = &[
    // DISPLAY section: header=0, divider=1
    (OptionsFocus::ColorScheme, 2),
    (OptionsFocus::HueRotation, 3),
    (OptionsFocus::RefreshRate, 4),
    (OptionsFocus::RuntimeHighlight, 5),
    (OptionsFocus::ShowScope, 6),
    (OptionsFocus::ShowSpectrum, 7),
    (OptionsFocus::ShowLissajous, 8),
    (OptionsFocus::GainBoost, 9),
    (OptionsFocus::NormalizeViz, 10),
    (OptionsFocus::ShowCompletion, 11),
    (OptionsFocus::ShowPreview, 12),
    (OptionsFocus::PerformanceMode, 13),
    (OptionsFocus::Font, 14),
    (OptionsFocus::ZoomFactor, 15),
    (OptionsFocus::WindowSize, 16),
    // blank=17, ABLETON LINK header=18, divider=19
    (OptionsFocus::LinkEnabled, 20),
    (OptionsFocus::StartStopSync, 21),
    (OptionsFocus::Quantum, 22),
    // blank=23, SESSION header=24, divider=25, Tempo=26, Beat=27, Phase=28
    // blank=29, MIDI OUTPUTS header=30, divider=31
    (OptionsFocus::MidiOutput0, 32),
    (OptionsFocus::MidiOutput1, 33),
    (OptionsFocus::MidiOutput2, 34),
    (OptionsFocus::MidiOutput3, 35),
    // blank=36, MIDI INPUTS header=37, divider=38
    (OptionsFocus::MidiInput0, 39),
    (OptionsFocus::MidiInput1, 40),
    (OptionsFocus::MidiInput2, 41),
    (OptionsFocus::MidiInput3, 42),
    // blank=43, ONBOARDING header=44, divider=45
    (OptionsFocus::ResetOnboarding, 46),
    (OptionsFocus::LoadDemoOnStartup, 47),
];

impl OptionsFocus {
    fn is_plugin_only(self) -> bool {
        PLUGIN_ONLY.contains(&self)
    }

    fn is_standalone_only(self) -> bool {
        STANDALONE_ONLY.contains(&self)
    }

    fn is_visible(self, plugin_mode: bool) -> bool {
        if self.is_plugin_only() && !plugin_mode {
            return false;
        }
        if self.is_standalone_only() && plugin_mode {
            return false;
        }
        true
    }

    pub fn line_index(self, plugin_mode: bool) -> usize {
        visible_layout(plugin_mode)
            .iter()
            .find(|(f, _)| *f == self)
            .map(|(_, l)| *l)
            .unwrap_or(0)
    }

    pub fn at_line(line: usize, plugin_mode: bool) -> Option<OptionsFocus> {
        visible_layout(plugin_mode)
            .iter()
            .find(|(_, l)| *l == line)
            .map(|(f, _)| *f)
    }
}

/// Total number of rendered lines for the options view.
pub fn total_lines(plugin_mode: bool) -> usize {
    visible_layout(plugin_mode)
        .last()
        .map(|(_, l)| *l + 1)
        .unwrap_or(0)
}

/// Compute (focus, line_index) pairs for only the visible options,
/// with line indices adjusted to account for hidden sections.
fn visible_layout(plugin_mode: bool) -> Vec<(OptionsFocus, usize)> {
    // Start from the full layout and compute adjusted line numbers.
    // Hidden items + their section headers/dividers/blanks shrink the layout.

    // We know the exact section structure, so we compute the offset to subtract
    // based on which sections are hidden.
    let mut offset: usize = 0;

    // Font/Zoom/Window lines (14,15,16) hidden when !plugin_mode
    if !plugin_mode {
        offset += 3; // 3 lines for Font, ZoomFactor, WindowSize
    }

    // Link + Session + MIDI sections hidden when plugin_mode
    // These span from blank(17) through MidiInput3(42) = 26 lines
    if plugin_mode {
        let link_section_lines = 26;
        offset += link_section_lines;
    }

    let mut result = Vec::new();
    for &(focus, raw_line) in FULL_LAYOUT {
        if !focus.is_visible(plugin_mode) {
            continue;
        }
        // Lines at or below index 13 (PerformanceMode) are never shifted
        let adjusted = if raw_line <= 13 {
            raw_line
        } else if !plugin_mode && raw_line <= 16 {
            // Font/Zoom/Window — these are hidden, skip
            continue;
        } else {
            raw_line - offset
        };
        result.push((focus, adjusted));
    }
    result
}

#[derive(Default)]
pub struct OptionsState {
    pub focus: OptionsFocus,
}

impl OptionsState {
    pub fn next_focus(&mut self, plugin_mode: bool) {
        let mut f = self.focus;
        loop {
            f = f.next();
            if f.is_visible(plugin_mode) {
                break;
            }
        }
        self.focus = f;
    }

    pub fn prev_focus(&mut self, plugin_mode: bool) {
        let mut f = self.focus;
        loop {
            f = f.prev();
            if f.is_visible(plugin_mode) {
                break;
            }
        }
        self.focus = f;
    }
}
