package cs6601.p1.generator;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import cs6601.p1.GameConfig;
import cs6601.p1.GameState;
import cs6601.p1.StateEvaluator;

public class AlphaBetaMoveGenerator implements MoveGenerator {
	private static final Logger LOGGER = Logger.getLogger(AlphaBetaMoveGenerator.class.getName());
	private static final int DEFAULT_RECURSIVE_PLAYS = 3;
	private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator();
	
	private String bestPick = MoveGenerator.PASS;
	
	@Override
	public String genMove(GameConfig gameConfig, GameState gameState,
			String initialColor) {
		
		int alpha = Integer.MIN_VALUE;
		int beta = Integer.MAX_VALUE;
		
		if ("b".equals(initialColor)) {
			getMaxValue(gameConfig,gameState,initialColor,false,DEFAULT_RECURSIVE_PLAYS*2,alpha,beta);
			return bestPick;
		} else if ("w".equals(initialColor)) {
			getMinValue(gameConfig,gameState,initialColor,false,DEFAULT_RECURSIVE_PLAYS*2,alpha,beta);
			return bestPick;
		} else {
			return MoveGenerator.PASS;
		}
	}
	
	private int getMaxValue(GameConfig gameConfig, GameState gameState,
			String initialColor, boolean playAsOpponent, int recursionLevel, int alpha, int beta) {
		if (terminalTest(recursionLevel)) {
			return getUtility(gameConfig,gameState);
		}
		
		String colorPlaying = getColorToPlay(initialColor, playAsOpponent);
		
		List<String> validMoves = validMoveGenerator.genMoves(gameConfig,
				gameState, colorPlaying, MoveGenerator.ALL_MOVES);
		
		int value = Integer.MIN_VALUE;
		
		//Map<Integer,String> firstMovesByScore = new HashMap<Integer,String> ();
		
		for (String 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);
			//value = Math.max(value, minResult);
			if (minValue > value) {
				value = minValue;
				if (recursionLevel == DEFAULT_RECURSIVE_PLAYS * 2) {
					bestPick = nextMove;
				}
			}
			///
			//if (recursionLevel == 2 * DEFAULT_RECURSIVE_PLAYS) {
			//	firstMovesByScore.put(value, nextMove);
			//}
			///
			if (value >= beta) {
				return value;
			}
			alpha = Math.max(alpha,value);
		}
		
		return value;
	}
	
	private int getMinValue(GameConfig gameConfig, GameState gameState,
			String initialColor, boolean playAsOpponent, int recursionLevel, int alpha, int beta) {
		if (terminalTest(recursionLevel)) {
			return getUtility(gameConfig,gameState);
		}
		
		String colorPlaying = getColorToPlay(initialColor, playAsOpponent);
		
		List<String> validMoves = validMoveGenerator.genMoves(gameConfig,
				gameState, colorPlaying, MoveGenerator.ALL_MOVES);
		
		int value = Integer.MAX_VALUE;
		
		//Map<Integer,String> firstMovesByScore = new HashMap<Integer,String> ();
		
		for (String 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);
			//value = Math.min(value, maxValue);
			if (maxValue < value) {
				value = maxValue;
				if (recursionLevel == 2 * DEFAULT_RECURSIVE_PLAYS) {
					//firstMovesByScore.put(value, nextMove);
					bestPick = nextMove;
				}
			}
			///
			//if (recursionLevel == 2 * DEFAULT_RECURSIVE_PLAYS) {
			//	firstMovesByScore.put(value, nextMove);
			//}
			///
			if (value <= alpha) {
				return value;
			}
			beta = Math.min(beta,value);			
		}
		
		//if (recursionLevel == DEFAULT_RECURSIVE_PLAYS * 2) {
		//	bestPick = firstMovesByScore.get(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();
	}
	
	private String getColorToPlay(String color, boolean playAsOpponent) {
		if (playAsOpponent) {
			if ("w".equals(color)) {
				return "b";
			} else if ("b".equals(color)) {
				return "w";
			} else {
				return "?"; // invalid color will cause randomMoveGenerator to
							// PASS
			}
		} else {
			return color;
		}
	}
	
	/**
	 * AlphaBetaMoveGenerator2 does not support this method.
	 */
	@Override
	public List<String> genMoves(GameConfig gameConfig, GameState gameState,
			String color, int nMoves) {
		String[] pass = new String[] {PASS};
		LOGGER.info("Minimax genMoves() stub returning [PASS]");
		return Arrays.asList(pass);
	}
}
