mod control;
mod csi;
mod osc;

pub use control::*;
pub use csi::CsiState;
pub use osc::OscState;

#[derive(Default)]
pub enum EscapeParser {
    #[default]
    Normal,
    Escape(EscapeState),
}

#[derive(Debug, PartialEq)]
pub enum TerminalInput {
    Character(char),
    Control(ControlSequence),
    Bell,
    Backspace,
    Tab,
    NewLine,
    VerticalTab,
    NewPage,
    CarriageReturn,
    Delete,
}

impl EscapeParser {
    pub const fn new() -> Self {
        Self::Normal
    }

    pub fn push(&mut self, ch: char) -> Option<TerminalInput> {
        match self {
            Self::Normal => self.push_normal(ch),
            Self::Escape(state) => {
                let (ret, to_normal) = state.push(ch);
                if to_normal {
                    *self = EscapeParser::Normal;
                }
                ret.map(TerminalInput::Control)
            }
        }
    }

    fn push_normal(&mut self, ch: char) -> Option<TerminalInput> {
        match ch {
            // Ignored: NUL, SOH, STX, ETX, EOT, ENQ, ACK
            '\x00'..='\x06' => None,
            // BEL
            '\x07' => Some(TerminalInput::Bell),
            // BS
            '\x08' => Some(TerminalInput::Backspace),
            // TAB
            '\x09' => Some(TerminalInput::Tab),
            // LF
            '\x0A' => Some(TerminalInput::NewLine),
            // VT
            '\x0B' => Some(TerminalInput::VerticalTab),
            // FF
            '\x0C' => Some(TerminalInput::NewPage),
            // CR
            '\x0D' => Some(TerminalInput::CarriageReturn),
            // Ignored: SO, ..., SUB
            '\x0E'..='\x1A' => None,
            // ESC
            '\x1B' => {
                *self = Self::Escape(EscapeState::Esc);
                None
            }
            // Ignored: FS, ..., US
            '\x1C'..='\x1F' => None,
            // Printable
            '\x20'..='\x7E' => Some(TerminalInput::Character(ch)),
            // DEL
            '\x7F' => Some(TerminalInput::Delete),
            // Other characters >0x7F
            _ => Some(TerminalInput::Character(ch)),
        }
    }
}

pub enum EscapeState {
    Esc,
    G0Charset(char),
    Csi(CsiState),
    Osc(OscState),
}

impl EscapeState {
    fn push(&mut self, ch: char) -> (Option<ControlSequence>, bool) {
        match self {
            Self::Esc => match ch {
                // ESC ETX: Switch to VT100 mode
                '\x03' => todo!(),
                // ESC ENQ: Return terminal status
                '\x05' => todo!(),
                // ESC FF: PAGE (Clear screen)
                '\x0C' => todo!(),
                // ESC SO: Begin 4015 APL mode
                // ESC SI: End 4015 APL mode
                '\x0E' | '\x0F' => todo!(),
                // ESC ETB: COPY (Save Tektronix Codes to file)
                '\x17' => todo!(),
                // ESC CAN: Bypass condition
                '\x18' => todo!(),
                // ESC SUB: GIN mode
                '\x1A' => todo!(),
                // ESC FS: Special Point Plot Mode
                '\x1C' => todo!(),
                // ESC ( C => Designate G0 Character Set
                '(' => {
                    *self = Self::G0Charset('\0');
                    (None, false)
                }
                // ESC ] => OSC
                ']' => {
                    *self = Self::Osc(OscState::new());
                    (None, false)
                }
                // ESC [ => CSI
                '[' => {
                    *self = Self::Csi(CsiState::new());
                    (None, false)
                }
                // ESC D => Index (IND)
                'D' => todo!(),
                // ESC E => Next Line (NEL)
                'E' => todo!(),
                // ESC H => Tab Set (HTS)
                'H' => todo!(),
                // ESC M => Reverse Index (RI)
                'M' => todo!(),
                // ESC N => Single Shift Select of G2 Character Set (SS2)
                // ESC O => Single Shift Select of G3 Character Set (SS3)
                'N' | 'O' => todo!(),
                // ESC P => Device Control String (DCS)
                'P' => (None, true),
                // ESC V => Start of Guarded Area (SPA)
                'V' => todo!(),
                // ESC W => End of Guarded Area (EPA)
                'W' => todo!(),
                // ESC X => Start of String (SOS)
                'X' => todo!(),
                // ESC Z => Return Terminal ID (DECID)
                'Z' => todo!(),
                // ESC \ => String Terminator (ST)
                '\\' => (None, true),
                // ESC ^ => Privacy Message (PM)
                '^' => todo!(),
                // ESC _ => Application Program Command (APC)
                '_' => todo!(),

                ' ' | '#' | '%' | ')' | '*' | '+' | '-' | '.' | '/' => todo!(),

                '6'..='9' => todo!(),

                // ESC c => Full Reset
                'c' => todo!(),
                // ESC = => Enter alternate keypad mode
                '=' => (Some(ControlSequence::SetAlternateKeypad(true)), true),
                // ESC > => Leave alternate keypad mode
                '>' => (Some(ControlSequence::SetAlternateKeypad(false)), true),

                // Unsupported
                'F' | 'l' | 'm' | 'n' | 'o' | '|' | '}' | '~' => todo!("ESC {ch}"),

                _ => todo!(),
            },
            Self::G0Charset(g0) => match ch {
                '"' | '%' if *g0 == '\0' => {
                    *g0 = ch;
                    (None, false)
                }
                // TODO
                _ => (None, true),
            },
            Self::Csi(csi) => csi.push(ch),
            Self::Osc(osc) => osc.push(ch),
        }
    }
}
