// ----------------------------------------------------------------
// IMPORTS
// ----------------------------------------------------------------

use rstest::fixture;
use rstest::rstest;
use std::fmt::Display;
use std::hash::Hash;

use ads2::core::utils;
use ads2::graphs::graph;
use ads2::graphs::tarjan;

// ----------------------------------------------------------------
// Fixtures
// ----------------------------------------------------------------

#[fixture]
fn fixture_graph1() -> graph::Graph<i32> {
    return graph::Graph::<i32>::new(
        vec![1,2,3,4],
        vec![(1,2), (2,4), (4,2)]
    );
}

#[fixture]
fn fixture_graph2() -> graph::Graph<i32> {
    return graph::Graph::<i32>::new(
        vec![1,2,3,4,5,6,7],
        vec![(1,2), (1,3), (2,3), (3,4), (4,5), (5,2), (5,6), (5,7), (6,7)]
    );
}

#[fixture]
fn fixture_graph3() -> graph::Graph<i32> {
    return graph::Graph::<i32>::new(
        vec![1,2,3,4,5,6,7,8],
        vec![
            (1,2),
            (1,3),
            (2,4),
            (2,5),
            (3,5),
            (3,6),
            (3,8),
            (4,5),
            (4,7),
            (5,1),
            (5,8),
            (6,8),
            (7,8),
            (8,6),
        ],
    );
}

// ----------------------------------------------------------------
// Test Tarjan-Algorithm
// ----------------------------------------------------------------

#[rstest]
#[case(fixture_graph1(), vec![vec![1], vec![3], vec![2,4]])]
#[case(fixture_graph2(), vec![vec![1], vec![6], vec![7], vec![2,3,4,5]])]
#[case(fixture_graph3(), vec![vec![1,2,3,4,5], vec![7], vec![6,8]])]
fn test_tarjan<T>(#[case] gph: graph::Graph<T>, #[case] expected: Vec<Vec<T>>)
    where T: Eq + Hash + Clone + Copy + Display
{
    let components = tarjan::tarjan_algorithm(&gph, false);
    assert_components_eq(&components, &expected)
}

// ----------------------------------------------------------------
// AUXILIARY METHODS
// ----------------------------------------------------------------

fn check_components_eq<T>(components1: &Vec<Vec<T>>, components2: &Vec<Vec<T>>) -> bool
    where T: Eq + Hash + Clone
{
    if components1.len() != components2.len() {
        return false
    }

    for component1 in components1 {
        let mut found = false;
        for component2 in components2 {
            if utils::vec_to_set(component1) == utils::vec_to_set(component2) {
                found = true;
                break;
            }
        }
        if !found {
            return false;
        }
    }
    return true;
}

fn assert_components_eq<T>(components1: &Vec<Vec<T>>, components2: &Vec<Vec<T>>)
    where T: Eq + Hash + Clone
{
    assert!(check_components_eq(components1, components2));
}