use crate::message::*;
use crate::keyboard::KeyboardProvider;

/// Provider returning a sequence of predefined keys.
/// Used for non-interactive inputs.
pub struct ScriptKeyboard {
    script: Vec<String>,
}

impl ScriptKeyboard {
    pub fn new(script: Vec<String>) -> ScriptKeyboard {
        ScriptKeyboard {
            script
        }
    }

    fn map(c: char) -> Option<Key> {
        match c {
            '1' => Some(Key::One),
            '2' => Some(Key::Two),
            '3' => Some(Key::Three),
            '4' => Some(Key::Four),
            '5' => Some(Key::Five),
            '6' => Some(Key::Six),
            '7' => Some(Key::Seven),
            '8' => Some(Key::Eight),
            '9' => Some(Key::Nine),
            '0' => Some(Key::Zero),
            'n' => Some(Key::Negative),
            '.' => Some(Key::Comma),
            
            '+' => Some(Key::Add),
            '-' => Some(Key::Subtract),
            'x' => Some(Key::Multiply),
            ':' => Some(Key::Divide),

            'V' => Some(Key::V),
            'W' => Some(Key::W),
            'Y' => Some(Key::Y),
            'Z' => Some(Key::Z),

            'A' => Some(Key::A),
            'B' => Some(Key::B),
            'C' => Some(Key::C),
            'D' => Some(Key::D),
            'E' => Some(Key::E),
            'F' => Some(Key::F),
            'R' => Some(Key::R),
            
            '⋄' => Some(Key::PrintProgram),
            '↓' => Some(Key::CopyToA),
            '↑' => Some(Key::CopyM),
            '*' => Some(Key::Clear),
            '/' => Some(Key::Split),
            'S' => Some(Key::Start),
            '↕' => Some(Key::SwapA),

            '!' => Some(Key::Power),
            'r' => Some(Key::GeneralReset),

            _ => unimplemented!()
        }
    }
}

impl KeyboardProvider for ScriptKeyboard {
    fn get_name(&self) -> &str {
        "ScriptKeyboard"
    }

    fn poll(&mut self) -> Option<Message> {
        if self.script.is_empty() {
            None
        } else if self.script[0].is_empty() {
            let _ = self.script.remove(0);
            None
        } else {
            let c = self.script[0].remove(0);

            if self.script[0].is_empty() {
                let _ = self.script.remove(0);
            }

            Self::map(c)
                .map(|key| Message::KeyPressed(key))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    pub fn polling_an_empty_buffer_returns_none() {

        // Arrange
        
        let mut provider = ScriptKeyboard::new(Vec::new());

        // Act
        
        let key = provider.poll();

        // Assert

        assert_eq!(None, key);
    }

    #[test]
    pub fn when_a_line_contains_some_sharacters_then_map_them_to_keys() {

        // Arrange
        
        let keys = vec!("12.34S".to_string());
        let mut provider = ScriptKeyboard::new(keys);

        // Act & assert
        
        assert_eq!(Some(Message::KeyPressed(Key::One)), provider.poll());
        assert_eq!(Some(Message::KeyPressed(Key::Two)), provider.poll());
        assert_eq!(Some(Message::KeyPressed(Key::Comma)), provider.poll());
        assert_eq!(Some(Message::KeyPressed(Key::Three)), provider.poll());
        assert_eq!(Some(Message::KeyPressed(Key::Four)), provider.poll());
        assert_eq!(Some(Message::KeyPressed(Key::Start)), provider.poll());
        assert_eq!(None, provider.poll());
    }

    #[test]
    pub fn when_a_line_ends_then_pass_to_next_one() {

        // Arrange
        
        let keys = vec!("1".to_string(),
            "2".to_string(),
            ".".to_string(),
            "3".to_string(),
            "4".to_string(),
            "S".to_string());
        let mut provider = ScriptKeyboard::new(keys);

        // Act & assert
        
        assert_eq!(Some(Message::KeyPressed(Key::One)), provider.poll());
        assert_eq!(Some(Message::KeyPressed(Key::Two)), provider.poll());
        assert_eq!(Some(Message::KeyPressed(Key::Comma)), provider.poll());
        assert_eq!(Some(Message::KeyPressed(Key::Three)), provider.poll());
        assert_eq!(Some(Message::KeyPressed(Key::Four)), provider.poll());
        assert_eq!(Some(Message::KeyPressed(Key::Start)), provider.poll());
        assert_eq!(None, provider.poll());
    }

    #[test]
    pub fn when_power_and_reset_are_pressed_then_emit_those_events() {
        
        // Arrange
        
        let keys = vec!("!".to_string(),  // Power on
            "r".to_string());             // General reset
        let mut provider = ScriptKeyboard::new(keys);

        // Act & assert
        
        assert_eq!(Some(Message::KeyPressed(Key::Power)), provider.poll());
        assert_eq!(Some(Message::KeyPressed(Key::GeneralReset)), provider.poll());
        assert_eq!(None, provider.poll());
    }
}
