use event_simulation::Simulation;
use multilinear::{Condition, EventInfo, MultilinearInfo, MultilinearSimulation};

#[test]
fn test_simulation_events_without_conditions() {
    let mut info = MultilinearInfo::new();
    info.add_channel();

    let mut event_info1 = EventInfo::new();
    event_info1.add_change(&[]).unwrap();
    let event1 = info.add_event(event_info1);

    let mut event_info2 = EventInfo::new();
    event_info2.add_change(&[]).unwrap();
    let event2 = info.add_event(event_info2);

    let mut simulation = MultilinearSimulation::new(info);
    assert_eq!(simulation.data(), vec![0]);

    assert!(simulation.try_call(event1));
    assert_eq!(simulation.data(), vec![0]);

    assert!(simulation.try_call(event2));
    assert_eq!(simulation.data(), vec![0]);

    assert!(simulation.try_revert(event2));
    assert_eq!(simulation.data(), vec![0]);

    assert!(simulation.try_revert(event1));
    assert_eq!(simulation.data(), vec![0]);
}

#[test]
fn test_simulation_linear() {
    let mut info = MultilinearInfo::new();
    let channel = info.add_channel();

    let condition1 = vec![Condition::change(channel, 0, 1)];
    let condition2 = vec![Condition::change(channel, 1, 2)];
    let condition3 = vec![Condition::change(channel, 2, 3)];
    let condition4 = vec![Condition::change(channel, 3, 4)];
    let condition5 = vec![Condition::change(channel, 4, 5)];

    let mut event_info1 = EventInfo::new();
    event_info1.add_change(&condition1).unwrap();
    let event1 = info.add_event(event_info1);

    let mut event_info2 = EventInfo::new();
    event_info2.add_change(&condition2).unwrap();
    let event2 = info.add_event(event_info2);

    let mut event_info3 = EventInfo::new();
    event_info3.add_change(&condition3).unwrap();
    let event3 = info.add_event(event_info3);

    let mut event_info4 = EventInfo::new();
    event_info4.add_change(&condition4).unwrap();
    let event4 = info.add_event(event_info4);

    let mut event_info5 = EventInfo::new();
    event_info5.add_change(&condition5).unwrap();
    let event5 = info.add_event(event_info5);

    let mut simulation = MultilinearSimulation::new(info);
    assert_eq!(simulation.data(), vec![0]);

    assert!(simulation.try_call(event1));
    assert_eq!(simulation.data(), vec![1]);

    assert!(simulation.try_call(event2));
    assert_eq!(simulation.data(), vec![2]);

    assert!(simulation.try_call(event3));
    assert_eq!(simulation.data(), vec![3]);

    assert!(simulation.try_call(event4));
    assert_eq!(simulation.data(), vec![4]);

    assert!(simulation.try_call(event5));
    assert_eq!(simulation.data(), vec![5]);

    assert!(simulation.try_revert(event5));
    assert_eq!(simulation.data(), vec![4]);

    assert!(simulation.try_revert(event4));
    assert_eq!(simulation.data(), vec![3]);

    assert!(simulation.try_revert(event3));
    assert_eq!(simulation.data(), vec![2]);

    assert!(simulation.try_revert(event2));
    assert_eq!(simulation.data(), vec![1]);

    assert!(simulation.try_revert(event1));
    assert_eq!(simulation.data(), vec![0]);
}

#[test]
fn test_simulation_linear_count() {
    let mut info = MultilinearInfo::new();
    let channel = info.add_channel();

    let condition1 = vec![Condition::change(channel, 0, 1)];
    let condition2 = vec![Condition::change(channel, 1, 2)];
    let condition3 = vec![Condition::change(channel, 2, 3)];
    let condition4 = vec![Condition::change(channel, 3, 4)];
    let condition5 = vec![Condition::change(channel, 4, 5)];

    let mut event_info = EventInfo::new();
    event_info.add_change(&condition1).unwrap();
    event_info.add_change(&condition2).unwrap();
    event_info.add_change(&condition3).unwrap();
    event_info.add_change(&condition4).unwrap();
    event_info.add_change(&condition5).unwrap();
    let event = info.add_event(event_info);

    let mut simulation = MultilinearSimulation::new(info);
    assert_eq!(simulation.data(), vec![0]);

    assert!(simulation.try_call(event));
    assert_eq!(simulation.data(), vec![1]);

    assert!(simulation.try_call(event));
    assert_eq!(simulation.data(), vec![2]);

    assert!(simulation.try_call(event));
    assert_eq!(simulation.data(), vec![3]);

    assert!(simulation.try_call(event));
    assert_eq!(simulation.data(), vec![4]);

    assert!(simulation.try_call(event));
    assert_eq!(simulation.data(), vec![5]);

    assert!(simulation.try_revert(event));
    assert_eq!(simulation.data(), vec![4]);

    assert!(simulation.try_revert(event));
    assert_eq!(simulation.data(), vec![3]);

    assert!(simulation.try_revert(event));
    assert_eq!(simulation.data(), vec![2]);

    assert!(simulation.try_revert(event));
    assert_eq!(simulation.data(), vec![1]);

    assert!(simulation.try_revert(event));
    assert_eq!(simulation.data(), vec![0]);
}

