package iwocs.graphs;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Scanner;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.graphstream.algorithm.Toolkit;
import org.graphstream.graph.Graph;
import org.graphstream.graph.implementations.SingleGraph;
import org.graphstream.stream.file.FileSource;
import org.graphstream.stream.file.FileSourceEdge;

public class GraphIO {
  /**
   * Export degrees distribution of a graph to a given file.
   * 
   * @param g          the graph from which we get the degree distribution.
   * @param destFile   the path to the file in which the distribution will be
   *                   written.
   * @param normalized if the values of the distribution will be normalized or
   *                   not.
   */
  public static void exportDegreeDistribution(Graph g, String destFile, boolean normalized) {
    int[] degreesDistribution = Toolkit.degreeDistribution(g);
    double[] convertedDistribution = new double[degreesDistribution.length];

    for (int i = 0; i < degreesDistribution.length; i++)
      convertedDistribution[i] = normalized ? (double) degreesDistribution[i] / g.getNodeCount()
          : (double) degreesDistribution[i];

    try (BufferedWriter w = new BufferedWriter(new FileWriter(new File(destFile)))) {
      for (int i = 0; i < convertedDistribution.length; i++)
        if (convertedDistribution[i] != 0)
          w.write(String.format("%d\t%f\n", i, convertedDistribution[i]));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * Export the distribution in a simple x\ty file.
   * 
   * @param distribution the map to write in the file.
   * @param destFile     the path to the file in which we'll write the
   *                     distribution.
   */
  public static void exportHashMapDistribution(HashMap<Integer, Double> distribution, String destFile) {
    try (BufferedWriter w = new BufferedWriter(new FileWriter(new File(destFile)))) {
      for (Entry<?, ?> entry : distribution.entrySet())
        w.write(String.format("%d\t\t%f\n", entry.getKey(), entry.getValue()));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * Get the scientific collaboration graph from the resource of the project.
   * 
   * @return the graph read from the resource.
   */
  public static Graph getNerdsGraph() {
    Graph g = new SingleGraph("nerds");

    FileSource fs = new FileSourceEdge();

    fs.addSink(g);

    try {
      InputStream stream = GraphIO.class.getClassLoader().getResourceAsStream("nerds.txt");
      fs.readAll(stream);
    } catch (IOException e) {
      e.printStackTrace();
    }

    return g;
  }

  /**
   * Parses a distribution from a simple (x y) file.
   * 
   * @param file       path to the file containing the distribution.
   * @param isResource file is resource or not.
   * @return the distribution.
   * @throws FileNotFoundException if the file isn't found.
   */
  public static HashMap<Double, Double> getDistributionFromFile(String file, boolean isResource)
      throws FileNotFoundException {
    Scanner sc;

    if (isResource)
      sc = new Scanner(GraphIO.class.getClassLoader().getResourceAsStream(file));
    else
      sc = new Scanner(new File(file));

    HashMap<Double, Double> distribution = new HashMap<>();

    Pattern linePattern = Pattern.compile("^\\s*(\\d+(.\\d+)?)\\s+(\\d+(.\\d+)?)\\s*$");

    while (sc.hasNextLine()) {
      Matcher lineMatcher = linePattern.matcher(sc.nextLine());

      if (lineMatcher.matches())
        distribution.put(Double.parseDouble(lineMatcher.group(1)), Double.parseDouble(lineMatcher.group(3)));
    }

    sc.close();

    return distribution;
  }
}