pub enum CatEntry {
    Section(&'static str),
    Category(&'static str),
}

use CatEntry::{Category, Section};

pub const CATEGORIES: &[CatEntry] = &[
    // Forth core
    Section("Forth"),
    Category("Stack"),
    Category("Arithmetic"),
    Category("Comparison"),
    Category("Logic"),
    Category("Control"),
    Category("Variables"),
    Category("Probability"),
    Category("Definitions"),
    // Live coding
    Section("Live Coding"),
    Category("Sound"),
    Category("Time"),
    Category("Context"),
    Category("Music"),
    Category("LFO"),
    // Synthesis
    Section("Synthesis"),
    Category("Oscillator"),
    Category("Wavetable"),
    Category("Generator"),
    Category("Envelope"),
    Category("Sample"),
    // Effects
    Section("Effects"),
    Category("Filter"),
    Category("FM"),
    Category("Modulation"),
    Category("Mod FX"),
    Category("Lo-fi"),
    Category("Stereo"),
    Category("Delay"),
    Category("Reverb"),
    // External I/O
    Section("I/O"),
    Category("MIDI"),
    Category("Desktop"),
];

pub fn category_count() -> usize {
    CATEGORIES
        .iter()
        .filter(|e| matches!(e, Category(_)))
        .count()
}

pub fn get_category_name(index: usize) -> &'static str {
    CATEGORIES
        .iter()
        .filter_map(|e| match e {
            Category(name) => Some(*name),
            Section(_) => None,
        })
        .nth(index)
        .unwrap_or("Unknown")
}

pub fn section_count() -> usize {
    CATEGORIES
        .iter()
        .filter(|e| matches!(e, Section(_)))
        .count()
}

pub fn section_index_for_category(cat_idx: usize) -> usize {
    let mut section: usize = 0;
    let mut cat: usize = 0;
    for entry in CATEGORIES.iter() {
        match entry {
            Section(_) => section += 1,
            Category(_) => {
                if cat == cat_idx {
                    return section.saturating_sub(1);
                }
                cat += 1;
            }
        }
    }
    section.saturating_sub(1)
}

pub fn first_category_in_section(section_idx: usize) -> Option<usize> {
    let mut section: usize = 0;
    let mut cat: usize = 0;
    let mut in_target = false;
    for entry in CATEGORIES.iter() {
        match entry {
            Section(_) => {
                if in_target {
                    return None;
                }
                if section == section_idx {
                    in_target = true;
                }
                section += 1;
            }
            Category(_) => {
                if in_target {
                    return Some(cat);
                }
                cat += 1;
            }
        }
    }
    None
}
