use super::*;
use crate::alu::TestAluBuilder;
use std::thread;

// TODO: It is not clear what the following test should prove so I commented it out.
// Understand what happens when "S" key is pressed while no program is running.
// Emulator, for instance, prints: 0 S

/*
#[test]
pub fn perform_a_start_stop_from_user_input() {
    // Arrange

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

    let alu = TestAluBuilder::new(sender.clone())
        .build();

    let mut component = AluComponent::new(alu, sender.clone());
    let component_sender = component.link.component_sender.clone();

    // Act

    let handle = thread::spawn(move || {
        component.run();
        return component.alu;
    });

    component_sender.send(Message::SystemStatusChanged(SystemStatus::Ready)).unwrap();
    component_sender.send(Message::KeyPressed(Key::Start)).unwrap();
    component_sender.send(Message::Halt).unwrap();

    let alu = handle.join().expect("The component should halt");

    // Assert

    assert_eq!(1, alu.log.len(), "One operation should have been logged");
    assert_eq!("start_stop", alu.log[0], "Should record an START_STOP(M) operation");
    assert_eq!(0, alu.ip, "IP should be unchanged");
}
*/


#[test]
pub fn when_a_stop_instruction_is_executed_then_suspend_the_program_and_wait_user_input() {
    // Arrange

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

    let alu = TestAluBuilder::new(sender.clone())
        .instr(0, Instruction::Label(JumpDestination::AV))
        .instr(1, Instruction::Stop)
        .build();

    let mut component = AluComponent::new(alu, sender.clone());
    let component_sender = component.link.component_sender.clone();

    // Act

    let handle = thread::spawn(move || {
        component.run();
        return component.alu;
    });

    component_sender.send(Message::SystemStatusChanged(SystemStatus::Ready)).unwrap();
    component_sender.send(Message::KeyPressed(Key::V)).unwrap();
    component_sender.send(Message::Halt).unwrap();

    let _alu = handle.join().expect("The component should halt");

    // Assert

    let started = receiver.try_recv();
    assert!(started.is_ok());
    assert_eq!(Message::ProgramStarted, started.unwrap(), "Expecting a Message::ProgramStarted");

    let stopped = receiver.try_recv();
    assert!(stopped.is_ok());
    assert_eq!(Message::ProgramStopped, stopped.unwrap(), "Expecting a Message::ProgramStopped");
}

#[test]
pub fn continue_program_execution_once_input_has_been_provided() {
    // Arrange

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

    let alu = TestAluBuilder::new(sender.clone())
        .instr(0, Instruction::Label(JumpDestination::AV))
        .instr(1, Instruction::Stop)
        .build();

    let mut component = AluComponent::new(alu, sender.clone());
    let component_sender = component.link.component_sender.clone();
    component.status = AluComponentStatus::StandBy;

    // Act

    let handle = thread::spawn(move || {
        component.run();
        return component.alu;
    });

    component_sender.send(Message::SystemStatusChanged(SystemStatus::Ready)).unwrap();
    component_sender.send(Message::KeyPressed(Key::V)).unwrap();
    component_sender.send(Message::KeyPressed(Key::One)).unwrap();
    component_sender.send(Message::KeyPressed(Key::Start)).unwrap();
    component_sender.send(Message::Halt).unwrap();

    let _alu = handle.join().expect("The component should halt");

    // Assert

    let started = receiver.try_recv();
    assert!(started.is_ok());
    assert_eq!(Message::ProgramStarted, started.unwrap(), "Expecting a Message::ProgramStarted");

    let stopped = receiver.try_recv();
    assert!(stopped.is_ok());
    assert_eq!(Message::ProgramStopped, stopped.unwrap(), "Expecting a Message::ProgramStopped");

    let restarted = receiver.try_recv();
    assert!(restarted.is_ok());
    assert_eq!(Message::ProgramStarted, restarted.unwrap(), "Expecting a Message::ProgramStarted");

    let ended = receiver.try_recv();
    assert!(ended.is_ok());
    assert_eq!(Message::ProgramEnded, ended.unwrap(), "Expecting a Message::ProgramEnded");
}
