use crate::util::DirectedGraph;
use std::collections::HashMap;

fn dfs(g: &mut DirectedGraph, node: usize, time: &mut usize) -> () {
    let mut nodes: Vec<(usize, usize)> = vec![(node, 0)];
    g.explored[node] = true;

    while nodes.len() > 0 {
        let (mut current_node, mut current_edge) = nodes.pop().unwrap();
        while current_edge < g.nodes[current_node].len() {
            let next_node = g.nodes[current_node][current_edge];
            if !g.explored[next_node] {
                nodes.push((current_node, current_edge + 1));
                current_node = next_node;
                current_edge = 0;
                g.explored[current_node] = true;
            } else {
                current_edge += 1;
            }
        }
        *time += 1;
        g.time[current_node] = *time;
    }
}

fn second_dfs(g: &mut DirectedGraph, node: usize, leader: usize) -> () {
    let mut nodes: Vec<(usize, usize)> = vec![(node, 0)];
    g.explored[node] = true;
    g.leader[node] = leader;
    while nodes.len() > 0 {
        let (mut current_node, mut current_edge) = nodes.pop().unwrap();
        while current_edge < g.nodes[current_node].len() {
            let next_node = g.nodes[current_node][current_edge];
            if !g.explored[next_node] {
                nodes.push((current_node, current_edge + 1));
                current_node = next_node;
                current_edge = 0;
                g.leader[current_node] = leader;
                g.explored[current_node] = true;
            } else {
                current_edge += 1;
            }
        }
    }
}

pub fn ssc(mut g: DirectedGraph, mut g_dash: DirectedGraph) -> Vec<usize> {
    println!("1. start first dfs");
    let mut time: usize = 0;
    for i in (0..g_dash.nodes.len()).rev() {
        if !g_dash.explored[i] {
            dfs(&mut g_dash, i, &mut time);
        }
    }

    println!("2. create time to index mapping");
    let mut time_to_index: Vec<usize> = vec![0; g.nodes.len()];
    for node in 0..g_dash.nodes.len() {
        time_to_index[g_dash.time[node] - 1] = node;
    }

    println!("3. start second dfs");
    for time in (0..time_to_index.len()).rev() {
        let node = time_to_index[time];
        if !g.explored[node] {
            second_dfs(&mut g, node, node);
        }
    }

    println!("4. print results");
    let mut leader_size: HashMap<usize, usize> = HashMap::new();
    for node in 0..g.nodes.len() {
        let leader = g.leader[node];
        let leader_size = leader_size.entry(leader).or_insert(0);
        *leader_size += 1;
    }

    let mut sizes: Vec<usize> = leader_size.values().cloned().collect();
    sizes.sort();
    sizes.reverse();

    // For debugging the finish time and leader functions.
    // for i in 0..g_dash.nodes.len() {
    //     println!("f({})={}", i + 1, g_dash.time[i]);
    // }
    // for i in 0..g.nodes.len() {
    // 	println!("leader({})={}", i + 1, g.leader[i] + 1);
    // }
    sizes
}