#[test]
fn test_simulation_branching() {
    let mut info = MultilinearInfo::new();
    let channel = info.add_channel();

    let condition_start_to_left = vec![Condition::change(channel, 0, 1)];
    let condition_start_to_right = vec![Condition::change(channel, 0, 2)];
    let condition_right_to_end = vec![Condition::change(channel, 2, 3)];
    let condition_left_to_end = vec![Condition::change(channel, 1, 3)];

    let mut event_info_start_to_left = EventInfo::new();
    event_info_start_to_left
        .add_change(&condition_start_to_left)
        .unwrap();
    let event_start_to_left = info.add_event(event_info_start_to_left);

    let mut event_info_start_to_right = EventInfo::new();
    event_info_start_to_right
        .add_change(&condition_start_to_right)
        .unwrap();
    let event_start_to_right = info.add_event(event_info_start_to_right);

    let mut event_info_right_to_end = EventInfo::new();
    event_info_right_to_end
        .add_change(&condition_right_to_end)
        .unwrap();
    let event_right_to_end = info.add_event(event_info_right_to_end);

    let mut event_info_left_to_end = EventInfo::new();
    event_info_left_to_end
        .add_change(&condition_left_to_end)
        .unwrap();
    let event_left_to_end = info.add_event(event_info_left_to_end);

    let mut simulation = MultilinearSimulation::new(info);
    assert_eq!(simulation.data(), vec![0]);

    assert!(simulation.try_call(event_start_to_left));
    assert_eq!(simulation.data(), vec![1]);
    assert!(simulation.try_call(event_left_to_end));
    assert_eq!(simulation.data(), vec![3]);
    assert!(simulation.try_revert(event_right_to_end));
    assert_eq!(simulation.data(), vec![2]);
    assert!(simulation.try_revert(event_start_to_right));
    assert_eq!(simulation.data(), vec![0]);

    assert!(simulation.try_call(event_start_to_right));
    assert_eq!(simulation.data(), vec![2]);
    assert!(simulation.try_call(event_right_to_end));
    assert_eq!(simulation.data(), vec![3]);
    assert!(simulation.try_revert(event_left_to_end));
    assert_eq!(simulation.data(), vec![1]);
    assert!(simulation.try_revert(event_start_to_left));
    assert_eq!(simulation.data(), vec![0]);
}

#[test]
fn test_simulation_back_forth() {
    let mut info = MultilinearInfo::new();
    let channel = info.add_channel();

    let condition1 = vec![Condition::change(channel, 0, 1)];
    let condition2 = vec![Condition::change(channel, 1, 0)];

    let mut event_info1 = EventInfo::new();
    event_info1.add_change(&condition1).unwrap();
    let event1 = info.add_event(event_info1);

    let mut event_info2 = EventInfo::new();
    event_info2.add_change(&condition2).unwrap();
    let event2 = info.add_event(event_info2);

    let mut simulation = MultilinearSimulation::new(info);
    assert_eq!(simulation.data(), vec![0]);

    assert!(!simulation.try_call(event2));
    assert!(simulation.try_call(event1));
    assert_eq!(simulation.data(), vec![1]);

    assert!(!simulation.try_call(event1));
    assert!(simulation.try_call(event2));
    assert_eq!(simulation.data(), vec![0]);

    assert!(!simulation.try_revert(event1));
    assert!(simulation.try_revert(event2));
    assert_eq!(simulation.data(), vec![1]);

    assert!(!simulation.try_revert(event2));
    assert!(simulation.try_revert(event1));
    assert_eq!(simulation.data(), vec![0]);
}

