package graph;

import graph.Vertice;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;

public class TermMatrix
{	
	private class Document
	{
		public String Name;
		public String FullName;
		public int Id;
		
		public Map<String, Integer> Terms = null;
		
		public Document(String fullName, int vertId)
		{
			Id = vertId;
			FullName = fullName;
			String[] nameSplit = fullName.split("\\.");
			Name = nameSplit[nameSplit.length - 1];
			
			Terms = new HashMap<String, Integer>();
		}
		
		public void IncrementTerm(String termName)
		{
			if(!Terms.containsKey(termName))
			{
				Terms.put(termName, 1);
			}
			else
			{
				Terms.put(termName, Terms.get(termName)  + 1);
			}
		}
	}
	
	private class Term
	{
		public String Name;
		Map<String, Document> AppearsIn;
		
		public Term(String name)
		{
			Name = name;
			AppearsIn = new HashMap<String, Document>(); 
		}
		
		public void addDocument(Document d)
		{
			AppearsIn.put(d.FullName, d);
		}
	}
	
	Map<String, Document> _documents = null;
	Map<String, Term> _terms = null;
	
	private FastMap _fastMap = null;
	
	public TermMatrix()
	{
		_documents = new HashMap<String,Document>();
		_terms = new HashMap<String, Term>();
		
	}
	
	
	
	
	public void printTF_IDF_Arff()
	{
		
		
	}
	public void printTF_IDF()
	{
		BufferedWriter out;
		
		try
		{
			out = new BufferedWriter(new FileWriter("termMatrix.txt"));
			
			out.write("'documentName'");
			for(Term t:_terms.values())
			{
				out.write(", " + t.Name);
			}
			out.write("\n");
			
			for(Document d:_documents.values())
			{
				out.write(d.Name);
				
				for(Term t:_terms.values())
				{
					Integer tf = d.Terms.get(t.Name);
					double idf = Math.log(_documents.size()/_terms.get(t.Name).AppearsIn.size());

					if(tf == null)
					{
						tf = 0;
					}
					
					out.write(", " + (double)tf*idf);
				}
				
				out.write("\n");
			}
			out.close();
		}
		catch(IOException ex)
		{
			System.out.println("Error writing termMatrix text file");
		}
	}
	
	public void printTF()
	{
		BufferedWriter out;
		
		try
		{
			out = new BufferedWriter(new FileWriter("termMatrix.txt"));
			
			out.write("'documentName'");
			for(Term t:_terms.values())
			{
				out.write(", " + t.Name);
			}
			out.write("\n");
			
			for(Document d:_documents.values())
			{
				out.write(d.Name);
				
				for(Term t:_terms.values())
				{
					Integer termCount = d.Terms.get(t.Name);
					out.write(", " + termCount);
				}
				
				out.write("\n");
			}
			out.close();
		}
		catch(IOException ex)
		{
			System.out.println("Error writing termMatrix text file");
		}
	}
	
	public void runFastMap()
	{
		ArrayList<ArrayList<Double>> fmData = new ArrayList<ArrayList<Double>>();
		ArrayList<String> lookup = new ArrayList<String>();
		
		int i = 0;
		for(Document d:_documents.values())
		{
			ArrayList<Double> row = new ArrayList<Double>();
			for(Term t:_terms.values())
			{
				Integer tf = d.Terms.get(t.Name);
				double idf = Math.log(_documents.size()/_terms.get(t.Name).AppearsIn.size());

				if(tf == null)
				{
					tf = 0;
				}
				
				row.add((double)tf*idf);
			}
			
			fmData.add(row);
			lookup.add(i++, d.Name);
		}
		
		_fastMap = new FastMap(fmData, 3, lookup);
		_fastMap.doMap();
		_fastMap.printData("step_tfidf_fastmap.arff");
	}
	
	public ArrayList<String> doQuery(Vertice v)
	{
		ArrayList<String> terms = getTermsFromName(v);
		ArrayList<Double> instance = new ArrayList<Double>();
		
		for(Term t:_terms.values())
		{
			int tf = 0;
			for(String tmp:terms) { if(tmp.equalsIgnoreCase(t.Name)) tf++; }
			double idf = Math.log(_documents.size()/_terms.get(t.Name).AppearsIn.size());			
			instance.add((double)tf*idf);
		}
		
		System.out.println(instance);
		ArrayList<Double> mappedV =_fastMap.MapNewInstance(instance);
		System.out.println(mappedV);
		
		return _fastMap.getNClosest(10, mappedV);	
	}
	
	
	public void incrementDocument(String docName, int vertId, String term)
	{
		Document d;
		
		term = term.toLowerCase();
		
		if(!_documents.containsKey(docName))
		{
			d = new Document(docName, vertId);
			d.IncrementTerm(term);
			_documents.put(docName, d);
		}
		else
		{
			d = _documents.get(docName);
			d.IncrementTerm(term);
		}
		
		if(!_terms.containsKey(term))
		{
			Term t = new Term(term);
			t.addDocument(d);
			_terms.put(term, t);
		}
		else
		{
			_terms.get(term).addDocument(d);
		}
	}
	
	public void processVertice(Vertice doc, Vertice toVert)
	{
		ArrayList<String> newTerms = getTermsFromName(toVert);

		for(String newTerm:newTerms)
		{
			incrementDocument(doc.getName(), doc.getId(), newTerm);
		}
	}
	
	private ArrayList<String> getTermsFromName(Vertice me)
	{
		//split up method name into terms
		String name = me.getName();
		name = name.replace("_", ".");
		name = name.replace(";", "");
		name = name.replace("(", ".");
		name = name.replace(")", ".");
		name = name.replace("[", ".");
		
		String[] candidateTerms = name.split("\\.");
		ArrayList<String> finalTerms = new ArrayList<String>();
		
		//Loop through each string
		for(String c:candidateTerms)
		{
			//Check for camel case terms and add those to the list
			// (ex. "thisSentenceIsCC" would be added as the terms
			//   {this, Sentence, Is, C, C} )
			int offset = 1, termStart = 0;
			for(int i = 1; i < c.length(); i++)
			{
				if(i == c.length() -1)
				{
					finalTerms.add(c.substring(termStart, termStart + offset + 1));
					continue;
				}
				
				if(Character.isUpperCase(c.charAt(i)))
				{
					finalTerms.add(c.substring(termStart, termStart + offset));
					offset = 0;
					termStart = i;
				}
				
				offset++;
			}
		}
		
		return finalTerms;
		
		/*for(String fTerm:finalTerms)
		{
			_termMatrix.incrementDocument(me.getName(), fTerm);
		}*/
	}
}
