package iitb.cfilt.cpost.stemmer;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Vector;
import java.sql.Timestamp;

import iitb.cfilt.cpost.ConfigReader;
import iitb.cfilt.cpost.lexicon.WordProperties;
import iitb.cfilt.cpost.lexicon.Wordlist;
import iitb.cfilt.cpost.utils.UTFConsole;


public class Stemmer {
	//private Wordlist wordlist;
	//private StemmerRuleReader stemmerRuleReader;

	//Flags
	static boolean mmAtStemmer = true;
	static boolean useStemDictionary = false;
	static{
		//System.out.println("Stemmer.mmAtStemmer : " + mmAtStemmer);
		//System.out.println("Stemmer.useStemDictionary: " + useStemDictionary);
	}

	public static HashMap<String, StemmedToken> stemDictionary;

	
	@SuppressWarnings("unchecked")
	public Stemmer(){
		//wordlist = new Wordlist();
		boolean fileFound = true;
		try {
			ObjectInput oin = new ObjectInputStream(new FileInputStream(ConfigReader.get("Stemmer.stemDictionary")));
			stemDictionary = (HashMap<String, StemmedToken>) oin.readObject();
		} catch (FileNotFoundException e) {
//			e.printStackTrace();
			fileFound = false;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		if(!fileFound || !useStemDictionary){
			stemDictionary = new HashMap<String, StemmedToken>();
		}
		Wordlist.populate();
		StemmerRuleReader.populate();
	}

	public Stemmer(String lang, String config)
	{
		//System.out.println("Path : ");
		//System.out.println(new File("./").getAbsolutePath());
		//System.out.println("********Before Config*********");
		ConfigReader.read(config);
		//System.out.println("********After Config*********");
		Wordlist.populate();
		//System.out.println("********After Wordlist*********");
		StemmerRuleReader.populate();
		//System.out.println("********After SRR*********");
	}
	
	static void writeStemDictionary(){
		try {
			ObjectOutput oout = new ObjectOutputStream(new FileOutputStream(ConfigReader.get("Stemmer.stemDictionary")));
			oout.writeObject(stemDictionary);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public Vector<StemmedToken> stem (Vector<String> tokens){
		Vector<StemmedToken> retVal = new Vector<StemmedToken>(tokens.size());
		for(int i = 0; i < tokens.size(); i++){
			StemmedToken currentStemmedToken = stem(tokens.get(i));
			if(currentStemmedToken != null){
				retVal.add(currentStemmedToken);
			}
		}
		if(retVal.size() == 0){
			retVal = null;
		}
		if(useStemDictionary){
			//writeStemDictionary();
		}
		return retVal;
	}

	public StemmedToken stem(String token){
		
		String temp_token = token;
		boolean flag = false;
//		Logger.println("Stemming : " + token);
		if(token.contains("-"))
		{
			int p = token.indexOf('-');
			token = token.substring(0,p);
			flag = true;
		}
		StemmedToken retVal = null;
		//if(false && stemDictionary.containsKey(token)){
		//	retVal = stemDictionary.get(token);
		//}
		//else{
			retVal = new StemmedToken(token);
			StemmerRuleResult specialCharacterResult = StemmerRuleReader.applySpecialCharacterRulesOn(token);
			if(specialCharacterResult != null){
				retVal.addStemmerOutput(specialCharacterResult);
			}

			Vector<StemmerRuleResult> lexItems = checkInLexicon(token);
			if(lexItems != null){
				retVal.addAllStemmerOutputs(lexItems);
			}


			Vector<StemmerRuleResult> suffixStrippedResults;

			suffixStrippedResults = checkWithSuffix(token); 
			if(suffixStrippedResults!=null){
				retVal.addAllStemmerOutputs(suffixStrippedResults);
			}

			if(retVal.getStemmedOutputs().size() == 0){
				Vector<String> spellingVariationsVector = StemmerRuleReader.getSpellingVariations(token);
				if(spellingVariationsVector != null){
					for(int i = 0; i < spellingVariationsVector.size(); i++){
						String currentSpellingVariation = spellingVariationsVector.get(i);
						lexItems = checkInLexicon(currentSpellingVariation);
						if(lexItems != null){
							retVal.addAllStemmerOutputs(lexItems);
						}
						suffixStrippedResults = checkWithSuffix(currentSpellingVariation);
						if(suffixStrippedResults!=null){
							retVal.addAllStemmerOutputs(suffixStrippedResults);
						}
					}
				}
			}

			if(retVal.getStemmedOutputs().size() == 0){
				//If still nothing found, add 'Unknown'
				StemmerRuleResult srresult = new StemmerRuleResult(token, "unknown", "unknown", "", "");
				retVal.addStemmerOutput(srresult);
				//Vector<StemmerRuleResult> srresult = StemmerRuleReader.checkForNounSuffixes(token);
				//retVal.addAllStemmerOutputs(srresult);
			}
			else{
				// Code to perform Maximum Matching
//				boolean mmAtStemmer = false;
//				if(mmAtStemmer){
//				Vector<StemmerRuleResult> retValOutputs = retVal.getStemmedOutputs();
//				int leastSuffixLength = retValOutputs.get(0).getSuffixSize();
//				int currentSize;
//				HashSet<StemmerRuleResult> srrWithMaxMatching = new HashSet<StemmerRuleResult>();

//				for(int i = 0; i < retValOutputs.size(); i++){
//				currentSize = retValOutputs.get(i).getSuffixSize();
//				if(leastSuffixLength > currentSize){
//				leastSuffixLength = currentSize;
//				}
//				}
//				for(int i = 0; i < retValOutputs.size(); i++){
//				currentSize = retValOutputs.get(i).getSuffixSize();
//				if(currentSize == leastSuffixLength){
//				srrWithMaxMatching.add(retValOutputs.get(i));
//				}
//				}
//				retVal.clearOutputs();
//				retValOutputs.clear();
//				retValOutputs.addAll(srrWithMaxMatching);
//				retVal.addAllStemmerOutputs(retValOutputs);
//				}
			}
//			for(StemmerRuleResult srresult : retVal.getStemmedOutputs()){
//			Logger.println("\t" + srresult.toString());
//			}
			retVal.sortResults();

			//stemDictionary.put(token, retVal);
		//}
		
		//******************************************************
		
		/*Iterator<StemmerRuleResult> iterRetval = retVal.getStemmedOutputs().iterator();
		while(iterRetval.hasNext()){
			StemmerRuleResult temp = (StemmerRuleResult)iterRetval.next();
			temp.suffixList.add(temp.ultimateDeletion);
		}
		*/
		
		
		//******************************************************
			
		if(flag == true)
		{
			retVal.setToken(temp_token);
		}
		
		return retVal;
	}
	
	

	private Vector<StemmerRuleResult> checkWithSuffix(String token) {
		Vector<StemmerRuleResult> retVal = new Vector<StemmerRuleResult>();
		int maxSuffixLength = 0;
		String maxSuffix = "";
		String maxParadigm = "";
		String maxCategory = "";
		String maxRoot = "";
		LinkedList<String> maxSuffixes = null;
		Vector<StemmerRuleResult> suffixReplacedTokenVector = StemmerRuleReader.applySuffixReplacementRulesOn(token);
		if(suffixReplacedTokenVector != null){
			for(int i = 0; i < suffixReplacedTokenVector.size(); i++){
				StemmerRuleResult currentResult = suffixReplacedTokenVector.get(i);
				
				
				boolean flag = StemmerRuleReader.checkForSuffixPriority(token,currentResult);
				if(flag == false)
					continue;
				
				
				String root = currentResult.getRoot();
				String paradigm = currentResult.getParadigm();
				
				String ud = currentResult.getUltimateDeletion();
				
				LinkedList<String> suffixes = currentResult.getSuffixList();

				StemmerRuleResult srresult = null;
//				System.out.println("Root : " + root + " Paradigm : " + paradigm + " Category : " + currentResult.getCategory());
//				System.out.println("Searching " + root + " in lexicon for paradigm " + paradigm);

				WordProperties wp = Wordlist.searchWordlistFor(root, paradigm);
				if(wp != null) {
					srresult = new StemmerRuleResult(root, wp.getParadigm(), wp.getCategory(), ud, suffixes);
//					System.out.println("Found in Lexicon : " + root + " for token : " + token + " lexiconParadigm : " + wp.getParadigm() + " ruleParadigm : " + paradigm + " Category : " + wp.getCategory());
					if(!retVal.contains(srresult))
						retVal.add(srresult);
				}
//				Vector<WordProperties> wpv = wordlist.searchWordlistFor(root);
//				if(wpv != null){
//				for(int k = 0; k < wpv.size(); k++){
//				WordProperties wp = wpv.get(k);
//				srresult = new StemmerRuleResult(root, paradigm, wp.getCategory(), "", suffixes);
//				System.out.println("Found in Lexicon : " + root + " for token : " + token + " lexiconParadigm : " + wp.getParadigm() + " ruleParadigm : " + paradigm);
//				if(!retVal.contains(srresult)){
//				retVal.add(srresult);
//				}
//				}
//				}
				else {
					/*Hack begins*/
					if(paradigm.equals("number")){
						srresult = new StemmerRuleResult(root, "number", "number", ud, suffixes);
						if(!retVal.contains(srresult))
							retVal.add(srresult);
					}
					/*Hack ends*/
					if(StemmerRuleReader.isAuxiliary(root) && suffixes.size() < 2){
//						System.out.println(root + " is auxiliary");
						srresult = new StemmerRuleResult(root, "vaux", "verb_aux", ud, suffixes);
						if(!retVal.contains(srresult))
							retVal.add(srresult);
					}
					//if((paradigm.equals("verb") || paradigm.equals("aaverb") || paradigm.equals("aaverbAux")) && (StemmerRuleReader.hasRegularRootForm(root)||StemmerRuleReader.hasRegularRootForm(token))){
					//if(paradigm.equals("verb") && (StemmerRuleReader.hasRegularRootForm(root)||StemmerRuleReader.hasRegularRootForm(token))){
					if(StemmerRuleReader.hasRegularRootForm(root)||StemmerRuleReader.hasRegularRootForm(token)){
						
						String regularRootForm=null;
						String new_paradigm = "";
						String new_category = "";
						Vector<Vector<String>> results = StemmerRuleReader.getRegularRootForm(root);
						for(int z=0;z<results.size();z++)
						{
							regularRootForm = results.get(z).get(1);
							new_paradigm = results.get(z).get(2);
							new_category = results.get(z).get(3);
							
							srresult = new StemmerRuleResult(regularRootForm, new_paradigm, new_category, ud, suffixes);
							if(!retVal.contains(srresult))
								retVal.add(srresult);
						}
						
						
						
						
						/*String regularRootForm=null;
						String new_paradigm = paradigm;
						if(StemmerRuleReader.hasRegularRootForm(root))
						{
							String[] tempstr = StemmerRuleReader.getRegularRootForm(root);
							regularRootForm = tempstr[0];
							new_paradigm = tempstr[1];
						}
						else
							if (StemmerRuleReader.hasRegularRootForm(token))
							{
								String[] tempstr = StemmerRuleReader.getRegularRootForm(token);
								regularRootForm = tempstr[0];
								new_paradigm = tempstr[1];
							}
//						System.out.println("The regular root for " + token + " is " + regularRootForm);
						//if(paradigm.endsWith("ux"))
							//srresult = new StemmerRuleResult(regularRootForm, paradigm, "verb_aux", ud, suffixes);
						//else
							srresult = new StemmerRuleResult(regularRootForm, new_paradigm, "verb", ud, suffixes);
							
							
						if(!retVal.contains(srresult))
							retVal.add(srresult);
							
						Vector<WordProperties> tempwp = Wordlist.searchWordlistFor(regularRootForm);
						for(int z=0;z<tempwp.size();z++)
						{
							if(tempwp.get(z).getCategory().trim().equals("verb_aux"))
							{
								srresult = new StemmerRuleResult(regularRootForm, paradigm, "verb_aux", ud, suffixes);
								if(!retVal.contains(srresult))
									retVal.add(srresult);
								break;
							}
						}
							
						*/	
						
//						System.out.println("Inside checkWithSuffix : Added :: " + srresult.toString());

						/*if(StemmerRuleReader.isAuxiliary(regularRootForm)){
							srresult = new StemmerRuleResult(regularRootForm, "vaux", "verb_aux", ud, suffixes);
							if(!retVal.contains(srresult))
								retVal.add(srresult);
						}*/
						if(StemmerRuleReader.hasUniqueSuffix(suffixes)){
							String uniqueSuffix = StemmerRuleReader.getUniqueSuffix(suffixes);
							if(uniqueSuffix.length() > maxSuffixLength){
								maxSuffix = uniqueSuffix;
								maxSuffixes = suffixes;
								maxSuffixLength = maxSuffix.length();
								maxParadigm = paradigm;
								maxCategory = Wordlist.getCategory(paradigm);
								maxRoot = regularRootForm;
							}
						}
					}
				}
			}
		}
		if(retVal.size() == 0 && maxSuffixLength>0){
			StemmerRuleResult srresult = new StemmerRuleResult(maxRoot, maxParadigm, maxCategory, "", maxSuffixes);
			if(!retVal.contains(srresult)){
				retVal.add(srresult);
			}
		}
		if(retVal.size() == 0){
			retVal = null;
		}
		else{
			// Code to perform Maximum Matching --- Code moved to stem() function

			if(mmAtStemmer){


				int leastSuffixLength = retVal.get(0).getSuffixSize();
				int currentSize;
				HashSet<StemmerRuleResult> srrWithMaxMatching = new HashSet<StemmerRuleResult>();

				for(int i = 0; i < retVal.size(); i++){
					currentSize = retVal.get(i).getSuffixSize();
//					UTFConsole.out.println("Root: " + retVal.get(i).getRoot() + " suffixSize : " + currentSize);
					if(leastSuffixLength > currentSize){
						leastSuffixLength = currentSize;

					}
				}
				for(int i = 0; i < retVal.size(); i++){
					currentSize = retVal.get(i).getSuffixSize();
					if(currentSize == leastSuffixLength){
						srrWithMaxMatching.add(retVal.get(i));
					}
				}
				retVal.clear();
				retVal.addAll(srrWithMaxMatching);
			}
		}
		return retVal;
	}

	private Vector<StemmerRuleResult> checkInLexicon(String token) {
		Vector<StemmerRuleResult> retVal = new Vector<StemmerRuleResult>();
		Vector<WordProperties> tokenProperties = Wordlist.searchWordlistFor(token);
		if(tokenProperties != null){
			for(int i = 0; i < tokenProperties.size(); i++){
				WordProperties tokenProperty = tokenProperties.get(i);
//				System.out.println("Found in Lexicon (fullsearch): " + token + " lexiconParadigm : " + tokenProperty.getParadigm() + " Category : " + tokenProperty.getCategory());
				StemmerRuleResult srresult = new StemmerRuleResult(token, tokenProperty.getParadigm(), tokenProperty.getCategory(),"", "");
				if(!retVal.contains(srresult))
					retVal.add(srresult);
			}
		}
		if(StemmerRuleReader.isAuxiliary(token)){
			StemmerRuleResult srresult = new StemmerRuleResult(token, "vaux", "verb_aux", "", "");
			if(!retVal.contains(srresult))
				retVal.add(srresult);
		}
		if(retVal.size() == 0){
			Vector<String[]> derivationalMorphologyOutput = StemmerRuleReader.checkDerivationalMorphology(token);
			if(derivationalMorphologyOutput != null){
				for(int i = 0; i < derivationalMorphologyOutput.size(); i++){
					String[] properties = derivationalMorphologyOutput.get(i);
					String derivedRoot = properties[0];
					String derivedRootCategory = properties[1];
					String derivedNewCategory = properties[2];

					Vector<WordProperties> derivedRootWordProperties = Wordlist.searchWordlistFor(derivedRoot);
					if(derivedRootWordProperties != null){
						for(int j = 0; j < derivedRootWordProperties.size(); j++){
							WordProperties dwp = derivedRootWordProperties.get(j);
							if(dwp.getCategory().equals(derivedRootCategory)){
								StemmerRuleResult srresult = new StemmerRuleResult(derivedRoot, "", derivedNewCategory,"","");
								if(!retVal.contains(srresult))
									retVal.add(srresult);
							}
						}
					}
				}
			}

		}
		if(retVal.size() == 0){
			retVal = null;
		}
		return retVal;
	}

	public static void main(String args[]){
		
		ConfigReader.read(args[0]);
		System.out.println(new Timestamp(System.currentTimeMillis()).toString());
		Stemmer stemmer = new Stemmer();
		System.out.println(new Timestamp(System.currentTimeMillis()).toString());
		StemmedToken stemmedToken = stemmer.stem("आया");
		UTFConsole.out.println(stemmedToken.getStemmedOutputs().toString()); //.get(0).getRoot());
		System.out.println(new Timestamp(System.currentTimeMillis()).toString());
		stemmedToken = stemmer.stem("हुआ");
		UTFConsole.out.println(stemmedToken.getStemmedOutputs().toString()); //.get(0).getRoot());
		System.out.println(new Timestamp(System.currentTimeMillis()).toString());
		//stemmedToken = stemmer.stem("म्हणाला");
		//UTFConsole.out.println(stemmedToken.getStemmedOutputs().get(0).getRoot());
		//System.out.println(new Timestamp(System.currentTimeMillis()).toString());
		System.out.println("Done!");
	}
}
