package net.woodyfolsom.msproj.policy;

import java.util.List;

import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.GoGame;
import net.woodyfolsom.msproj.Player;
import net.woodyfolsom.msproj.StateEvaluator;


public class AlphaBeta implements Policy {
	private static final int DEFAULT_RECURSIVE_PLAYS = 1;
	private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator();

	private Action bestPick = Action.PASS;

	@Override
	public Action getAction(GameConfig gameConfig, GameState gameState,
			Player player) {

		int alpha = Integer.MIN_VALUE;
		int beta = Integer.MAX_VALUE;

		if (player == Player.BLACK) {
			getMaxValue(gameConfig, gameState, player, false,
					DEFAULT_RECURSIVE_PLAYS * 2, alpha, beta);
			return bestPick;
		} else if (player == Player.WHITE) {
			getMinValue(gameConfig, gameState, player, false,
					DEFAULT_RECURSIVE_PLAYS * 2, alpha, beta);
			return bestPick;
		} else {
			return Action.PASS;
		}
	}

	private int getMaxValue(GameConfig gameConfig, GameState gameState,
			Player initialColor, boolean playAsOpponent, int recursionLevel,
			int alpha, int beta) {
		if (terminalTest(recursionLevel)) {
			return getUtility(gameConfig, gameState);
		}

		Player colorPlaying = GoGame.getColorToPlay(initialColor, playAsOpponent);

		List<Action> validMoves = validMoveGenerator.getActions(gameConfig,
				gameState, colorPlaying, ActionGenerator.ALL_ACTIONS);

		int value = Integer.MIN_VALUE;

		for (Action nextMove : validMoves) {
			GameState nextState = new GameState(gameState);

			if (!nextState.playStone(colorPlaying, nextMove)) {
				throw new RuntimeException(
						"Illegal move attempted during search!");
			}

			int minValue = getMinValue(gameConfig, nextState, initialColor,
					!playAsOpponent, recursionLevel - 1, alpha, beta);

			if (minValue > value) {
				value = minValue;
				if (recursionLevel == DEFAULT_RECURSIVE_PLAYS * 2) {
					bestPick = nextMove;
				}
			}

			if (value >= beta) {
				return value;
			}
			alpha = Math.max(alpha, value);
		}

		return value;
	}

	private int getMinValue(GameConfig gameConfig, GameState gameState,
			Player initialColor, boolean playAsOpponent, int recursionLevel,
			int alpha, int beta) {
		if (terminalTest(recursionLevel)) {
			return getUtility(gameConfig, gameState);
		}

		Player colorPlaying = GoGame.getColorToPlay(initialColor, playAsOpponent);

		List<Action> validMoves = validMoveGenerator.getActions(gameConfig,
				gameState, colorPlaying, ActionGenerator.ALL_ACTIONS);

		int value = Integer.MAX_VALUE;

		for (Action nextMove : validMoves) {
			GameState nextState = new GameState(gameState);

			if (!nextState.playStone(colorPlaying, nextMove)) {
				throw new RuntimeException(
						"Illegal move attempted during search!");
			}

			int maxValue = getMaxValue(gameConfig, nextState, initialColor,
					!playAsOpponent, recursionLevel - 1, alpha, beta);

			if (maxValue < value) {
				value = maxValue;
				if (recursionLevel == 2 * DEFAULT_RECURSIVE_PLAYS) {
					bestPick = nextMove;
				}
			}

			if (value <= alpha) {
				return value;
			}
			beta = Math.min(beta, value);
		}

		return value;
	}

	private boolean terminalTest(int recursionLevel) {
		return recursionLevel < 1;
	}

	private int getUtility(GameConfig gameConfig, GameState gameState) {
		StateEvaluator stateEvaluator = new StateEvaluator(gameConfig);
		return stateEvaluator.scoreGame(gameState).getAggregateScore();
	}
}