use crate::jobs;
use crate::k_clustering;
use crate::k_clustering_big;
use crate::min_cut;
use crate::prims;
use crate::ssc;
use std::fs::File;
use std::io;
use std::io::{BufRead, BufReader, Error, ErrorKind};
use std::vec::Vec;

pub fn read_to_vector(path: &str) -> Result<Vec<i64>, io::Error> {
    let file = File::open(path)?;
    let br = BufReader::new(file);
    let mut v = Vec::new();
    for line in br.lines() {
        let line = line?;
        let n = line
            .trim()
            .parse()
            .map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
        v.push(n);
    }
    Ok(v)
}

pub fn read_to_graph(path: &str) -> Result<min_cut::Graph, io::Error> {
    let mut g = min_cut::Graph {
        nodes: vec![],
        edges: vec![],
    };
    let file = File::open(path)?;
    let br = BufReader::new(file);

    for line in br.lines() {
        let line = line?;
        let mut nodes = line.split_whitespace();
        let current_node = nodes.next().unwrap().parse().unwrap();
        g.nodes.push(current_node);

        for neightbor in nodes {
            let neightbor: u32 = neightbor.parse().unwrap();
            // If the neighbor is smaller than the current node that means we
            // have already added it when we iterated over that smaller node.
            // Hence, we do not add the edge again.
            if neightbor > current_node {
                let edge = (current_node, neightbor);
                g.edges.push(edge);
            }
        }
    }
    Ok(g)
}

fn get_node_size(path: &str) -> Result<usize, io::Error> {
    let mut node_size = 0;
    let file = File::open(path)?;
    let br = BufReader::new(file);

    for line in br.lines() {
        let line = line?;
        let mut nodes = line.split_whitespace();
        let node: usize = nodes.next().unwrap().parse().unwrap();
        if node > node_size {
            node_size = node;
        }
    }
    Ok(node_size)
}

pub fn read_to_directed_graph(
    path: &str,
) -> Result<(ssc::DirectedGraph, ssc::DirectedGraph), io::Error> {
    let nodes: usize = get_node_size(path).unwrap();
    let mut g = ssc::DirectedGraph {
        nodes: vec![vec![]; nodes],
        explored: vec![false; nodes],
        time: vec![0; nodes],
        leader: vec![0; nodes],
    };

    let mut g_dash = ssc::DirectedGraph {
        nodes: vec![vec![]; nodes],
        explored: vec![false; nodes],
        time: vec![0; nodes],
        leader: vec![0; nodes],
    };

    let file = File::open(path)?;
    let br = BufReader::new(file);

    for line in br.lines() {
        let line = line?;
        let mut nodes = line.split_whitespace();
        let first_node: usize = nodes.next().unwrap().parse().unwrap();
        let second_node: usize = nodes.next().unwrap().parse().unwrap();

        g.nodes[first_node - 1].push(second_node - 1);
        g_dash.nodes[second_node - 1].push(first_node - 1);
    }

    Ok((g, g_dash))
}

pub fn read_to_directed_weighted_graph(
    path: &str,
) -> Result<crate::dijkstra::DirectedWeightedGraph, io::Error> {
    let nodes: usize = get_node_size(path).unwrap();
    let mut g = crate::dijkstra::DirectedWeightedGraph {
        nodes: vec![vec![]; nodes],
        distance: vec![1000000; nodes], // initial distance based on instructions
    };

    let file = File::open(path)?;
    let br = BufReader::new(file);

    for line in br.lines() {
        let line = line?;
        let mut nodes = line.split_whitespace();
        let current_node: usize = nodes.next().unwrap().parse().unwrap();

        for edge in nodes {
            let mut it = edge.split(",");
            let target_node: usize = it.next().unwrap().parse().unwrap();
            let distance: usize = it.next().unwrap().parse().unwrap();
            g.nodes[current_node - 1].push((target_node - 1, distance))
        }
    }
    Ok(g)
}

pub fn read_jobs(path: &str) -> Result<jobs::Jobs, io::Error> {
    let mut jobs = Vec::new();
    let file = File::open(path)?;
    let br = BufReader::new(file);
    let mut lines = br.lines();
    lines.next();
    for line in lines {
        let line = line?;
        let mut nodes = line.split_whitespace();
        let weight = nodes.next().unwrap().parse().unwrap();
        let length = nodes.next().unwrap().parse().unwrap();
        let job = jobs::Job {
            weight: weight,
            length: length,
        };
        jobs.push(job);
    }

    Ok(jobs)
}

pub fn read_weighted_graph_prims(path: &str) -> Result<prims::WeightedGraph, io::Error> {
    let mut g = Vec::new();
    let file = File::open(path)?;
    let br = BufReader::new(file);
    let mut lines = br.lines();
    let line = lines.next().unwrap().unwrap();
    let mut fields = line.split_whitespace();
    let n_nodes: usize = fields.next().unwrap().parse().unwrap();
    // let n_edges: usize = fields.next().unwrap().parse().unwrap();

    for i in 0..n_nodes {
        let node = prims::Vertex {
            id: i,
            edges: vec![],
        };
        g.push(node);
    }

    for line in lines {
        let line = line?;
        let mut nodes = line.split_whitespace();
        let id_a: usize = nodes.next().unwrap().parse::<usize>().unwrap() - 1;
        let id_b: usize = nodes.next().unwrap().parse::<usize>().unwrap() - 1;
        let weight = nodes.next().unwrap().parse().unwrap();

        let edge = prims::Edge {
            weight: weight,
            source_id: id_a,
            target_id: id_b,
        };
        g[id_a].edges.push(edge);

        let edge = crate::prims::Edge {
            weight: weight,
            source_id: id_b,
            target_id: id_a,
        };
        g[id_b].edges.push(edge);
    }

    Ok(g)
}

pub fn read_weighted_graph_clustering(
    path: &str,
) -> Result<k_clustering::WeightedGraph, io::Error> {
    let file = File::open(path)?;
    let mut lines = BufReader::new(file).lines();
    let line = lines.next().unwrap().unwrap();
    let n_nodes: usize = line.parse().unwrap();

    let mut g = k_clustering::WeightedGraph {
        n_nodes: n_nodes,
        edges: Vec::new(),
    };

    for line in lines {
        let line = line?;
        let mut nodes = line.split_whitespace();
        let id_a: usize = nodes.next().unwrap().parse::<usize>().unwrap() - 1;
        let id_b: usize = nodes.next().unwrap().parse::<usize>().unwrap() - 1;
        let weight = nodes.next().unwrap().parse().unwrap();

        let edge = k_clustering::Edge {
            weight: weight,
            source_id: id_a,
            target_id: id_b,
        };
        g.edges.push(edge)
    }

    Ok(g)
}

pub fn read_k_cluster_big(path: &str) -> Result<k_clustering_big::ImpliciteGraph, io::Error> {
    let file = File::open(path)?;
    let mut lines = BufReader::new(file).lines();
    lines.next();
    let mut g = Vec::new();

    for line in lines {
        let mut s = line?;
        s.retain(|c| !c.is_whitespace());
        let u = u32::from_str_radix(&s, 2).unwrap();
        g.push(u);
    }

    Ok(g)
}
