use crate::alu::*;
use crate::message::*;
use p101_is::*;
use std::sync::mpsc;

const LABELS: [Option<Instruction>; 24] = [
    Some(Instruction::Label(JumpDestination::AV)),
    Some(Instruction::Label(JumpDestination::AW)),
    Some(Instruction::Label(JumpDestination::AY)),
    Some(Instruction::Label(JumpDestination::AZ)),
    Some(Instruction::Label(JumpDestination::BV)),
    Some(Instruction::Label(JumpDestination::BW)),
    Some(Instruction::Label(JumpDestination::BY)),
    Some(Instruction::Label(JumpDestination::BZ)),
    Some(Instruction::Label(JumpDestination::EV)),
    Some(Instruction::Label(JumpDestination::EW)),
    Some(Instruction::Label(JumpDestination::EY)),
    Some(Instruction::Label(JumpDestination::EZ)),
    Some(Instruction::Label(JumpDestination::FV)),
    Some(Instruction::Label(JumpDestination::FW)),
    Some(Instruction::Label(JumpDestination::FY)),
    Some(Instruction::Label(JumpDestination::FZ)),
    None,
    None,
    None,
    None,
    None,
    None,
    None,
    None,
];

fn alu_with_labels() -> (DefaultAlu, mpsc::Receiver<Message>) {
    let memory = MemoryBuilder::new()
        .with_p1(LABELS.into())
        .with_ip(24)
        .build();

    let (sender, receiver) = mpsc::channel();

    let alu = DefaultAlu::new_with_memory(sender, memory);

    (alu, receiver)
}

#[test]
pub fn jump_to_av_should_set_pc_to_0() {
    let (mut alu, _) = alu_with_labels();

    let result = alu.jump(Origin::V);

    assert_eq!(Ok(()), result);
    assert_eq!(0, alu.memory.ip);
}

#[test]
pub fn jump_to_aw_should_set_pc_to_1() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::W);

    assert_eq!(Ok(()), result);
    assert_eq!(1, alu.memory.ip);
}

#[test]
pub fn jump_to_ay_should_set_pc_to_2() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::Y);

    assert_eq!(Ok(()), result);
    assert_eq!(2, alu.memory.ip);
}

#[test]
pub fn jump_to_az_should_set_pc_to_3() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::Z);

    assert_eq!(Ok(()), result);
    assert_eq!(3, alu.memory.ip);
}

#[test]
pub fn jump_to_cv_should_set_pc_to_4() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::CV);

    assert_eq!(Ok(()), result);
    assert_eq!(4, alu.memory.ip);
}

#[test]
pub fn jump_to_cw_should_set_pc_to_5() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::CW);

    assert_eq!(Ok(()), result);
    assert_eq!(5, alu.memory.ip);
}

#[test]
pub fn jump_to_cy_should_set_pc_to_6() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::CY);

    assert_eq!(Ok(()), result);
    assert_eq!(6, alu.memory.ip);
}

#[test]
pub fn jump_to_cz_should_set_pc_to_7() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::CZ);

    assert_eq!(Ok(()), result);
    assert_eq!(7, alu.memory.ip);
}

#[test]
pub fn jump_to_dv_should_set_pc_to_8() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::DV);

    assert_eq!(Ok(()), result);
    assert_eq!(8, alu.memory.ip);
}

#[test]
pub fn jump_to_dw_should_set_pc_to_9() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::DW);

    assert_eq!(Ok(()), result);
    assert_eq!(9, alu.memory.ip);
}

#[test]
pub fn jump_to_dy_should_set_pc_to_10() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::DY);

    assert_eq!(Ok(()), result);
    assert_eq!(10, alu.memory.ip);
}

#[test]
pub fn jump_to_dz_should_set_pc_to_11() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::DZ);

    assert_eq!(Ok(()), result);
    assert_eq!(11, alu.memory.ip);
}

#[test]
pub fn jump_to_rv_should_set_pc_to_12() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::RV);

    assert_eq!(Ok(()), result);
    assert_eq!(12, alu.memory.ip);
}

#[test]
pub fn jump_to_rw_should_set_pc_to_13() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::RW);

    assert_eq!(Ok(()), result);
    assert_eq!(13, alu.memory.ip);
}

#[test]
pub fn jump_to_ry_should_set_pc_to_14() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::RY);

    assert_eq!(Ok(()), result);
    assert_eq!(14, alu.memory.ip);
}

#[test]
pub fn jump_to_rz_should_set_pc_to_15() {
    let (mut alu, _) = alu_with_labels();
    
    let result = alu.jump(Origin::RZ);

    assert_eq!(Ok(()), result);
    assert_eq!(15, alu.memory.ip);
}
