use p101_is::Register;
use crate::message::Key;

pub struct SelectedRegister {
    operand: Register
}

impl SelectedRegister {
    pub fn new() -> Self {
        Self {
            operand: Register::M
        }
    }

    pub fn reset(&mut self) {
        self.operand = Register::M;
    }

    pub fn update(&mut self, key: Key) {
        match key {
            Key::Split => match self.operand {
                Register::B => self.operand = Register::b,
                Register::C => self.operand = Register::c,
                Register::D => self.operand = Register::d,
                Register::E => self.operand = Register::e,
                Register::F => self.operand = Register::f,
                _ => ()
            },
            Key::A => self.operand = Register::A,
            Key::B => self.operand = Register::B,
            Key::C => self.operand = Register::C,
            Key::D => self.operand = Register::D,
            Key::E => self.operand = Register::E,
            Key::F => self.operand = Register::F,
            Key::R => self.operand = Register::R,
            _ => ()
        }
    }

    pub fn get_operand(&self) -> Register {
        self.operand
    }
}

#[cfg(test)]
mod tests {
    use crate::alu::component::selected_operand::SelectedRegister;
    use super::*;

    #[test]
    pub fn when_a_key_is_pressed_then_select_the_a_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::A);
        assert_eq!(Register::A, selected.get_operand());
    }

    #[test]
    pub fn when_a_and_split_keys_are_pressed_then_select_the_a_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::A);
        selected.update(Key::Split);
        assert_eq!(Register::A, selected.get_operand());
    }

    #[test]
    pub fn when_split_and_a_keys_are_pressed_then_select_the_a_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::Split);
        selected.update(Key::A);
        assert_eq!(Register::A, selected.get_operand());
    }

    #[test]
    pub fn when_b_key_is_pressed_then_select_the_b_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::B);
        assert_eq!(Register::B, selected.get_operand());
    }

    #[test]
    pub fn when_b_and_split_keys_are_pressed_then_select_the_split_b_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::B);
        selected.update(Key::Split);
        assert_eq!(Register::b, selected.get_operand());
    }

    #[test]
    pub fn when_split_and_b_keys_are_pressed_then_select_the_b_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::Split);
        selected.update(Key::B);
        assert_eq!(Register::B, selected.get_operand());
    }

    #[test]
    pub fn when_c_key_is_pressed_then_select_the_c_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::C);
        assert_eq!(Register::C, selected.get_operand());
    }

    #[test]
    pub fn when_c_and_split_keys_are_pressed_then_select_the_split_c_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::C);
        selected.update(Key::Split);
        assert_eq!(Register::c, selected.get_operand());
    }

    #[test]
    pub fn when_split_and_c_keys_are_pressed_then_select_the_c_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::Split);
        selected.update(Key::C);
        assert_eq!(Register::C, selected.get_operand());
    }

    #[test]
    pub fn when_d_key_is_pressed_then_select_the_d_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::D);
        assert_eq!(Register::D, selected.get_operand());
    }

    #[test]
    pub fn when_d_and_split_keys_are_pressed_then_select_the_split_d_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::D);
        selected.update(Key::Split);
        assert_eq!(Register::d, selected.get_operand());
    }

    #[test]
    pub fn when_split_and_d_keys_are_pressed_then_select_the_d_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::Split);
        selected.update(Key::D);
        assert_eq!(Register::D, selected.get_operand());
    }

    #[test]
    pub fn when_e_key_is_pressed_then_select_the_e_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::E);
        assert_eq!(Register::E, selected.get_operand());
    }

    #[test]
    pub fn when_e_and_split_keys_are_pressed_then_select_the_split_e_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::E);
        selected.update(Key::Split);
        assert_eq!(Register::e, selected.get_operand());
    }

    #[test]
    pub fn when_split_and_e_keys_are_pressed_then_select_the_e_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::Split);
        selected.update(Key::E);
        assert_eq!(Register::E, selected.get_operand());
    }

    #[test]
    pub fn when_f_key_is_pressed_then_select_the_f_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::F);
        assert_eq!(Register::F, selected.get_operand());
    }

    #[test]
    pub fn when_f_and_split_keys_are_pressed_then_select_the_split_f_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::F);
        selected.update(Key::Split);
        assert_eq!(Register::f, selected.get_operand());
    }

    #[test]
    pub fn when_split_and_f_keys_are_pressed_then_select_the_f_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::Split);
        selected.update(Key::F);
        assert_eq!(Register::F, selected.get_operand());
    }

    #[test]
    pub fn when_r_key_is_pressed_then_select_the_r_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::R);
        assert_eq!(Register::R, selected.get_operand());
    }

    #[test]
    pub fn when_r_and_split_keys_are_pressed_then_select_the_split_r_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::R);
        selected.update(Key::Split);
        assert_eq!(Register::R, selected.get_operand());
    }

    #[test]
    pub fn when_split_and_r_keys_are_pressed_then_select_the_r_register() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::Split);
        selected.update(Key::R);
        assert_eq!(Register::R, selected.get_operand());
    }

    fn selected(preset: Key, select: Key) -> SelectedRegister {
        let mut selected = SelectedRegister::new();
        selected.update(preset);
        selected.update(select);
        selected
    }

    #[test]
    pub fn get_operand_is_idempotent() {
        let mut selected = SelectedRegister::new();
        selected.update(Key::A);
        assert_eq!(Register::A, selected.get_operand());
        assert_eq!(Register::A, selected.get_operand());
    }

    #[test]
    pub fn ignore_unmanaged_keys() {
        assert_eq!(Register::F, selected(Key::F, Key::One).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Two).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Three).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Four).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Five).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Six).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Seven).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Eight).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Nine).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Zero).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Comma).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Negative).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::V).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::W).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Y).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Z).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Power).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::GeneralReset).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::DecimalUp).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::DecimalDown).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::RecordProgram).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::PrintProgram).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::TapeAdvance).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::TapeRelease).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Add).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Subtract).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Multiply).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Divide).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::SquareRoot).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::CopyToA).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::CopyM).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Clear).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::Start).get_operand());
        assert_eq!(Register::F, selected(Key::F, Key::SwapA).get_operand());
    }
}

