mod common;
pub mod graph;
mod utils;

use std::cmp::max;
use std::env;

use crate::common::{Distance, Node, NodeSet, Result};

struct Memo<'a> {
    max_dist: &'a mut Distance,
    prev_dist: Distance,
    visited: &'a mut NodeSet,
}

fn depth_first_search(node: &Node, memo: Memo<'_>) {
    memo.visited.insert(node.clone());

    let mut distance = 0;

    for (out_node, out_weight) in &node.borrow().outgoing {
        if !memo.visited.contains(&out_node) {
            distance = memo.prev_dist + out_weight;

            depth_first_search(
                &out_node,
                Memo {
                    max_dist: &mut *memo.max_dist,
                    prev_dist: distance,
                    visited: &mut *memo.visited,
                },
            );
        }

        if *memo.max_dist < distance {
            *memo.max_dist = distance;
        }

        distance = 0
    }
}

fn visit_all_longest(start_node: &Node) -> Distance {
    let mut visited = NodeSet::new();

    let mut distance = 0;

    depth_first_search(
        &start_node,
        Memo {
            max_dist: &mut distance,
            prev_dist: 0,
            visited: &mut visited,
        },
    );

    distance
}

fn main() -> Result<()> {
    let path = env::args().nth(1).unwrap();

    let directions = common::parse_file(path)?;

    let mut longest_distance = 0;

    for node in &directions.nodes {
        let distance = visit_all_longest(node);
        longest_distance = max(distance, longest_distance);
    }

    println!("{}", longest_distance);

    Ok(())
}