/**
 * @(#)Board.java	11/03/05
 * 
 * Copyright 2005 3GP01, KCL.  All unmodified code is copyright of its respective owner.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * The GNU General Public Licence should be contained within licence.txt;
 * if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA  02110-1301, USA.
 */

import java.util.*;

/**
 * This class creates a board for a given ScrabbleBuilder.
 * Class also provides methods for operating on the board.
 * Method <code>placeMove</code> is called by g.endTurn() and is key to
 * validating a player's Move.
 *
 * @author 3GP01 - Hardev Bhamra, Deepak Chandarana, James Tompkin
 * @version 2.0
 * @see Game
 * @see Move
 * @see LetterMove
 */

public class Board 
{	
	/**
	 * <b>Square</b> array containing all squares on the board
	 */
	private Square[][] squares = new Square[15][15];
	
	/**
	 * Square[x][y] is square at this coordinate
	 */
	private List allSquares = new ArrayList();
	
	/**
     * Constructs a board given a ScrabbleBuilder <b>b</b> as input.
     * 
     * @param b The ScrabbleBuilder of the current game.
     */
	public Board(ScrabbleBuilder b) 
	{ 	// uses b to do layout -- mostly standard
		squares = ScrabbleBuilder.getBoard();
		for (int i = 0; i < 15; i++) 
		{
			for (int j = 0; j < 15; j++) 
			{
				allSquares.add(squares[i][j]);
			}
		}
	}
	
	/**
     * Constructs a board given a <b>Square</b> array <b>arr</b> as input.
     * 
     * @param arr The <b>Square</b> array to be processed.
     */
	public Board(Square[][] arr) 
	{
		squares = arr;
		for (int i = 0; i < 15; i++) 
		{
			for (int j = 0; j < 15; j++) 
			{
				allSquares.add(squares[i][j]);
			}
		}
	}
	
	/**
     * Clones the current board.
     * 
     * @return Object A new <b>Board</b>.
     */
	public Object clone() {
		Square[][] newsquares = new Square[15][15];
		for (int i = 0; i < 15; i++) {
			for (int j = 0; j < 15; j++) 
			{
				Square sq = (Square) squares[i][j].clone();
				newsquares[i][j] = sq;
			}
		}
		return new Board(newsquares);
	}
	
	/**
     * Gets a square on the board at a given <b>i</b>, <b>j</b>.
     * 
     * @param i The i co-ordinate on the board of the required square.
     * @param j The j co-ordinate on the board of the required square.
     * 
     * @return Square The ScrabbleBuilder of the current game.
     */
	//TODO ArrayIndexOutOfBounds on getting a square with i, j >14
	//This error has occured once before during the whole project.
	public Square getSquare(int i, int j) 
	{
		return squares[i][j];
	}
	
	/**
     * Returns a row of squares as an ArrayList.
     * 
     * @param y The y co-ordinate of the row you'd like to return.
     * 
     * @return ArrayList The row required.
     */
	public ArrayList getRow(int y) 
	{
		ArrayList res = new ArrayList();
		for (int i = 0; i < 15; i++) 
		{
			res.add(squares[i][y]);
		}
		return res;
	}
	
	/**
     * Returns all squares as a List.
     * 
     * @return List A List containing all squares.
     */
	public List getSquares() 
	{
		return allSquares;
	}
	
	/**
     * Returns a boolean stating whether a specified square is occupied or not (ie whether it has a letter placed on it or not).
     * 
     * @param x The x co-ordinate of the square you'd like to test.
     * @param y The y co-ordinate of the square you'd like to test.
     * 
     * @return boolean A true or false value.
     */
	public boolean isOccupied(int x, int y) 
	{
		return squares[x][y].isOccupied();
	}
	
	/**
     * Returns a boolean stating whether a specified occupied square <b>i</b>, <b>j</b> is adjacent to any other occupied squares.
     * 
     * @param x The x co-ordinate of the square you'd like to test.
     * @param y The y co-ordinate of the square you'd like to test.
     * 
     * @return boolean A true or false value.
     */
	public boolean occupiedAdjacent(int x, int y) 
	{ 	
		if (getSquare(7, 7).isOccupied()) 
		{
			int x0 = x;
			int y0 = y;
			int x1 = x;
			int y1 = y;
			if (x > 0) 
			{
				x0 = x - 1;
			}
			if (x < 14) {
				x1 = x + 1;
			}
			if (y > 0) 
			{
				y0 = y - 1;
			}
			if (y < 14) 
			{
				y1 = y + 1;
			}
			return squares[x0][y].isOccupied() || squares[x1][y].isOccupied()
			|| squares[x][y0].isOccupied()
			|| squares[x][y1].isOccupied();
		} 
		else if (!getSquare(7, 7).isOccupied()) 
		{
			return true;
		} 
		else 
		{
			return false;
		}
	}
	
	/**
     * Returns a boolean after receiving a boolean from m.validateMove(numb).
     * 
     * @param m The <b>Move</b> you'd like to validate.
     * @param numb The type of validation you'd like.
     * 
     * @return boolean A true or false value.
     */
	public boolean validateMove(Move m, int numb) 
	{
		return m.validateMove(numb);
	} // check each created word is valid
	
	/**
     * Returns a boolean stating whether a specified Move.
     * 
     * @param m The <b>Move</b> you'd like to place.
     * @param oldboard The <b>Board</b> you'd like it to be placed on.
     * 
     * @return boolean A true or false value.
     */
	public boolean placeMove(Move m, Board oldboard) 
	{
		if (m == null) 
		{
			return false;
		}
		List lms = m.getLetterMoves();
		List connected = new ArrayList();
		List disconnected = new ArrayList();
		
		for (int i = 0; i < lms.size(); i++) 
		{
			LetterMove lm = (LetterMove) lms.get(i);
			Letter l = lm.getLetter();
			int x = lm.getX();
			int y = lm.getY();
			if (oldboard.occupiedAdjacent(x, y)) 
			{
				connected.add(lm);
			} 
			else 
			{
				disconnected.add(lm);
			}
			placeLetter(l, x, y);
		}
		
		if (connected.size() == 0) 
		{ 	//System.out.println("Invalid move: disconnected moves " + disconnected); 
			return false;
		}
		return true;
	}
	
	/**
     * Resets a given Move's used squares.
     * 
     * @param m The <b>Move</b> you'd like to be reset.
     * 
     */
	public void resetMoveSquares(Move m) 
	{
		List lms = m.getLetterMoves(); //get list of new letters on board
		
		for (int i = 0; i < lms.size(); i++) 
		{
			LetterMove lm = (LetterMove) lms.get(i);
			Letter l = null; //set label of square
			int x = lm.getX(); //get x location of square
			int y = lm.getY(); //get y location of square
			placeLetter(l, x, y); //sets label l for square at x,y 
		}
	}
	
	/**
     * Method to place a given letter on the board on a specific <b>i</b>, <b>j</b> square.
     * 
     * @param l The <b>Letter</b> you'd like to place.
     * @param x The x co-ordinate of the square you'd like to place a letter.
     * @param y The y co-ordinate of the square you'd like to place a letter.
     * 
     */
	public void placeLetter(Letter l, int x, int y) 
	{
		squares[x][y].setLetter(l);
	}
	
	/**
     * Returns a String representation of a square.
     * 
     * @return String
     * 
     */
	public String toString() 
	{
		String res = "";
		for (int j = 0; j < 15; j++) 
		{
			for (int i = 0; i < 15; i++) 
			{
				res = res + squares[i][j];
			}
			res = res + "\n";
		}
		return res;
	}
}