package graph;

import graph.Vertice;
import graph.Edge;
import graph.TermMatrix;

import java.io.*;
import java.util.*;

public class Graph 
{
	private static Graph _instance = null;
  
	public static Graph getInstance() {
      if(_instance == null) {
         _instance = new Graph();
      }
      return _instance;
   }
	   
	Map<Integer, Vertice> _vertMap = null;
	ArrayList<Edge> _edges = null;
	
	TermMatrix _termMatrix = null;
	
	private int _hops = 1;
	
	//What type of verticie will be considered a document
	private int _documentType = 0;
	
	public Graph(ArrayList<Edge> edges, ArrayList<Vertice> verts)
	{
		_termMatrix = new TermMatrix();
		
		_edges = edges;
		//_vertList = verts;
		_vertMap = new HashMap<Integer, Vertice>();
		for(Vertice v:verts)
		{
			_vertMap.put(v.getId(), v);
		}
	}
	
	public Graph()
	{
		_termMatrix = new TermMatrix();
		_vertMap = new HashMap<Integer, Vertice>();
		_edges = new ArrayList<Edge>();
		//_vertList = new ArrayList<Verticie>();
	}
	
	public void AddVerticie(Vertice v)
	{
		if(!_vertMap.containsKey(v.getName()))
		{
			v.setId(_vertMap.keySet().size());
			//_vertList.add(v);
			_vertMap.put(v.getId(), v);
		}
	}
	
	public void AddEdge(Edge e)
	{
		if(!_edges.contains(e))
		{
			e.setId(_edges.size());
			_edges.add(e);
			try
			{
				((Vertice)_vertMap.get(e.getFromVert().getId())).addEdge(e);//_verticeMap
			}
			catch(NullPointerException ex)
			{
				_vertMap.put(e.getFromVert().getId(), e.getFromVert());
			}
		}
	}
	
	public boolean load(String vertFile, String edgeFile)
	{
		System.out.println("Loading verts - " + System.currentTimeMillis());
		boolean ret = loadVerts(vertFile);
		
		
		System.out.println("Loading edges - " + System.currentTimeMillis());
		ret = ret && loadEdges(edgeFile);
		
		System.out.println("DONE (loading edges) - " + System.currentTimeMillis());
		
		return ret;
	}
	
	public void buildTermMatrix()
	{
		for(Vertice v:_vertMap.values())
		{
			if(v.getType() == _documentType  || _documentType == 0)
			{
				switch(_documentType)
				{
					//treat every verticie as a document
					case 0:
						buildTM_all(v);
						break;
				}
			}
		}
				
		_termMatrix.runFastMap();
		
		_termMatrix.printTF_IDF();
	}
	
	public void enterQueryLoop()
	{
		while(true)
		{
			System.out.println("Enter a query: ");
			Scanner sc = new Scanner(System.in);
			while(!sc.hasNextLine()){}
			String tmp = sc.nextLine().replace(" ", ".");
			Vertice v = new Vertice();
			v.setName(tmp);
			
			System.out.println(_termMatrix.doQuery(v));
			
		}
	}
	
	private void buildTM_all(Vertice me)
	{
		ArrayList<Vertice> vertsProcessed = new ArrayList<Vertice>();
		
		ArrayList<Vertice> verts = new ArrayList<Vertice>();
		verts.add(me);
		

		//First inspect the root vertice
		_termMatrix.processVertice(me, me);
		vertsProcessed.add(me);
	
		for(int i = 0; i < _hops; i++)
		{
			ArrayList<Vertice> newVerts = new ArrayList<Vertice>();
			for(Vertice v:verts)
			{	
				//Now all of its connected vertices
				ArrayList<Edge> edges = v.getEdges();
				for(Edge e:edges)
				{
					Vertice toVert = e.getToVert();
					_termMatrix.processVertice(me, toVert);
					vertsProcessed.add(toVert);
					newVerts.add(toVert);
				}
			}
			
			//clear out the last list
			verts.clear();
			
			//copy new verts into verts if that vertice hasn't been processed yet
			for(Vertice v:newVerts)
			{
				if(!vertsProcessed.contains(v))
				{
					verts.add(v);
				}
			}
			newVerts.clear();
		}
	}
	
