package ben_seb_lab_E;

import com.swabunga.spell.event.SpellChecker;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import solns_lab_C.WordAnagram;

import wordPuzzler.lang.NoMoreTilesException;
import wordPuzzler.util.LetterDispenser;
import wordPuzzler.util.WordFactory;

/**
 * This prepares a strategy that emulates a game of Scrabble.
 * <br><br>
 * Created: solns_lab_E.WordArranger.java, 15.06.2008, 12:23:30
 */
public class WordScrambler {
	/**
	 * A reference to a SpellChecker.
	 */
	protected SpellChecker sChecker = null;

	/**
	 * A reference to the LetterDispenser.
	 */
	protected LetterDispenser ld = null;
	
	/**
	 * A reference to the WordAnagram.
	 */
	protected WordAnagram wAnag = null;
	
	
	/**
	 * The maximum number of tiles allowed per scramble round.
	 */
	protected int maxTiles = 7;	// <-- Default.
	
	

	// --[ Constructor(s) ]----------------------------------------------------
	/**
	 * Creates a default new instance of a WordScrambler.
	 */
	public WordScrambler() {
		this.sChecker = WordFactory.createSpellChecker();
		this.ld = new LetterDispenser();
		this.wAnag = new WordAnagram( this.sChecker );
	}
	
	/**
	 * Creates a new instance of a WordScrambler.
	 * @param maxTiles indicate the maximum number of tiles allowed per scramble round.
	 */
	public WordScrambler(int maxTiles) {
		this();
		this.maxTiles = maxTiles;
	}

		
	// --[ Domain specific(s) ]------------------------------------------------
	/**
	 * Returns true of the LetterDispenser still contains tiles to be dispensed.
	 */
	public boolean hasMoreTiles() { return ld.getNumTilesLeft() > 0; }
	
	/**
	 * Retrieves an n-letter word from the LetterDispenser.
	 * If there are fewer than n tiles left in the LetterDispenser, return
	 * all the tiles that are left.
	 * @return null if there are no more tiles left in the LetterDispenser.
	 */
	public String getScrambledWord(int n) {
		// TODO To be properly completed, according to the above specifications.
		try {
			char[] word = this.ld.dispense(n);
			return new String(word);
		}
		catch (NoMoreTilesException e) {
			System.out.println(e);
		}
		
		return null;
	}
	
	/**
	 * Given a word, determine the total point-value of the letters within.
	 */
	public int getPointValue(String word) {

		int wordValue = 0;
		char[] charWord = word.toCharArray();
		
		//Counting the value together
		for(int i=0; i<=charWord.length-1;i++){
			wordValue = wordValue + ld.getPointValue(charWord[i]);
		}
		
		return wordValue;
	}
	
	/**
	 * Sorts a given List of words in order of the point-value of each word in
	 * the List, so that the word with the highest value is at the top of the List.
	 * If several words with the same point-value exist, then these words must
	 * be arranged in alphabetical order.
	 */
	public List<String> sortWordsByPointValue(List<String> words) {
		//solns_lab_c (ez Mode)
		
		// Guard.
		if ( words.size() <= 1 ) return words;
		
		// Call the divisive-recursion method.
		int hi = words.size()-1;
		return mergeSortWordsbyPV(words, 0, hi);
	}

	//solns_lab_c (ez Mode)
	protected List<String> mergeSortWordsbyPV(List<String> words, int lo, int hi) {
		List<String> sWords = new ArrayList<String>(hi-lo+1);
		
		// Base case.
		if (lo == hi)	sWords.add( words.get(lo) );
		
		// Recursive case.
		else {
			// Divide.
			int m = (lo + hi + 1)/2;
			List<String> ltList = this.mergeSortWordsbyPV(words, lo, m-1);
			List<String> rtList = this.mergeSortWordsbyPV(words, m, hi);
			
			// Merge.
			Iterator<String> ltIter = ltList.iterator();
			Iterator<String> rtIter = rtList.iterator();
			String ltWord = ltIter.hasNext() ? ltIter.next() : null;
			String rtWord = rtIter.hasNext() ? rtIter.next() : null;			
			do {
				//Changed Code to PointValue
				if ( ltWord != null && (rtWord == null || getPointValue(ltWord) > getPointValue(rtWord)) ) {
					sWords.add( ltWord );
					ltWord = ltIter.hasNext() ? ltIter.next() : null;
				}
				//Changed Code to PointValue
				else if ( rtWord != null && (ltWord == null || getPointValue(ltWord) < getPointValue(rtWord)) ) {
					sWords.add( rtWord );
					rtWord = rtIter.hasNext() ? rtIter.next() : null;
				}
				//Sort alphabetically if value is equal
				else {
					if ( ltWord != null && (rtWord == null || ltWord.compareTo(rtWord) <= 0) ) {
						sWords.add( ltWord );
						ltWord = ltIter.hasNext() ? ltIter.next() : null;
					}
					if ( rtWord != null && (ltWord == null || rtWord.compareTo(ltWord) <= 0) ) {
						sWords.add( rtWord );
						rtWord = rtIter.hasNext() ? rtIter.next() : null;
					}
				}
			} while ( !(ltWord == null && rtWord == null) );
		}
		
		return sWords;
	}

	
	/**
	 * Given an n-letter refWord, generate all VALID words of shortest length 2 and
	 * longest length n. Return the generated words in order of their point-values;
	 * i.e. largest point-value on the top of the List.
	 * @return null if no valid words can be generated.
	 */
	public List<String> generateValidWords(String refWord) {
		List<String> returnList = new ArrayList<String>();
		
		//Permutate. Use solns_lab_C
		List<List<String>> wList = wAnag.findAllAnagrams(refWord);
;
		
		//from List of Lists of Strings => List of Strings
		for(int i=0; i < wList.size();i++){
			List<String> tmpList = wList.get(i);
			for (int j = 0; j < tmpList.size(); j++) {
				returnList.add(tmpList.get(j));
			}
		}
		
		//Sort by Value. Use Ex. 3
		returnList = sortWordsByPointValue(returnList);
		
		return returnList;
	}
	
