use crate::alu::*;
use crate::message::*;
use p101_is::instruction::*;
use std::sync::mpsc;
use rust_decimal::Decimal;

fn new_alu(d: MemoryRegister, r: SplitRegister) -> (DefaultAlu, mpsc::Receiver<Message>) {
    let mut memory = Memory::new();

    memory.d = d;
    memory.r = r;
    
    let (sender, receiver) = mpsc::channel();

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

    (alu, receiver)
}

#[test]
pub fn when_d_is_a_register_exchange_its_value_with_r() {
    let r = Decimal::new(9, 0);
    let d = Decimal::new(11, 1);

    let (mut alu, _) = new_alu(d.into(), r.into());

    let result = alu.dr_exchange();

    assert_eq!(Ok(()), result);
    assert_eq!(alu.memory.d.read(), r);
    assert_eq!(alu.memory.r.read(), d);

    let result = alu.dr_exchange();

    assert_eq!(Ok(()), result);
    assert_eq!(alu.memory.d.read(), d);
    assert_eq!(alu.memory.r.read(), r);
}

#[test]
pub fn when_d_is_split_exchange_its_value_with_r() {
    let r = Decimal::new(9, 0);
    let d = (Decimal::new(11, 1), Decimal::new(77, 1));

    let (mut alu, _) = new_alu(d.into(), r.into());

    let result = alu.dr_exchange();

    assert_eq!(Ok(()), result);
    assert_eq!(alu.memory.d.read(), r);
    assert_eq!(alu.memory.r.read(), d.1);
    assert_eq!(alu.memory.r.read_left(), d.0);

    let result = alu.dr_exchange();

    assert_eq!(Ok(()), result);
    assert_eq!(alu.memory.d.read(), d.1);
    assert_eq!(alu.memory.d.read_left(), d.0);
    assert_eq!(alu.memory.r.read(), r);
}

#[test]
pub fn when_d_contains_both_data_and_instructions_exchange_value_with_r_zeroing_the_instruction_part() {
    let r = Decimal::new(9, 0);

    let mut i = [None; 11];
    i[0] = Some(Instruction::Abs);
    i[1] = Some(Instruction::NewLine);

    let d = (Decimal::new(77, 1), i);

    let (mut alu, _) = new_alu(d.into(), r.into());

    let result = alu.dr_exchange();

    assert_eq!(Ok(()), result);
    assert_eq!(alu.memory.d.read(), r);
    assert_eq!(alu.memory.r.read(), d.0);
    assert_eq!(alu.memory.r.read_left(), 0.into());

    let result = alu.dr_exchange();

    assert_eq!(Ok(()), result);
    assert_eq!(alu.memory.r.read(), r);
    assert_eq!(alu.memory.d.read(), d.0);
    assert_eq!(alu.memory.d.read_left(), 0.into());
}
