package com.jp;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.util.Arrays;

public class Dawg {
	
	private static final int CHILD_BIT_SHIFT = 5;
	private static final int CHILD_INDEX_BIT_MASK = 0X003FFFE0;
	private static final int LETTER_BIT_MASK = 0X0000001F;
	private static final int END_OF_WORD_BIT_MASK = 0X00800000;
	private static final int END_OF_LIST_BIT_MASK = 0X00400000;
	private static final int INPUT_SIZE_LIMIT = 100;
	private static final char LOWER_IT = 32;

	private int numberOfNodes;

	private int[] theDawgArray;
	
	public Dawg() throws Exception {

	    //DataInputStream dawgDataFile = new DataInputStream(new BufferedInputStream(getClass().getResourceAsStream("Traditional_Dawg_For_Word-List.dat")));
	    DataInputStream dawgDataFile = new DataInputStream(new BufferedInputStream(new FileInputStream("Traditional_Dawg_For_Word-List.dat")));
	    numberOfNodes = endianConversion(dawgDataFile.readInt());
		theDawgArray = new int[numberOfNodes];
		
		for (int x = 0; x < numberOfNodes; x++) {
			theDawgArray[x] = endianConversion(dawgDataFile.readInt());
		}
		dawgDataFile.close();
	}

	private int endianConversion(int thisInteger) {
		return ((thisInteger & 0x000000ff) << 24) + ((thisInteger & 0x0000ff00) << 8) + ((thisInteger & 0x00ff0000) >>> 8) + ((thisInteger & 0xff000000) >>> 24);
	}
	
	// These methods are used to extract information from the "theDawgArray" nodes.
	private char nodeLetter(int index) {
		return (char)((theDawgArray[index]&LETTER_BIT_MASK) + 'A');
	}
	private boolean nodeEndOfWord(int index) {
		return ((theDawgArray[index]&END_OF_WORD_BIT_MASK) != 0);
	}
	private int nodeNext(int index) {
		return ((theDawgArray[index]&END_OF_LIST_BIT_MASK) == 0)? (index + 1): 0;
	}
	private int nodeChild(int index) {
		return ((theDawgArray[index]&CHILD_INDEX_BIT_MASK)>>>CHILD_BIT_SHIFT);
	}

	private String searchForStringRecurse(String thisString, int position, int thisIndex, boolean[] result) {
		int currentIndex = thisIndex;
		char currentChar = thisString.charAt(position);
		String addThisMessage = new String("----------------------------------------\n");
		addThisMessage += "Seek |" + currentChar + "| in position |" + position + "|.\n";
		String returnHolder;
		while ( currentIndex != 0 ) {
			addThisMessage += "Node|" + currentIndex + "| Letter|" + nodeLetter(currentIndex) + "| "; 
			if ( currentChar > nodeLetter(currentIndex) ) {
				currentIndex = nodeNext(currentIndex);
				addThisMessage += "- Letter too small.\n";
			}
			else if ( currentChar < nodeLetter(currentIndex) ) {
				result[0]= false; 
				return (addThisMessage + "- Letter too big\n\nWord Not Found\n");
			}
			else if ( thisString.length() == (position + 1) ) {
				addThisMessage += "= Letter match.\n";
				if ( nodeEndOfWord(currentIndex) ) {
					result[0] = true;
					return (addThisMessage + "\nWord Found.\n");
				}
				else {
					result[0] = false;
					return (addThisMessage + "\nWord Not Found.\n");
				}
			}
			else {
				addThisMessage += "= Letter match.\n";
				returnHolder = searchForStringRecurse(thisString, position + 1, nodeChild(currentIndex), result);
				addThisMessage += returnHolder;
				return addThisMessage;
			}
		}
		result[0] = false;
		return (addThisMessage + "Reached end of list.\n\nWord Not Found\n");
	}

	public String searchForString(String toSearchFor) {
		boolean[] found = new boolean[1];
		String holder;
		String upperString = toSearchFor.toUpperCase();
		String traversalResult = new String("Searching for:  |" + upperString + "| - ");
		found[0] = false;
		holder = searchForStringRecurse(upperString, 0, (upperString.charAt(0) - 'A' + 1), found);
		if ( found[0] ) traversalResult += "Word Found.\n";
		else traversalResult += "Word Not Found.\n";
		traversalResult += holder;
		return traversalResult;
	}
	
	private String searchForPatternRecurse(String emptyPattern, int position, int thisIndex, char[] thePattern, int[] tally) {
		int currentIndex = thisIndex;
		String addThisMessage = "";
		String returnHolder;
		char currentChar = emptyPattern.charAt(position);
		while ( currentIndex != 0 ) {
			if ( currentChar == '?') {
				thePattern[position] = nodeLetter(currentIndex);
				thePattern[position] += LOWER_IT;
				if ( (position == (emptyPattern.length() - 1)) ) {
					if ( nodeEndOfWord(currentIndex) ) {
						tally[0] += 1;
						addThisMessage += "|" + tally[0] + "| - " + new String(thePattern, 0, position + 1) + "\n";
					}
				}
				else {
					returnHolder = searchForPatternRecurse(emptyPattern, position + 1, nodeChild(currentIndex), thePattern, tally);
					addThisMessage += returnHolder;
				}
				currentIndex = nodeNext(currentIndex);
			}
			else if ( currentChar > nodeLetter(currentIndex) ) {
				currentIndex = nodeNext(currentIndex);
			}
			else if ( currentChar < nodeLetter(currentIndex) ) {
				break;
			}
			else if ( (position == (emptyPattern.length() - 1)) ) {
				if ( nodeEndOfWord(currentIndex) ) {
					thePattern[position] = nodeLetter(currentIndex);
					tally[0] += 1;
					addThisMessage = "|" + tally[0] + "| - " + new String(thePattern, 0, position + 1) + "\n";
					return addThisMessage;
				}
				break;
			}
			else {
				thePattern[position] = nodeLetter(currentIndex);
				addThisMessage = searchForPatternRecurse(emptyPattern, position + 1, nodeChild(currentIndex), thePattern, tally);
				return addThisMessage;
			}
		}
		return addThisMessage;
	}
	