	/**
	 * Given a validWord and a suggested letter, find all DIRECT anagrams of 
	 * the combined validWord and the suggested letter.
	 * @return null if no such anagrams exist.
	 */
	public List<String> findDirectAnagrams(String validWord, char letter) {
		List<String> returnList = new ArrayList<String>();
		
		//Permutate. Use solns_lab_C
		List<List<String>> wList = wAnag.findAllAnagrams(validWord+letter);
		
		//from List of Lists of Strings => List of Strings
		for(int i=0; i < wList.size();i++){
			List<String> tmpList = wList.get(i);
			for (int j = 0; j < tmpList.size(); j++) {
				//most ridiculous if-statement ever! (adding only permutations with length of word + letter)
				if (tmpList.get(j).length() == (validWord+letter).length()) {
					returnList.add(tmpList.get(j));
				}
			}
		}
		return returnList;
	}
	
	/**
	 * Given a validWord, suggest one letter that can be added into any position 
	 * within the validWord, and still make the new word (or its resultant 
	 * anagrams) VALID.
	 * Find all such letters and return them in a char[] array.
	 * @return null if no such letters exist.
	 */
	public char[] suggestLetterForNewWord(String validWord) {
		List<String> returnList = new ArrayList<String>();
		
		//Definitely not the easiest way...
		List<String> letterList = 	Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
									"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z");
		
		//Permutate with alle letter of the alphabet
		for(int i = 0; i < letterList.size(); i++){
			//Day of the crappy parameter. I apologize, but it works fine!
			List<String> tmpList = findDirectAnagrams( validWord, letterList.get(i).toCharArray()[0]);
			
			//if there are words => add to the returned list
			if(tmpList.size() > 0){
				returnList.add(letterList.get(i));
			}			
		}
		
		//List => Array
		char[] chars = new char[returnList.size()];
		for(int i = 0; i < returnList.size(); i++){
			chars[i] = returnList.get(i).toCharArray()[0];
		}
		
		return chars;
	}
	
	/**
	 * Given a random n-letter refWord and a validWord, which was generated from 
	 * refWord, suggest one letter that can be added into any position within the 
	 * validWord, and still make the new validWord (or its resultant anagrams) VALID.
	 * The suggested letter must NOT be in refWord.
	 * Return the one letter that will BEST improve the point-value of the new 
	 * validWord (or its anagrams). If more than one such letter exists, then return 
	 * the first one encountered.
	 * @return an empty char ' ' if no such letter exists.
	 */
	public char suggestLetterForNewWord(String refWord, String validWord) {
		// TODO Auto-generated method stub
		return ' ';
	}
	
	
	/**
	 * Executes a WordScrambler round:
	 *  1. Retrieves a random refWord from the LetterDispenser.
	 *  2. Generates all valid words from the refWord.
	 *  3. For each valid word, find the letter that will increase the word value,
	 *     and generate all DIRECT anagrams of the combined word and letter, and
	 *     add them to the list of valid words.
	 * @return the list of valid words in order of their point-values; i.e. largest 
	 * point-value on the top of the List. Returns null if no valid word exists.
	 */
	public List<String> executeRound() {
		String refWord = this.getScrambledWord(this.maxTiles);
		List<String> validWords = this.generateValidWords(refWord);

		// TODO Auto-generated method stub
		return null;
	}
	
    // ----------------------------------------------------------------------
	// Executable.
    // ----------------------------------------------------------------------
	public static void main(String[] args){
		WordScrambler wS = new WordScrambler();
		
		System.out.println("Ex 2:");		
		String word = "test";
		System.out.println("Value of word '" + word + "' is : " + wS.getPointValue(word) + "\n");
		
		System.out.println("Ex 3:");
		List<String> wList = Arrays.asList("thet", "on", "in", "tthe", "fox", "dog");
		System.out.println("Unsorterd: " + wList);
		wList = wS.sortWordsByPointValue(wList);
		System.out.println("Sorted by value then alphabetical: " + wList + "\n");

		System.out.println("Ex 4:");
		String w = "dog";
		System.out.println("Permutate and sort '" + w + "': "  + wS.generateValidWords(w) + "\n");
		
		System.out.println("Ex 5:");
		String w2 = "do";
		char c = 'g';
		System.out.println("Direct Anagrams of '" + w2 + "' and letter '" + c + "': \n" + wS.findDirectAnagrams(w2,c) + "\n");
		
		System.out.println("Ex 6:");
		System.out.print("With the letters '");
		System.out.print(wS.suggestLetterForNewWord(w2));
		System.out.println("' added to '" + w2 + "' you can make a valid new words.");
	}
}
