import java.util.Vector;

/**
 *  class Engine
 *
 *  This class holds static methods for finding the best move
 *  give a certain position.
 *
 *  @author Jonatan Pettersson (zlaire@gmail.com)
 *  Date: 2006-12-26
 */

public class Engine implements Definitions
{
	public static final int PLY_DEPTH = 5;
	public static Vector pv = new Vector();

	/**
	 *  Calls the alpha beta and returns
	 *  the principal variation
	 *
	 *  @param Board The board on which to find the moves
	 *  @return Vector The principal variation
	 */
	public static LineEval search(Board board)
	{
		// Will return the principal variation
		// not implemented yet, use alphaBeta()
		// to get evaluation of the board		

		int eval = alphaBeta(board,PLY_DEPTH,-200000,200000, pv);

		LineEval finalEval = new LineEval(pv, eval);

		return finalEval;
	}
	// END search()
	
	/**
	 *  class LineEval
	 *
	 *  Holds a line and evaluation number used to return in search
	 */
	public static class LineEval
	{
		public Vector line;
		public int eval;

		public LineEval(Vector line, int eval)
		{
			this.line = line;
			this.eval = eval;
		}
	}

	/**
	 *  The alpha-beta search.
	 *
	 *  @param Board The position we're at
	 *  @param int The current ply we're at
	 *  @param int The alpha value
	 *  @param int The beta value
	 *  @param Vector Passing the principal variation through the trees
	 *  @return int Depending on where in the search we are
	 *              it returns alpha, beta (cutoff) or the
	 *              final evaluation
	 */
	public static int alphaBeta(Board board, int ply, int alpha, int beta, Vector pv)
	{
		Vector localpv = new Vector(); // Holds this ply's pv
		Vector moves = board.generateMoves(); // Generate moves for the position

		
		if (moves.size() == 0) return mateCheck(board, ply);
		else if(ply == 0) return evaluatePosition(board); // If we reached our wanted depth, evaluate the position
		else // We're not at the wanted depth and continue with the search
		{
			for(int i = 0; i < moves.size(); i++) // Loop through all the moves
			{
				board.makeMove((Move)moves.get(i)); // Make the move on the board

				// Call the method again with the updated board, one less ply and
				// swapped alpha and beta. This will return the evaluation after
				// all recursive calls are done.
				int eval = -alphaBeta(board, ply-1, -beta, -alpha, localpv);

				board.unmakeMove((Move)moves.get(i)); // Reset the board

				if(eval >= beta) // If the evalatuion is bigger than beta, cutoff
					return beta;

				if(eval > alpha) // If the evaluation is bigger than alpha (but less than beta) this is our new best move
				{
					// Extract the principal variation
					//
					// If there is nothing in the pv so far, add a new move
					if(pv.size() == 0)
						pv.add(moves.get(i));
					else // Else replace the old move
						pv.set(0, moves.get(i)); // Add the move at first index

					// Append the localpv to the pv
					for(int j = 0; j < localpv.size(); j++)
					{
						// Check if we have a spot ready in the pv or if
						// we need to add a new one.
						if(pv.size() -2 < j)
							pv.add(j+1, localpv.get(j));
						else
							pv.set(j+1, localpv.get(j));
					}

					// Remember the new best value
					alpha = eval;
				}
			}

			// Check for mate/stalemate
			if(moves.size() == 0) return mateCheck(board, ply);
			
		}

		
	//	catch(ArrayIndexOutOfBoundsException ex)
		return alpha;
	}
	//END alphaBeta()

	/**
	 *  Takes a position and evaluates it
	 *  Returns the evaluation based on who is moving, negate it if black
	 *  (need to be like that for the search)
	 *
	 *  @param Board The position
	 *  @return int The evaluation of the position
	 */
	public static int evaluatePosition(Board board)
	{
		// Check for draw by fifty moves rule
		if(board.movesFifty >= 50) return DRAW_VALUE * board.toMove; // Negate if we ever choose another value than0 for draw

		// Go through all the pieces on the board
		//
		// Add up the values, and see if there is enough material left to force a mate

		boolean whiteHasBishop = false; // Used by draw by material detection
		boolean whiteHasKnight = false; // we need to keep track of the number of
		boolean blackHasBishop = false; // knights and bishops on the board since 1
		boolean blackHasKnight = false; // is draw but 2 is not

		int evaluation = 0; // Initialize the evaluation
		boolean drawn_by_material = true; // Initialize the draw check
		for(int i = 0; i < 120; i++)
		{
			if((i & 0x88) == 0)
			{
				// Add the value of the piece to the evaluation
				evaluation += PIECE_VALUE_ARRAY[board.boardArray[i] + 7];

				// Do the checks for drawn by material
				if(drawn_by_material && board.boardArray[i] != EMPTY_SQUARE)
				{
					// If either side has queen, rook or pawn it is not drawn
					if(board.boardArray[i] == W_QUEEN || board.boardArray[i] == W_ROOK  ||
					   board.boardArray[i] == W_PAWN  || board.boardArray[i] == B_QUEEN ||
					   board.boardArray[i] == B_ROOK  || board.boardArray[i] == B_PAWN    )
						drawn_by_material = false;

					else if((whiteHasBishop || whiteHasKnight) && board.boardArray[i] == W_BISHOP)
						drawn_by_material = false;

					else if(whiteHasBishop && board.boardArray[i] == W_KNIGHT)
						drawn_by_material = false;

					else if(board.boardArray[i] == W_BISHOP)
						whiteHasBishop = true;

					else if(board.boardArray[i] == W_KNIGHT)
						whiteHasKnight = true;

					else if((blackHasBishop || blackHasKnight) && board.boardArray[i] == B_BISHOP)
						drawn_by_material = false;

					else if(blackHasBishop && board.boardArray[i] == B_KNIGHT)
						drawn_by_material = false;

					else if(board.boardArray[i] == B_BISHOP)
						blackHasBishop = true;

					else if(board.boardArray[i] == B_KNIGHT)
						blackHasKnight = true;

				}

			}
			else i += 7;
		}

		if(drawn_by_material) return DRAW_VALUE * board.toMove;
		else return evaluation * board.toMove; 
	}
	// END evaluatePosition()
	
	/**
	 *  Takes a board and checks for mate/stalemate
	 *
	 *  @param Board The board to check
	 *  @param int What ply the mate is found on
	 *  @return int The evaluation
	 */
	public static int mateCheck(Board board, int ply)
	{

		// Start with finding the king
		int king_square = -1;		
		for(int index = 0; index < 120; index++)
		{
			if((index & 0x88) == 0)
			{
				if(board.boardArray[index]*board.toMove == W_KING)
				{
					king_square = index; // Remember the king's index
					index = 120; // End the loop
				}
			}
			else index += 7;
		}

		board.toMove *= -1; // Switch side to move on the board to get the other side's pieces to be the attacker
		if(board.isAttacked(king_square))
		{
			board.toMove *= -1; // Switch back side to move
			return (MATE_VALUE + ply) * board.toMove; // Return value depending on who is checking, add the ply to make it more worth making a fast mate (a high ply means near the root)
		}
		else
		{
			board.toMove *= -1; // Switch back side to move
			return DRAW_VALUE * board.toMove;
		}	



	}
	//END mateCheck()
	
}
