package myFlowmap;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
public class Graph 
{
	private ArrayList<String> vertices = new ArrayList<String>();
	private int[] edge_to;
	private int[] edge_from;
	private double[] edge_lengths;
	private boolean[] hasEdgeWeight;
	private boolean[] isFixedVertex;
	private boolean[] isSuppressed;
	private double[] vertx;
	private double[] verty;
	private int n_vertices;
	private int n_edges;
	private int max_edges,max_vertices;

	public Graph(int e,int v){
		this.max_edges=e;
		this.max_vertices=v;
		this.vertx = new double[v];
		this.verty= new double[v];
		this.isFixedVertex = new boolean[v];
		this.isSuppressed = new boolean[v];
		for(int i=0;i<this.max_vertices;i++)
		{
			this.isFixedVertex[i]=false;
			this.isSuppressed[i]=false;
		}
		this.edge_to = new int[e];
		this.edge_from = new int[e];
		this.edge_lengths = new double[e];
		this.hasEdgeWeight = new boolean[e];
		this.n_vertices=0;
		this.n_edges=0;
	}


	public void addVertex(String name){
		//Do we have room?
		if(this.n_vertices>=max_vertices) { System.out.println("No more vertices allowed"); throw new IllegalArgumentException("Too many vertices");}
		this.vertices.add(name);
		this.n_vertices++;
	}

	public void addFixedVertex(String name, double x, double y){
		//Do we have room?
		if(this.n_vertices>=max_vertices) { System.out.println("No more vertices allowed"); throw new IllegalArgumentException("Too many vertices");}
		this.vertices.add(name);
		this.vertx[n_vertices]=x;
		this.verty[n_vertices]=y;
		this.isFixedVertex[n_vertices]=true;
		this.n_vertices++;
	}


	public void addEdge(String name_orig, String name_targ)
	{
		//Do we have room?
		if(this.n_edges>=max_edges) { System.out.println("No more edges allowed"); throw new IllegalArgumentException("Too many edges");}


		//Validity checking
		boolean validOrig=false;
		boolean validTarg=false;
		int origIndex=0;
		int targIndex=0;
		for(int i=0;i<n_vertices;i++)
		{
			if(this.vertices.get(i).equals(name_orig)){ validOrig=true; origIndex=i;}
			if(this.vertices.get(i).equals(name_targ)){ validTarg=true; targIndex=i;}
		}
		if(!validOrig) { System.out.println("Invalid origin "+name_orig); throw new IllegalArgumentException("Invalid Origin");}
		if(!validTarg) { System.out.println("Invalid target "+name_targ); throw new IllegalArgumentException("Invalid Target");}

		this.edge_from[n_edges]=origIndex;
		this.edge_to[n_edges]=targIndex;
		this.hasEdgeWeight[n_edges]=false;
		this.n_edges++;
	}

	public void addEdge(String name_orig, String name_targ, double elength)
	{
		//Do we have room?
		if(this.n_edges>=max_edges) { System.out.println("No more edges allowed"); throw new IllegalArgumentException("Too many edges");}


		//Validity checking
		boolean validOrig=false;
		boolean validTarg=false;
		int origIndex=0;
		int targIndex=0;
		for(int i=0;i<n_vertices;i++)
		{
			if(this.vertices.get(i).equals(name_orig)){ validOrig=true; origIndex=i;}
			if(this.vertices.get(i).equals(name_targ)){ validTarg=true; targIndex=i;}
		}
		if(!validOrig) { System.out.println("Invalid origin "+name_orig); throw new IllegalArgumentException("Invalid Origin");}
		if(!validTarg) { System.out.println("Invalid target "+name_targ); throw new IllegalArgumentException("Invalid Target");}

		this.edge_from[n_edges]=origIndex;
		this.edge_to[n_edges]=targIndex;
		this.edge_lengths[n_edges]=elength;
		this.hasEdgeWeight[n_edges]=true;
		this.n_edges++;
	}

