package net.woodyfolsom.msproj;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;

import net.woodyfolsom.msproj.policy.Policy;
import net.woodyfolsom.msproj.sgf.SGFLexer;
import net.woodyfolsom.msproj.sgf.SGFNodeCollection;
import net.woodyfolsom.msproj.sgf.SGFParser;

public class Referee {
	private Policy blackPolicy;
	private Policy whitePolicy;

	public Policy getPolicy(Player player) {
		if (Player.BLACK.equals(player)) {
			return blackPolicy;
		} else if (Player.WHITE.equals(player)) {
			return whitePolicy;
		} else {
			throw new IllegalArgumentException(
					"Player must be one of {Player.WHITE, Player.BLACK}.");
		}
	}

	public void setPolicy(Player player, Policy policy) {
		if (Player.BLACK.equals(player)) {
			blackPolicy = policy;
		} else if (Player.WHITE.equals(player)) {
			whitePolicy = policy;
		} else {
			throw new IllegalArgumentException(
					"Player must be one of {Player.WHITE, Player.BLACK}.");
		}
	}

	public static GameRecord replay(InputStream sgfInputStream) throws IOException, RecognitionException {
		ANTLRStringStream in = new ANTLRInputStream(sgfInputStream);
		SGFLexer lexer = new SGFLexer(in);
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		SGFParser parser = new SGFParser(tokens);
		SGFNodeCollection nodeCollection = parser.collection();
		
		//parse sgf header
		GameConfig gameConfig = nodeCollection.getGameConfig();
		GameRecord gameRecord = new GameRecord(gameConfig);
		
		//replay sgf moves, throw exception if moves are illegal
		for (Action action : nodeCollection.getMoves(gameConfig.getSize())) {
			gameRecord.play(gameRecord.getPlayerToMove(), action);
		}
		
		return gameRecord;
	}
	
	public GameResult play(GameConfig gameConfig) {
		GameRecord gameRecord = new GameRecord(gameConfig);
		
		System.out.println("Game started.");
		
		GameState initialGameState = gameRecord.getGameState(gameRecord.getNumTurns());
		
		//Allow policy / GUI to do setup.
		blackPolicy.setState(initialGameState);
		whitePolicy.setState(initialGameState);
		
		try {
			while (!gameRecord.isFinished()) {
				GameState gameState = gameRecord.getGameState(gameRecord.getNumTurns());
				System.out.println(gameState);

				Player playerToMove = gameRecord.getPlayerToMove();
				Policy policy = getPolicy(playerToMove);
				Action action = policy.getAction(gameConfig,
						gameState, playerToMove);
				
				if (gameRecord.play(playerToMove, action)) {
					policy.setState(gameRecord.getGameState(gameRecord.getNumTurns()));
				} else {
					System.out.println("Move rejected - try again.");
				}
			}
		} catch (Exception ex) {
			System.out
					.println("Game halted early due to the following Exception:");
			ex.printStackTrace();
			return GameResult.VOID;
		}

		GameResult result = gameRecord.getResult();

		System.out.println("Game over.  Result: " + result);

		DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmssZ");

		try {

			File sgfFile = new File("gogame-" + dateFormat.format(new Date())
					+ ".sgf");
			FileOutputStream fos = new FileOutputStream(sgfFile);
			try {
				SGFWriter.write(fos, gameRecord);
				System.out
						.println("Game saved as " + sgfFile.getAbsolutePath());
			} finally {
				try {
					fos.close();
				} catch (IOException ioe) {
					ioe.printStackTrace();
				}
			}
		} catch (IOException ioe) {
			System.out.println("Unable to save game file due to IOException: "
					+ ioe.getMessage());
		}

		System.out.println("Game finished.");

		return result;
	}
}
