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

fn alu_with_memory(memory: Memory) -> (DefaultAlu, mpsc::Receiver<Message>) {
    let (sender, receiver) = mpsc::channel();

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

    (alu, receiver)
}

fn read_events_until_timeout(receiver: mpsc::Receiver<Message>) -> Vec<Message> {
    const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(1);
    let mut messages = Vec::new();

    loop {
        if let Ok(message) = receiver.recv_timeout(TIMEOUT) {
            messages.push(message);
        } else {
            break;
        }
    }

    return messages;
}

#[test]
fn resetting_the_accumulator_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let value = Decimal::new(123, 2);
    let expected = Message::Print(value, Register::A, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_a(value)
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::A);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(zero, alu.memory.a.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_b_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let value = Decimal::new(123, 2);
    let expected = Message::Print(value, Register::B, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_b(value.into())
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::B);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(zero, alu.memory.b.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_b_left_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let left_value = Decimal::new(123, 2);
    let right_value = Decimal::new(200, 2);
    let expected = Message::Print(left_value, Register::b, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_b((left_value, right_value).into())
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::b);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(zero, alu.memory.b.read_left());
    assert_eq!(right_value, alu.memory.b.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_b_right_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let left_value = Decimal::new(123, 2);
    let right_value = Decimal::new(200, 2);
    let expected = Message::Print(right_value, Register::B, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_b((left_value, right_value).into())
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::B);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(left_value, alu.memory.b.read_left());
    assert_eq!(zero, alu.memory.b.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_c_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let value = Decimal::new(123, 2);
    let expected = Message::Print(value, Register::C, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_c(value.into())
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::C);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(zero, alu.memory.c.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_c_left_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let left_value = Decimal::new(123, 2);
    let right_value = Decimal::new(200, 2);
    let expected = Message::Print(left_value, Register::c, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_c((left_value, right_value).into())
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::c);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(zero, alu.memory.c.read_left());
    assert_eq!(right_value, alu.memory.c.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_c_right_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let left_value = Decimal::new(123, 2);
    let right_value = Decimal::new(200, 2);
    let expected= Message::Print(right_value, Register::C, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_c((left_value, right_value).into())
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::C);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(left_value, alu.memory.c.read_left());
    assert_eq!(zero, alu.memory.c.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_d_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let value = Decimal::new(123, 2);
    let expected = Message::Print(value, Register::D, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_d(value.into())
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::D);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(zero, alu.memory.d.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_d_left_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let left_value = Decimal::new(123, 2);
    let right_value = Decimal::new(200, 2);
    let expected= Message::Print(left_value, Register::d, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_d((left_value, right_value).into())
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::d);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(zero, alu.memory.d.read_left());
    assert_eq!(right_value, alu.memory.d.read());
    assert!(messages.contains(&expected));
}

fn resetting_the_d_right_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let left_value = Decimal::new(123, 2);
    let right_value = Decimal::new(200, 2);
    let expected= Message::Print(right_value, Register::C, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_d((left_value, right_value).into())
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::D);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(left_value, alu.memory.d.read_left());
    assert_eq!(zero, alu.memory.d.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_e_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let value = Decimal::new(123, 2);
    let expected = Message::Print(value, Register::E, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_e(value.into())
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::E);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(zero, alu.memory.e.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_e_left_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let left_value = Decimal::new(123, 2);
    let right_value = Decimal::new(200, 2);
    let expected = Message::Print(left_value, Register::e, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_e((left_value, right_value).into())
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::e);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(zero, alu.memory.e.read_left());
    assert_eq!(right_value, alu.memory.e.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_e_right_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let left_value = Decimal::new(123, 2);
    let right_value = Decimal::new(200, 2);
    let expected = Message::Print(right_value, Register::E, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_e((left_value, right_value).into())
        .with_precision(2)
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::E);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(left_value, alu.memory.e.read_left());
    assert_eq!(zero, alu.memory.e.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_f_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let value = Decimal::new(123, 2);
    let expected = Message::Print(value, Register::F, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_precision(2)
        .with_f(value.into())
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::F);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(zero, alu.memory.f.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_f_left_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let left_value = Decimal::new(123, 2);
    let right_value = Decimal::new(200, 2);
    let expected = Message::Print(left_value, Register::f, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_precision(2)
        .with_f((left_value, right_value).into())
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::f);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(zero, alu.memory.f.read_left());
    assert_eq!(right_value, alu.memory.f.read());
    assert!(messages.contains(&expected));
}

#[test]
fn resetting_the_f_right_register_writes_a_zero_in_it_and_prints_its_previous_value() {
    let zero = Decimal::new(0, 2);
    let left_value = Decimal::new(123, 2);
    let right_value = Decimal::new(200, 2);
    let expected= Message::Print(right_value, Register::F, Operator::Reset);

    let memory = MemoryBuilder::new()
        .with_precision(2)
        .with_f((left_value, right_value).into())
        .build();

    let (mut alu, receiver) = alu_with_memory(memory);

    let result = alu.reset(Register::F);
    let messages = read_events_until_timeout(receiver);

    assert_eq!(Ok(()), result);
    assert_eq!(left_value, alu.memory.f.read_left());
    assert_eq!(zero, alu.memory.f.read());
    assert!(messages.contains(&expected));
}
