package net.woodyfolsom.msproj.policy;

import java.util.ArrayList;
import java.util.List;

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

public abstract class MonteCarlo implements Policy {
	protected Policy movePolicy;
	protected long searchTimeLimit;
	protected volatile long elapsedTime = 0L;
	
	public MonteCarlo(Policy movePolicy, long searchTimeLimit) {
		this.movePolicy = movePolicy;
		this.searchTimeLimit = searchTimeLimit;
	}
	
	/**
	 * Descend the tree from the specified node and return a list of nodes to grow.
	 * 
	 * @param node
	 * @return
	 */
	public abstract List<GameTreeNode> descend(GameTreeNode node);
	
	@Override
	public Action getAction(GameConfig gameConfig, GameState gameState,
			Player initialColor) {
		long startTime = System.currentTimeMillis();
		
		//If for some reason no moves are evaluated within the time limit, pass.
		//Note that this may lose the game by forfeit even when picking any random move could
		//result in a win.
		
		GameTreeNode rootNode = new GameTreeNode(gameState);
		
		do {
			//TODO these return types may need to be lists for some MC methods
			List<GameTreeNode> selectedNodes = descend(rootNode);
			List<GameTreeNode> newLeaves = new ArrayList<GameTreeNode>();
			
			for (GameTreeNode selectedNode: selectedNodes) {
				for (GameTreeNode newLeaf : grow(selectedNode)) {
					newLeaves.add(newLeaf);
				}
			}
			
			for (GameTreeNode newLeaf : newLeaves) {
				int reward = rollout(newLeaf);
				update(newLeaf, reward);
			}
			
			elapsedTime = System.currentTimeMillis() - startTime;
		} while (elapsedTime < searchTimeLimit);
		
		return getBestAction(rootNode);
	}
	
	public long getElapsedTime() {
		return elapsedTime;
	}
	
	public abstract Action getBestAction(GameTreeNode node);
	
	public abstract List<GameTreeNode> grow(GameTreeNode node);
	
	public abstract int rollout(GameTreeNode node);
	
	public abstract void update(GameTreeNode node, int reward);
	
	public long getSearchTimeLimit() {
		return searchTimeLimit;
	}
	
	public int doRollout() {
		return 0;
	}
}