use std::io::Read;

use crate::cursor::Direction;

pub enum KeyboardInput {
    Eof,
    Key(u8),
    CtrlA,
    CtrlB,
    CtrlC,
    CtrlE,
    CtrlD,
    CtrlL,
    CtrlR,
    Arrow(Direction),
    CtrlArrow(Direction),
    DeleteLeft,
    DeleteRight,
    CtrlDeleteRight,
    Home,
    End,
}

fn read1() -> Option<u8> {
    let mut buf = [0];
    match std::io::stdin().lock().read_exact(&mut buf) {
        Ok(_) => Some(buf[0]),
        Err(_) => None,
    }
}

fn byte_to_dir(b: u8) -> Option<Direction> {
    use Direction::*;
    match b {
        b'A' => Some(Up),
        b'B' => Some(Down),
        b'C' => Some(Right),
        b'D' => Some(Left),
        _ => None,
    }
}

fn read_escape(debug: bool) -> KeyboardInput {
    use Direction::*;
    use KeyboardInput::*;

    let mut seq = vec![match read1() {
        Some(x) => x,
        None => return Eof,
    }];

    if seq[0] == b'[' {
        // still more
        while {
            let last = seq[seq.len() - 1];
            !(0x40..=0x7E).contains(&last) || seq.len() == 1
        } {
            seq.push(match read1() {
                Some(x) => x,
                None => return Eof,
            });
        }

        if debug {
            println!("escape: {}", seq.escape_ascii());
        }

        match seq[1] {
            b'3' => {
                if seq.len() > 2 && seq[2] == b'~' {
                    DeleteRight
                } else {
                    todo!("unhandled: {}", seq.escape_ascii());
                }
            }
            b'H' => Home,
            b'F' => End,
            b'd' => CtrlDeleteRight,

            // Ctrl Arrow
            b'1' => {
                if seq[1..].starts_with(b"1;5") {
                    if seq.len() == 4 {
                        todo!("idk what this is.");
                    }
                    match seq[4] {
                        b'A' => CtrlArrow(Up),
                        b'B' => CtrlArrow(Down),
                        b'C' => CtrlArrow(Right),
                        b'D' => CtrlArrow(Left),
                        _ => todo!("unhandled {}", seq.escape_ascii()),
                    }
                } else {
                    todo!("unhandled {}", seq[1..].escape_ascii())
                }
            }

            x => {
                if let Some(dir) = byte_to_dir(x) {
                    Arrow(dir)
                } else {
                    todo!("escape characters {}", seq[1..].escape_ascii())
                }
            }
        }
    } else {
        if debug {
            println!("escape: {}", seq.escape_ascii());
        }
        match seq[0] {
            b'd' => CtrlDeleteRight,
            x => todo!("unhandled escape code: ESC {x}"),
        }
    }
}

pub fn read(debug: bool) -> KeyboardInput {
    use KeyboardInput::*;

    let Some(x) = read1() else {
        return KeyboardInput::Eof;
    };

    match x {
        1 => CtrlA,
        2 => CtrlB,
        3 => CtrlC,
        4 => CtrlD,
        8 | 127 => DeleteLeft,
        12 => CtrlL,
        18 => CtrlR,
        27 => read_escape(debug),
        b'\t' | b'\r' => Key(x),
        x if !x.is_ascii_control() => Key(x),
        x => todo!("unimplemented control code: {x}"),
    }
}