	private boolean loadVerts(String file)
	{
		//First load verts
		Scanner sc;
		try
		{
			sc = new Scanner(new File(file));
		}
		catch(FileNotFoundException ex)
		{
			System.out.println("loadVerts: failure to open file. \n " + 
					"ex.getMessages = " + ex.getMessage());
			return false;
		}
		
		while(sc.hasNextLine())
		{
			String line = sc.nextLine();
			if(!line.startsWith("#"))
			{
				String[] vals = line.split(",");
				if(vals.length != 4)
				{
					System.out.println("loadVerts: invalid file format");
					return false;
				}
				
				Vertice v = new Vertice();
				v.setId(Integer.parseInt(vals[2].trim()));
				v.setType(Integer.parseInt(vals[1].trim()));
				v.setName(vals[3].trim());
				if(!_vertMap.containsKey(v.getId()))
				{
					_vertMap.put(v.getId(), v);
				}
			}
		}
		
		//try
		//{
			sc.close();
			System.out.println("finished verts");
		//}
		//catch(FileNotFoundException ex)
		//{
		//	return false;
		//}
		
		return true;
	}
	
	private boolean loadEdges(String file)
	{
		Scanner sc;
		try
		{
			sc = new Scanner(new File(file));
		}
		catch(FileNotFoundException ex)
		{
			return false;
		}
		
		while(sc.hasNextLine())
		{
			String line = sc.nextLine();
			if(!line.startsWith("#"))
			{
				String[] vals = line.split(",");
				if(vals.length != 4)
				{
					return false;
				}
				
				Edge e = new Edge();
				e.setId(Long.parseLong(vals[1].trim() + vals[2].trim() + vals[3].trim()));
				e.setType(Integer.parseInt(vals[1].trim()));
				Vertice fromVert =(Vertice)_vertMap.get(Integer.parseInt(vals[2].trim()));
				
				if(fromVert == null)
				{
					System.out.println("Invalid vertice id: " + vals[2]);
				}
				else
				{
					fromVert.addEdge(e);
					e.setFromVert(fromVert);
					e.setToVert((Vertice)_vertMap.get(Integer.parseInt(vals[3].trim())));
					AddEdge(e);
				}
			}
		}
		
		System.out.println("Finished edges");
		sc.close();
		return true;
	}
	
	
	public ArrayList<Edge> GetEdges()
	{
		return _edges;
	}
	
	public Collection<Vertice> GetVerticies()
	{
		return _vertMap.values();
	}
	
	public Vertice GetVertice(String name)
	{
		return _vertMap.get(name);
	}
	
	public void printVerts()
	{
		BufferedWriter out;
		
		try
		{
			out = new BufferedWriter(new FileWriter("verts.txt"));
			
			out.write("#'v', vertType, id, name, sourceFileName, sourceStartLine, sourceLineCount \n");
			
			for(Vertice v:(Collection<Vertice>)_vertMap.values())
			{
				out.write("v, " + v.getType() + "," + v.getId() + "," + 
						v.getName() + "," + v.getSourceFileName() + "," + 
						v.getSourceStartLine() + "," + v.getSourceLineCount() + "\n");
			}
			
			out.close();
		}
		catch(IOException ex)
		{
			System.out.println("Error writing verticie text file");
		}
	}
	
	public void printEdges()
	{
		BufferedWriter out;
		
		try
		{
			out = new BufferedWriter(new FileWriter("edges.txt"));
			
			out.write("# e, edgeType, vertId1, vertId2 \n");
			
			for(Edge e:_edges)
			{
				out.write( "e," + e.getType() + "," + e.getFromVert().getId() + "," + e.getToVert().getId() + "\n");
			}
			
			out.close();
		}
		catch(IOException ex)
		{
			System.out.println("Error writing edge text file");
		}
	}
	
	
}
