package net.woodyfolsom.msproj.policy;

import java.util.Collection;
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.tree.GameTreeNode;
import net.woodyfolsom.msproj.tree.MinimaxProperties;

public class Minimax implements Policy {
	private static final int DEFAULT_LOOKAHEAD = 1;

	private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator();

	private int lookAhead;
	private int numStateEvaluations = 0;

	public Minimax() {
		this(DEFAULT_LOOKAHEAD);
	}

	public Minimax(int lookAhead) {
		this.lookAhead = lookAhead;
	}

	@Override
	public Action getAction(GameConfig gameConfig, GameState gameState,
			Player player) {
		numStateEvaluations = 0;

		GameTreeNode<MinimaxProperties> rootNode = new GameTreeNode<MinimaxProperties>(
				gameState, new MinimaxProperties());

		if (player == Player.BLACK) {
			return getMax(lookAhead * 2, rootNode, player);
		} else {
			return getMin(lookAhead * 2, rootNode, player);
		}
	}

	private boolean isTerminal(int nValidMoves, int recursionLevels) {
		return recursionLevels == 0 || nValidMoves == 0;
	}

	private Action getMax(int recursionLevels,
			GameTreeNode<MinimaxProperties> node, Player player) {

		GameState gameState = new GameState(node.getGameState());

		List<Action> validMoves = validMoveGenerator.getActions(node
				.getGameState().getGameConfig(), node.getGameState(), player,
				ActionGenerator.ALL_ACTIONS);

		boolean terminal = isTerminal(validMoves.size(), recursionLevels);

		double maxScore = Double.NEGATIVE_INFINITY;
		Action bestAction = Action.NONE;

		if (terminal) {
			node.getProperties().setReward(
					gameState.getResult().getNormalizedScore());

			numStateEvaluations++;

			return bestAction;
		} else {

			for (Action nextMove : validMoves) {
				GameState nextState = new GameState(gameState);
				nextState.playStone(player, nextMove);
				GameTreeNode<MinimaxProperties> childNode = new GameTreeNode<MinimaxProperties>(
						nextState, new MinimaxProperties());
				node.addChild(nextMove, childNode);

				getMin(recursionLevels - 1, childNode,
						GoGame.getNextPlayer(player));

				double gameScore = childNode.getProperties().getReward();

				if (gameScore > maxScore) {
					maxScore = gameScore;
					bestAction = nextMove;
				}
			}

			node.getProperties().setReward(maxScore);
			return bestAction;
		}
	}

	private Action getMin(int recursionLevels,
			GameTreeNode<MinimaxProperties> node, Player player) {

		GameState gameState = new GameState(node.getGameState());

		List<Action> validMoves = validMoveGenerator.getActions(node
				.getGameState().getGameConfig(), node.getGameState(), player,
				ActionGenerator.ALL_ACTIONS);

		boolean terminal = isTerminal(validMoves.size(), recursionLevels);

		double minScore = Double.POSITIVE_INFINITY;
		Action bestAction = Action.NONE;

		if (terminal) {
			node.getProperties().setReward(
					gameState.getResult().getNormalizedScore());

			numStateEvaluations++;

			return bestAction;
		} else {

			for (Action nextMove : validMoves) {
				GameState nextState = new GameState(gameState);
				nextState.playStone(player, nextMove);
				GameTreeNode<MinimaxProperties> childNode = new GameTreeNode<MinimaxProperties>(
						nextState, new MinimaxProperties());
				node.addChild(nextMove, childNode);

				getMax(recursionLevels - 1, childNode,
						GoGame.getNextPlayer(player));

				double gameScore = childNode.getProperties().getReward();

				if (gameScore < minScore) {
					minScore = gameScore;
					bestAction = nextMove;
				}
			}

			node.getProperties().setReward(minScore);
			return bestAction;
		}
	}

	@Override
	public int getNumStateEvaluations() {
		return numStateEvaluations;
	}

	@Override
	public Action getAction(GameConfig gameConfig, GameState gameState,
			Collection<Action> prohibitedActions, Player player) {
		throw new UnsupportedOperationException(
				"Prohibited actions not supported by this class.");
	}

	@Override
	public void setState(GameState gameState) {
		// TODO Auto-generated method stub
		
	}
}