use indexmap::IndexMap;

use crate::prelude::*;

#[derive(Clone, Debug, serde::Deserialize)]
#[serde(rename_all = "snake_case", default)]
pub struct InputConfig {
    pub scroll_amount: usize,
    pub timeout_millis: u64,
    pub mappings: IndexMap<KeySequence, CommandSequence>,
}

// a macro for pleasure
macro_rules! cmd_mappings {
    [$($key_seq:literal => $($command_seq:literal)*),*,] => {
        vec![$(($key_seq.into(), [$(Command::parse($command_seq, false).unwrap()),*].into()),)*].into_iter().collect()
    };
}

fn generate_default_input_commands() -> IndexMap<KeySequence, CommandSequence> {
    cmd_mappings! [
        "up"        => "up",
        "down"      => "down",
        "C-j"       => "in feeds down",
        "C-k"       => "in feeds up",
        "J"         => "in content down",
        "K"         => "in content up",
        "M-j"       => "in articles down",
        "M-k"       => "in articles up",
        "C-h"       => "left",
        "C-l"       => "right",
        "left"      => "left",
        "right"     => "right",
        "j"         => "down",
        "k"         => "up",
        "enter"     => "_submit",
        "esc"       => "_abort",
        "C-g"       => "_abort",
        "C-u"       => "_clear",
        "space"     => "toggle",
        "C-f"       => "pagedown",
        "C-b"       => "pageup",
        "g g"       => "gotofirst",
        "G"         => "gotolast",
        "q"         => "confirm quit",
        "C-c"       => "quit",
        "x"         => "scrape",
        "g f"       => "focus feeds",
        "g a"       => "focus articles",
        "g c"       => "focus content",
        ":"         => "cmd",
        "l"         => "next",
        "h"         => "prev",
        "tab"       => "nextc",
        "backtab"   => "prevc",
        "y"         => "share clipboard",
        "o"         => "open" "in articles read" "in articles nextunread",
        "O"         => "open unread" "confirm in articles read %",
        "s"         => "sync",
        "r"         => "read" "nextunread",
        "t"         => "cmd tag",
        "R"         => "confirm in articles read %",
        "0 r"       => "confirm in articles read above",
        "$ r"       => "confirm in articles read below",
        "M-r"       => "cmd read",
        "u"         => "unread",
        "U"         => "confirm in articles unread %",
        "0 u"       => "confirm in articles unread above",
        "$ u"       => "confirm in articles unread below",
        "M-u"       => "cmd unread",
        "m"         => "mark",
        "M"         => "confirm in articles mark %",
        "0 m"       => "confirm in articles mark above",
        "$ m"       => "confirm in articles mark below",
        "M-m"       => "cmd mark",
        "v"         => "unmark",
        "V"         => "confirm in articles unmark %",
        "0 v"       => "confirm in articles unmark above",
        "$ v"       => "confirm in articles unmark below",
        "M-v"       => "cmd unmark",
        "; ;"       => "cmd hintfollow",
        "; y"       => "cmd hintshare clipboard",
        "; s"       => "cmd hintshare",

        // flagging
        "f"         => "flag" "in articles down",
        "0 f"       => "flag above",
        "$ f"       => "flag below",
        "F"         => "flag all",
        "M-f"       => "cmd flag",
        "d"         => "unflag" "in articles down",
        "0 d"       => "unflag above",
        "$ d"       => "unflag below",
        "D"         => "unflag all",
        "M-d"       => "cmd unflag",
        "i"         => "flaginvert" "in articles down",
        "0 i"       => "flaginvert above",
        "$ i"       => "flaginvert below",
        "I"         => "flaginvert all",
        "M-i"       => "cmd flaginvert",

        "1"         => "show all",
        "2"         => "show unread",
        "3"         => "show marked",
        "z"         => "zen",
        "/"         => "_search",
        "n"         => "searchnext",
        "N"         => "searchprev",
        "="         => "cmd filter ",
        "+ r"       => "filterclear",
        "+ +"       => "filterapply",
        "\\"        => "cmd sort",
        "| |"       => "sortreverse",
        "| r"       => "sortclear",
        "c w"       => "cmd rename",
        "c d"       => "confirm remove",
        "c x"       => "confirm removeall",
        "c f"       => "cmd feedadd",
        "c e"       => "confirm feedadd https://github.com/christo-auer/eilmeldung/releases.atom eilmeldung releases",
        "c a"       => "cmd categoryadd",
        "c u"       => "cmd feedchangeurl",
        "c y"       => "yank",
        "c p"       => "paste after",
        "c P"       => "paste before",
        "c c"       => "cmd tagchangecolor",
        "c s"       => "confirm sortfeeds",
        "S"         => "cmd share",
        "e"         => "openenclosure",
        "E"         => "cmd openenclosure",
        "?"         => "helpinput",

    ]
}

impl Default for InputConfig {
    fn default() -> Self {
        Self {
            scroll_amount: 10,
            timeout_millis: 5000,
            mappings: generate_default_input_commands(),
        }
    }
}

impl InputConfig {
    pub fn match_single_key(&self, key: &Key) -> Option<&CommandSequence> {
        self.mappings.get(&KeySequence { keys: vec![*key] })
    }

    pub fn match_single_key_to_single_command(&self, key: &Key) -> Option<&Command> {
        self.match_single_key(key).and_then(|command_sequence| {
            let first = command_sequence.commands.first();
            first.filter(|_| command_sequence.commands.len() == 1)
        })
    }
}