	public void printJSON()
	{
		System.out.println("{\n\t\"nodes\":[");
		for(int i=0;i<this.n_vertices;i++){

			if(this.isFixedVertex[i])
			{
				System.out.println("\t\t{\"name\":\""+this.vertices.get(i)+"\", \"x\":"+this.vertx[i]+", \"y\":"+this.verty[i]+", \"fixed\":true, \"type\":\"fixedPoint\"},");
			}
			else{
				System.out.println("\t\t{\"name\":\""+this.vertices.get(i)+"\", \"x\":0, \"y\":0, \"fixed\":false, \"type\":\"mobilePoint\"}"+(i<this.n_vertices-1?",":""));
			}
		}
		System.out.println("\t],");
		System.out.println("\t\"edges\": [");
		for(int j=0;j<this.n_edges;j++){
			System.out.println("\t{\"source\":"+this.edge_from[j]+", \"target\":"+this.edge_to[j]+", \"weight\":"+this.edge_lengths[j]+"}"+(j<this.n_edges-1?",":""));
		}
		System.out.println("]");
		System.out.println("}");
	}
	
	
	public void writeJSON() throws Exception
	{
		PrintWriter pw = new PrintWriter("graph.json"); pw.close(); //Clears any existing data in graph.json!!!
		PrintWriter graphOut = new PrintWriter(new BufferedWriter(new FileWriter("graph.json", true)));
		graphOut.println("{\n\t\"nodes\":[");
		for(int i=0;i<this.n_vertices;i++){

			if(this.isFixedVertex[i])
			{
				graphOut.println("\t\t{\"name\":\""+this.vertices.get(i)+"\", \"x\":"+this.vertx[i]+", \"y\":"+this.verty[i]+", \"fixed\":true, \"type\":\"fixedPoint\"},");
			}
			else{
				graphOut.println("\t\t{\"name\":\""+this.vertices.get(i)+"\", \"x\":0, \"y\":0, \"fixed\":false, \"type\":\"mobilePoint\"}"+(i<this.n_vertices-1?",":""));
			}
		}
		graphOut.println("\t],");
		graphOut.println("\t\"edges\": [");
		for(int j=0;j<this.n_edges;j++){
			graphOut.println("\t{\"source\":"+this.edge_from[j]+", \"target\":"+this.edge_to[j]+", \"weight\":"+this.edge_lengths[j]+"}"+(j<this.n_edges-1?",":""));
		}
		graphOut.println("]");
		graphOut.println("}");
		graphOut.close();
		System.out.println("Output written to graph.json");
	}
	


	public Graph reHang()
	{
		System.out.println("Identifying Source-root path");
		int currentChild=0;
		int currentParent=0;
		int suppressedVertices=0;
		System.out.println("\tParent of "+this.vertices.get(currentChild)+" is "+this.vertices.get(currentParent));
		while(currentParent!=this.max_vertices-1){
			//Find new parent
			for(int j=0;j<this.max_edges;j++){
				//System.out.println("Examining edge "+j+" from "+this.edge_from[j]+" to "+this.edge_to[j]);
				if(this.edge_from[j]==currentChild){
					currentParent=this.edge_to[j];
					break;
				}
			}
			System.out.println("\tParent of "+this.vertices.get(currentChild)+" is "+this.vertices.get(currentParent));
			this.isSuppressed[currentParent]=true;
			suppressedVertices++;
			currentChild=currentParent;	
		}
		Graph rootedMap= new Graph(this.max_edges,this.max_vertices-suppressedVertices);
		for(int i=0;i<this.n_vertices;i++){
			if(!this.isSuppressed[i]){
				if(this.isFixedVertex[i]) rootedMap.addFixedVertex(this.vertices.get(i),this.vertx[i],this.verty[i]);
				else rootedMap.addVertex(this.vertices.get(i));
			}
		}
		System.out.println("Rewiring");
		for(int j=0;j<this.n_edges;j++){

			if(!this.isSuppressed[this.edge_from[j]]){
				//edge from a vertex we've kept; does it go somewhere still valid?
				if(this.isSuppressed[this.edge_to[j]]){
					System.out.println("\tJoining "+this.vertices.get(this.edge_from[j])+" to "+this.vertices.get(0));
					rootedMap.addEdge(this.vertices.get(this.edge_from[j]),this.vertices.get(0),this.edge_lengths[j]);
				}
				else{
					rootedMap.addEdge(this.vertices.get(this.edge_from[j]),this.vertices.get(this.edge_to[j]),this.edge_lengths[j]);
				}
			}
		}
		return rootedMap;
	}
}