	public String searchForPattern(String thisPattern) {
		int[] counter = new int[1];
		String holder = "";
		String upperString = thisPattern.toUpperCase();
		String traversalResult = new String("Pattern:  |" + upperString + "| - ");
		char[] runningPattern = new char[upperString.length()];
		counter[0] = 0;
		if ( upperString.charAt(0) != '?' ) holder += searchForPatternRecurse(upperString, 0, (upperString.charAt(0) - '@'), runningPattern, counter);
		else holder += searchForPatternRecurse(upperString, 0, 1, runningPattern, counter);
		traversalResult += counter[0] + " Words fit:\n\n";
		traversalResult += holder;
		return traversalResult;
	}
	
	// This method is the core program component.  It requires that "unusedChars" be in alphabetical order because the traditional "Dawg" is a list based structure.
	private String anagramRecurse(int currentIndex, char[] toyWithMe, int fillThisPosition, char[] unusedChars, int sizeOfBank, int[] forTheCounter, boolean onWildcard){
		char previousChar = '\0';
		char currentChar;
		int tempIndex = nodeChild(currentIndex);
		String holder;
		String wordAccumulator = "";
		
		toyWithMe[fillThisPosition] = nodeLetter(currentIndex);
		if ( onWildcard ) toyWithMe[fillThisPosition] += LOWER_IT;
		if ( nodeEndOfWord(currentIndex) ) {
			forTheCounter[0] += 1;
			wordAccumulator = new String(toyWithMe, 0, fillThisPosition + 1);
			wordAccumulator = "|" + forTheCounter[0] + "| - " + wordAccumulator + "";
			if ( sizeOfBank == 0 ) wordAccumulator += " ********->\n";
			else wordAccumulator +="\n";
		}
		
		if ( (sizeOfBank > 0) && (tempIndex != 0) ) {
			for ( int x = 0; x < sizeOfBank; x++ ) {
				currentChar = unusedChars[x];
				if ( currentChar == previousChar ) continue;
				do {
					if ( currentChar == nodeLetter(tempIndex) || currentChar == '?') {
						removeCharFromArray(unusedChars, x, sizeOfBank);
						holder = anagramRecurse(tempIndex, toyWithMe, fillThisPosition + 1, unusedChars, sizeOfBank - 1, forTheCounter, (currentChar == '?')? true: false);
						wordAccumulator += holder;
						insertCharIntoArray(unusedChars, x, currentChar, sizeOfBank);
						if ( currentChar != '?' ) {
							tempIndex = nodeNext(tempIndex);
							break;
						}
					}
					else if ( currentChar < nodeLetter(tempIndex) ) break;
				} while ( (tempIndex = nodeNext(tempIndex)) != 0 );
				if ( currentChar == '?' ) tempIndex = nodeChild(currentIndex);
				if ( tempIndex == 0 ) break;
				previousChar = currentChar;
			}
		}
		return wordAccumulator;
	}
	
	// The "toScrambleUp" String may contain '?' wildcards, so indicate these wildcards as lower case letters.
	public String anagram(String toScrambleUp) {
		String upperString = toScrambleUp.toUpperCase();
		int numberOfLetters = upperString.length();
		char[] inputCharArray = new char[INPUT_SIZE_LIMIT];
		char[] theWordSoFar = new char[INPUT_SIZE_LIMIT];
		upperString.getChars(0, numberOfLetters, inputCharArray, 0);
		Arrays.sort(inputCharArray,0,numberOfLetters);
		String traversalResult = "";
		String holder;
		
		char previousChar = '\0';
		char currentChar;
		int[] forTheCount = new int[1];
		forTheCount[0] = 0;
		
		for ( int x = 0; x < numberOfLetters; x++){
			currentChar = inputCharArray[x];
			if ( currentChar == previousChar ) continue;
			removeCharFromArray(inputCharArray, x, numberOfLetters);
			if ( currentChar != '?' ) {
				holder = "-----------------------------\n";
				holder += anagramRecurse(currentChar - '@', theWordSoFar, 0, inputCharArray, numberOfLetters - 1, forTheCount, false);
				traversalResult += holder;
			}
			else {
				for ( int y = 'A'; y <= 'Z'; y++  ) {
					holder = "-----------------------------\n";
					holder += anagramRecurse(y - '@', theWordSoFar, 0, inputCharArray, numberOfLetters - 1, forTheCount, true);
					traversalResult += holder;
				}
			}
			insertCharIntoArray(inputCharArray, x, currentChar, numberOfLetters);
			previousChar = currentChar;
		}
		traversalResult = "Anagramming this:  |" + upperString + "|\nResults in |" + forTheCount[0] + "| words.\n" + traversalResult;
		return traversalResult;
	}
	
	private void removeCharFromArray(char[] thisArray, int thisPosition, int size) {
		System.arraycopy(thisArray, thisPosition + 1, thisArray, thisPosition, (size - thisPosition - 1));
	}
	
	private void insertCharIntoArray(char[] thisArray, int thisPosition, char thisChar, int size) {
		System.arraycopy(thisArray, thisPosition, thisArray, thisPosition + 1, (size - thisPosition - 1));
		thisArray[thisPosition] = thisChar;
	}
}