#[test]
fn test_simulation_multiple_channels_same() {
    let mut info = MultilinearInfo::new();
    let channel1 = info.add_channel();
    let channel2 = info.add_channel();

    let condition1 = vec![
        Condition::change(channel1, 0, 1),
        Condition::change(channel2, 0, 1),
    ];
    let condition2 = vec![
        Condition::change(channel1, 1, 2),
        Condition::change(channel2, 1, 2),
    ];

    let mut event_info1 = EventInfo::new();
    event_info1.add_change(&condition1).unwrap();
    let event1 = info.add_event(event_info1);

    let mut event_info2 = EventInfo::new();
    event_info2.add_change(&condition2).unwrap();
    let event2 = info.add_event(event_info2);

    let mut simulation = MultilinearSimulation::new(info);
    assert_eq!(simulation.data(), vec![0, 0]);

    assert!(simulation.try_call(event1));
    assert_eq!(simulation.data(), vec![1, 1]);

    assert!(simulation.try_call(event2));
    assert_eq!(simulation.data(), vec![2, 2]);

    assert!(simulation.try_revert(event2));
    assert_eq!(simulation.data(), vec![1, 1]);

    assert!(simulation.try_revert(event1));
    assert_eq!(simulation.data(), vec![0, 0]);
}

#[test]
fn test_simulation_multiple_channels() {
    let mut info = MultilinearInfo::new();
    let channel1 = info.add_channel();
    let channel2 = info.add_channel();
    let channel3 = info.add_channel();

    let condition1 = vec![
        Condition::change(channel1, 0, 1),
        Condition::new(channel2, 0),
        Condition::change(channel3, 0, 1),
    ];
    let condition2 = vec![
        Condition::change(channel1, 1, 2),
        Condition::change(channel3, 1, 0),
    ];
    let condition3 = vec![
        Condition::change(channel1, 2, 3),
        Condition::change(channel2, 0, 1),
        Condition::new(channel3, 0),
    ];

    let mut event_info1 = EventInfo::new();
    event_info1.add_change(&condition1).unwrap();
    let event1 = info.add_event(event_info1);

    let mut event_info2 = EventInfo::new();
    event_info2.add_change(&condition2).unwrap();
    let event2 = info.add_event(event_info2);

    let mut event_info3 = EventInfo::new();
    event_info3.add_change(&condition3).unwrap();
    let event3 = info.add_event(event_info3);

    let mut simulation = MultilinearSimulation::new(info);
    assert_eq!(simulation.data(), vec![0, 0, 0]);

    assert!(simulation.try_call(event1));
    assert_eq!(simulation.data(), vec![1, 0, 1]);

    assert!(simulation.try_call(event2));
    assert_eq!(simulation.data(), vec![2, 0, 0]);

    assert!(simulation.try_call(event3));
    assert_eq!(simulation.data(), vec![3, 1, 0]);

    assert!(simulation.try_revert(event3));
    assert_eq!(simulation.data(), vec![2, 0, 0]);

    assert!(simulation.try_revert(event2));
    assert_eq!(simulation.data(), vec![1, 0, 1]);

    assert!(simulation.try_revert(event1));
    assert_eq!(simulation.data(), vec![0, 0, 0]);
}

#[test]
fn test_simulation_condition_triangle() {
    let mut info = MultilinearInfo::new();
    let channel1 = info.add_channel();
    let channel2 = info.add_channel();
    let channel3 = info.add_channel();

    let condition1 = vec![
        Condition::change(channel1, 0, 1),
        Condition::change(channel2, 0, 1),
    ];
    let condition2 = vec![
        Condition::change(channel2, 1, 0),
        Condition::change(channel3, 0, 1),
    ];
    let condition3 = vec![
        Condition::change(channel3, 1, 0),
        Condition::change(channel1, 1, 0),
    ];

    let mut event_info = EventInfo::new();
    event_info.add_change(&condition1).unwrap();
    event_info.add_change(&condition2).unwrap();
    event_info.add_change(&condition3).unwrap();
    let event = info.add_event(event_info);

    let mut simulation = MultilinearSimulation::new(info);
    assert_eq!(simulation.data(), vec![0, 0, 0]);

    assert!(simulation.try_call(event));
    assert_eq!(simulation.data(), vec![1, 1, 0]);

    assert!(simulation.try_call(event));
    assert_eq!(simulation.data(), vec![1, 0, 1]);

    assert!(simulation.try_call(event));
    assert_eq!(simulation.data(), vec![0, 0, 0]);
}
