import java.util.Random;
import java.util.ArrayList;
import java.awt.Point;

/**
 * Write a description of class BoggleBoard here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class BoggleBoard
{	public static final int BOARD_SIZE = 4;
	public static final int NUM_CELLS = BOARD_SIZE * BOARD_SIZE;

	private static final int CAP_OFFSET = 'a' - 'A';
	private static Random classRandNumGen = new Random();
	private static String[] classDiceLetters = {"lrytte", "vthrwe", "eghwne",
		"seotis", "anaeeg", "idsytt", "oattow", "mtoicu", "afpkfs",
		"xlderi", "hcpoas", "ensieu", "yldevr", "znrnhl", "nmiqhu", 
		"obbaoj"};

	private LetterDice[] myDice;
	private char[][] myBoard;
	private boolean bMyIsHighlighted;

	/* 
	pre: none
	post: board ready, shake should still be called to randomize the dice
	*/
	public BoggleBoard()
	{	myBoard = new char[BOARD_SIZE][BOARD_SIZE];
		myDice = new LetterDice[classDiceLetters.length];
		for(int i = 0; i < myDice.length; i++)
			myDice[i] = new LetterDice( classDiceLetters[i] );
		bMyIsHighlighted = false;
	}

	public BoggleBoard(String letters)
	{	myBoard = new char[BOARD_SIZE][BOARD_SIZE];
		for(int i = 0; i < BOARD_SIZE * BOARD_SIZE; i++)
			myBoard[i/BOARD_SIZE][i%BOARD_SIZE] = letters.charAt(i);
	}

	/*
	pre: none
	post:dice in random locations and randmom side showing
	*/ 
	public void shake()
	{	//randomize the dice in the list
		LetterDice temp;
		int num;
		for(int i = 0; i < myDice.length; i++)
		{	num = classRandNumGen.nextInt(myDice.length);
			temp = myDice[num];
			myDice[num] = myDice[i];
			myDice[i] =  temp;
		}

		// roll each Dice
		for(int i = 0; i < myDice.length; i++)
			myBoard[i/BOARD_SIZE][i%BOARD_SIZE] = myDice[i].roll();
	}

	/*
	pre: none
	post: returns string version of board. 
	ewline char inserted between rows of the board
	*/
	public String toString()
	{	String result = "";
		for(int r = 0; r < BOARD_SIZE; r++)
		{	for(int c = 0; c < BOARD_SIZE; c++)
				result += myBoard[r][c] + " ";
			result += "\n";
		}
		return result;
	}

	/*
	pre: word != null
	post: if word is onboard it is highlighted (all
	 chars in word shifted to uppercase) otherwise
	 board left in same state as before
	*/
	public void findAndHighlightWord(String word)
	{	ArrayList wordLocation = new ArrayList();
		Point startCell = new Point(0,0);
		boolean found = false;
		int cellNum = 0;
		while( !found && cellNum < NUM_CELLS )
		{	startCell.y = cellNum / BOARD_SIZE;
			startCell.x = cellNum % BOARD_SIZE;
			found = findWord(word, startCell, wordLocation);
			cellNum++; 
		}
		if(found)
		{	bMyIsHighlighted = true;
			highlightWord(wordLocation);
		}		
	}

	private void highlightWord(ArrayList wordLocation)
	{	Point temp;
		for(int i = 0; i < wordLocation.size(); i++)
		{	temp = ( (Point)wordLocation.get(i) );
			myBoard[temp.y][temp.x] = (char)((int)myBoard[temp.y][temp.x] - CAP_OFFSET);
		}
	}

	/*
	pre: none
	post: all letters un highlighted
	*/
	public void unHighlight()
	{	if(bMyIsHighlighted)
		{	for(int r = 0; r < BOARD_SIZE; r++)
				for(int c = 0; c < BOARD_SIZE; c++)
					myBoard[r][c] = Character.toLowerCase( myBoard[r][c] );
			bMyIsHighlighted = false;
		}
	}
		
	private boolean cellOnBoard(Point cell)
	{	return cell.x >= 0 && cell.x < BOARD_SIZE
			 && cell.y >= 0 && cell.y < BOARD_SIZE;
	}

	private boolean cellUsed(Point cell, ArrayList list)
	{	return list.contains(cell);	}

	/*
	used to find a given word on the board. Uses an arrayList of Points to
	track locations that have already been used in the word.
	base cases: word found, cell does not exist, cell already used, 
		and cell does not contain the next letter in the word
	recursive step: look at all 8 surrounding cells for the next
		letter in the word
	*/
	private boolean findWord(String word, Point currentCell, ArrayList wordLocation)
	{	boolean done = wordLocation.size() == word.length();
		if( !done 
			&& cellOnBoard(currentCell) 
			&& !cellUsed(currentCell, wordLocation)
			&& myBoard[currentCell.y][currentCell.x] == word.charAt(wordLocation.size()))
		{	wordLocation.add(currentCell);
			done = wordLocation.size() == word.length();
			if( !done ) 
			{	Point nextCell = new Point();
				for(int row = -1; row < 2 && !done; row ++)
					for(int col = -1; col < 2 && !done; col++)
					{	nextCell.y = currentCell.y + row;
						nextCell.x = currentCell.x + col;
						done = findWord(word, nextCell, wordLocation);
					}
				if(!done)
					wordLocation.remove(wordLocation.size() - 1);
			}
		}
		return done;
	}

	/*
	pre: none
	post: returns true if board is currently unhighlighted
	*/
	public boolean isHighlighted()
	{	return bMyIsHighlighted;	}
	
	/*
	pre: lex != null
	post: returns an ArrayList of all words on board and in lex
	*/
	public ArrayList findAllWords(Lexicon lex)
	{	ArrayList result = new ArrayList();
		ArrayList wordLocation = new ArrayList();
		Point startCell = new Point();
		String currentWord = "";
		
		for(int num = 0; num < NUM_CELLS; num++)
		{	startCell.y = num / BOARD_SIZE;
			startCell.x = num % BOARD_SIZE;
			currentWord = "";
			findAllWordsStartingAtCell(lex, result, startCell, currentWord, wordLocation);
		}
		return result;
	}

	/*
	builds all words from a given cell. 
	base cases: cell does not exist, cell has already been used
		in the current word, and the word formed by adding this
		cell is not a valid prefix for any words in lex
	recursive step: add the current letter to the current word.
		if it forms a valid word that has not been used yet add
		it to the list of words. Check all 8 neighboring cells
		for words
	*/
	private void findAllWordsStartingAtCell(Lexicon lex, 
		ArrayList words, Point currentCell,
		String currentWord, ArrayList wordLocation)
	{	//System.out.println("At cell " + currentCell + " current word " + currentWord);
		//System.out.println("foundWords " + foundWords.size() + " newWords " + newWords.size() + " wordLocation " + wordLocation.size());
		if( cellOnBoard(currentCell) && !cellUsed(currentCell, wordLocation)
			&& lex.isValidPrefix(currentWord + myBoard[currentCell.y][currentCell.x]))
		{	currentWord += myBoard[currentCell.y][currentCell.x];
			wordLocation.add(currentCell);
			if( lex.wordIsPresent(currentWord) 
				&& !words.contains(currentWord))
			{	//System.out.println("Adding " + currentWord + " Size of list " + newWords.size());
				words.add(currentWord);	}
			Point next = new Point();
			for(int row = -1; row < 2; row ++)
				for(int col = -1; col < 2; col++)
				{	next.y = currentCell.y + row;
					next.x = currentCell.x + col;
					findAllWordsStartingAtCell(lex, words, 
						next, currentWord, wordLocation);
				}
			wordLocation.remove(wordLocation.size() - 1);
		}
	}
 
}
