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

// TODO: Most unit tests do not check edge scenarios and must be completed.

fn alu_with_m(value: Decimal) -> (DefaultAlu, mpsc::Receiver<Message>) {
    let memory = MemoryBuilder::new()
        .with_m(value.into())
        .with_ip(1)
        .build();
    
    let (sender, receiver) = mpsc::channel();

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

    (alu, receiver)
}

#[test]
fn copying_m_to_itself_changes_nothing() {

    // Arrange
    
    let (mut alu, _) = alu_with_m(1u8.into());

    // Act

    let result = alu.copy_m(Register::M);

    // Assert

    assert_eq!(Ok(()), result);
    assert_eq!(Decimal::new(1, 0), alu.memory.m.read());
    assert_eq!(2, alu.memory.ip);
}

// See discussion of Constants as instructions
// #[test]
// fn copying_m_to_accumulator() {
//     let mut cu = Alu::new();

//     cu.m.write(1.into()).unwrap();
//     cu.copy_m(Register::A);

//     assert_eq!(Decimal::new(1, 0), cu.m.read());
// }

#[test]
fn copying_m_to_r_register_changes_nothing() {

    // Arrange
    
    let (mut alu, _) = alu_with_m(1u8.into());

    // Act

    let result = alu.copy_m(Register::R);

    // Assert

    assert_eq!(Ok(()), result);
    assert_eq!(Decimal::new(0, 0), alu.memory.r.read());
    assert_eq!(2, alu.memory.ip);
}

#[test]
fn copying_m_to_b_register_always_works() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(1u8.into());

    // Act

    let result = alu.copy_m(Register::B);


    // Assert

    assert_eq!(Ok(()), result);
    assert_eq!(Decimal::new(1, 0), alu.memory.b.read());
    assert_eq!(2, alu.memory.ip);
}

#[test]
fn copying_m_to_c_register_always_works() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(1u8.into());

    // Act

    let  result = alu.copy_m(Register::C);


    // Assert

    assert_eq!(Ok(()), result);
    assert_eq!(Decimal::new(1, 0), alu.memory.c.read());
    assert_eq!(2, alu.memory.ip);
}

#[test]
fn copying_m_to_d_register_always_works() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(1u8.into());

    // Act

    let  result = alu.copy_m(Register::D);


    // Assert

    assert_eq!(Ok(()), result);
    assert_eq!(Decimal::new(1, 0), alu.memory.d.read());
    assert_eq!(2, alu.memory.ip);
}

#[test]
fn copying_m_to_e_register_always_works() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(1u8.into());

    // Act

    let  result = alu.copy_m(Register::E);

    // Assert

    assert_eq!(Ok(()), result);
    assert_eq!(Decimal::new(1, 0), alu.memory.e.read());
    assert_eq!(2, alu.memory.ip);
}

#[test]
fn copying_m_to_f_register_always_works() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(1u8.into());

    // Act

    let  result = alu.copy_m(Register::F);


    // Assert

    assert_eq!(Ok(()), result);
    assert_eq!(Decimal::new(1, 0), alu.memory.f.read());
    assert_eq!(2, alu.memory.ip);
}

#[test]
fn copying_m_to_b_split_register_succeed_when_the_value_is_fit() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(2u8.into());

    // Act

    let result = alu.copy_m(Register::b);

    // Assert

    assert_eq!(Ok(()), result);
    assert_eq!(Decimal::new(2, 0), alu.memory.b.read_left());
    assert_eq!(2, alu.memory.ip);
}

#[test]
fn copying_m_to_b_split_register_fails_if_the_value_overflows() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(123_456_789_012_345_i64.into());

    // Act

    let result = alu.copy_m(Register::b);

    // Assert

    assert!(result.is_err());
    assert_eq!(Err("Register overflow".to_string()), result);
}

#[test]
fn copying_m_to_c_split_register_succeed_when_the_value_is_fit() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(2u8.into());

    // Act

    let result = alu.copy_m(Register::c);

    // Assert

    assert_eq!(Ok(()), result);
    assert_eq!(Decimal::new(2, 0), alu.memory.c.read_left());
    assert_eq!(2, alu.memory.ip);
}

#[test]
fn copying_m_to_c_split_register_fails_if_the_value_overflows() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(123_456_789_012_345_i64.into());

    // Act

    let result = alu.copy_m(Register::c);

    // Assert

    assert!(result.is_err());
    assert_eq!(Err("Register overflow".to_string()), result);
}

#[test]
fn copying_m_to_d_split_register_succeed_when_the_value_is_fit() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(2u8.into());

    // Act

    let result = alu.copy_m(Register::d);

    // Assert

    assert_eq!(Ok(()), result);
    assert_eq!(Decimal::new(2, 0), alu.memory.d.read_left());
    assert_eq!(2, alu.memory.ip);
}

#[test]
fn copying_m_to_d_split_register_fails_if_the_value_overflows() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(123_456_789_012_345_i64.into());

    // Act

    let result = alu.copy_m(Register::d);

    // Assert

    assert!(result.is_err());
    assert_eq!(Err("Register overflow".to_string()), result);
}

#[test]
fn copying_m_to_e_split_register_succeed_when_the_value_is_fit() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(2u8.into());

    // Act

    let  result = alu.copy_m(Register::e);

    // Assert

    assert_eq!(Ok(()), result);
    assert_eq!(Decimal::new(2, 0), alu.memory.e.read_left());
    assert_eq!(2, alu.memory.ip);
}

#[test]
fn copying_m_to_e_split_register_fails_if_the_value_overflows() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(123_456_789_012_345_i64.into());

    // Act

    let  result = alu.copy_m(Register::e);

    // Assert

    assert!(result.is_err());
    assert_eq!(Err("Register overflow".to_string()), result);
}

#[test]
fn copying_m_to_f_split_register_succeed_when_the_value_is_fit() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(2u8.into());

    // Act

    let result = alu.copy_m(Register::f);

    // Assert

    assert_eq!(Ok(()), result);
    assert_eq!(Decimal::new(2, 0), alu.memory.f.read_left());
    assert_eq!(2, alu.memory.ip);
}

#[test]
fn copying_m_to_f_split_register_fails_if_the_value_overflows() {

    // Arrange
    
    let (mut alu, _receiver) = alu_with_m(123_456_789_012_345_i64.into());

    // Act

    let result = alu.copy_m(Register::f);

    // Assert

    assert!(result.is_err());
    assert_eq!(Err("Register overflow".to_string()), result);
}
