use libterm::TermKey;

use crate::{
    buffer::Mode,
    command::Action,
    keymap::{bind1, bindn, KeyMap, KeySeq, PrefixNode},
};

pub struct Config {
    // TODO must be a power of 2, lol
    pub tab_width: usize,
    pub number: bool,

    pub nmap: KeyMap,
    pub imap: KeyMap,
}

impl Default for Config {
    fn default() -> Self {
        use Action::*;

        let nmap = KeyMap::from_iter([
            bind1('i', [InsertBefore]),
            bind1('a', [InsertAfter]),
            bind1('h', [MoveCharPrev]),
            bind1('l', [MoveCharNext]),
            bind1('j', [MoveLineForward(1)]),
            bind1('J', [MoveLineForward(25)]),
            bind1('k', [MoveLineBack(1)]),
            bind1('K', [MoveLineBack(25)]),
            bind1(TermKey::Left, [MoveCharPrev]),
            bind1(TermKey::Right, [MoveCharNext]),
            bind1(TermKey::Up, [MoveLineBack(1)]),
            bind1(TermKey::Down, [MoveLineForward(1)]),
            bind1(TermKey::Home, [MoveLineStart]),
            bind1(TermKey::End, [MoveLineEnd]),
            bindn(['g', 'g'], [MoveFirstLine]),
            bind1('G', [MoveLastLine]),
            bind1('I', [MoveLineStart, InsertBefore]),
            bind1('A', [MoveLineEnd, InsertAfter]),
            bind1('o', [NewlineAfter, MoveLineForward(1), InsertBefore]),
            bind1('O', [NewlineBefore, MoveLineBack(1), InsertBefore]),
            bindn(['d', 'd'], [KillLine]),
        ]);

        let imap = KeyMap::from_iter([
            bind1('\x7F', [EraseBackward]),
            bind1(TermKey::Left, [MoveCharPrev]),
            bind1(TermKey::Right, [MoveCharNext]),
            bind1(TermKey::Up, [MoveLineBack(1)]),
            bind1(TermKey::Down, [MoveLineForward(1)]),
            bind1(TermKey::Home, [MoveLineStart]),
            bind1(TermKey::End, [MoveLineEnd]),
            bind1(
                '\n',
                [BreakLine, MoveLineForward(1), MoveLineStart, InsertBefore],
            ),
            bind1(
                '\x0D',
                [BreakLine, MoveLineForward(1), MoveLineStart, InsertBefore],
            ),
        ]);

        Self {
            tab_width: 4,
            number: true,
            nmap,
            imap,
        }
    }
}

impl Config {
    pub fn key_seq(&self, mode: Mode, seq: &KeySeq) -> Option<&PrefixNode<KeySeq, Vec<Action>>> {
        match mode {
            Mode::Normal => self.nmap.get(seq),
            Mode::Insert => self.imap.get(seq),
        }
    }
}
