package net.woodyfolsom.msproj;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import net.woodyfolsom.msproj.gui.Goban;
import net.woodyfolsom.msproj.policy.HumanGuiInput;
import net.woodyfolsom.msproj.policy.HumanKeyboardInput;
import net.woodyfolsom.msproj.policy.MonteCarloAMAF;
import net.woodyfolsom.msproj.policy.MonteCarloUCT;
import net.woodyfolsom.msproj.policy.Policy;
import net.woodyfolsom.msproj.policy.RandomMovePolicy;
import net.woodyfolsom.msproj.policy.RootParallelization;

public class StandAloneGame {
	public static final int EXIT_USER_QUIT = 1;
	public static final int EXIT_NOMINAL = 0;
	public static final int EXIT_IO_EXCEPTION = -1;
	
	private int gameNo = 0;
	
	enum PLAYER_TYPE {
		HUMAN, HUMAN_GUI, ROOT_PAR, UCT, RANDOM, RAVE
	};

	public static void main(String[] args) throws IOException {
		try {
			GameSettings gameSettings = GameSettings
					.createGameSetings("data/gogame.cfg");
			System.out.println("Game Settings: " + gameSettings);
			System.out.println("Successfully parsed game settings.");
			new StandAloneGame().playGame(
					parsePlayerType(gameSettings.getPlayerOne()),
					parsePlayerType(gameSettings.getPlayerTwo()),
					gameSettings.getBoardSize(), gameSettings.getKomi(),
					gameSettings.getNumGames(), gameSettings.getTurnTime(),
					gameSettings.isSpectatorBoardShown(),
					gameSettings.isBlackMoveLogged(), gameSettings.isWhiteMoveLogged());
			System.out.println("Press <Enter> or CTRL-C to exit");
			System.in.read(new byte[80]);
		} catch (IOException ioe) {
			ioe.printStackTrace();
			System.exit(EXIT_IO_EXCEPTION);
		}
		System.exit(EXIT_NOMINAL);
	}

	private static PLAYER_TYPE parsePlayerType(String playerTypeStr) {
		if ("UCT".equalsIgnoreCase(playerTypeStr)) {
			return PLAYER_TYPE.UCT;
		} else if ("ROOT_PAR".equalsIgnoreCase(playerTypeStr)) {
			return PLAYER_TYPE.ROOT_PAR;
		} else if ("HUMAN".equalsIgnoreCase(playerTypeStr)) {
			return PLAYER_TYPE.HUMAN;
		} else if ("HUMAN_GUI".equalsIgnoreCase(playerTypeStr)) {
			return PLAYER_TYPE.HUMAN_GUI;
		} else if ("RANDOM".equalsIgnoreCase(playerTypeStr)) {
			return PLAYER_TYPE.RANDOM;
		} else if ("RAVE".equalsIgnoreCase(playerTypeStr)) {
			return PLAYER_TYPE.RAVE;
		} else {
			throw new RuntimeException("Unknown player type: " + playerTypeStr);
		}
	}

	public void playGame(PLAYER_TYPE playerType1, PLAYER_TYPE playerType2,
			int size, double komi, int rounds, long turnLength, boolean showSpectatorBoard,
			boolean blackMoveLogged, boolean whiteMoveLogged) {

		long startTime = System.currentTimeMillis();

		GameConfig gameConfig = new GameConfig(size);
		gameConfig.setKomi(komi);

		Referee referee = new Referee();
		referee.setPolicy(Player.BLACK,
				getPolicy(playerType1, gameConfig, Player.BLACK, turnLength, blackMoveLogged));
		referee.setPolicy(Player.WHITE,
				getPolicy(playerType2, gameConfig, Player.WHITE, turnLength, whiteMoveLogged));

		List<GameResult> round1results = new ArrayList<GameResult>();
		
		boolean logGameRecords = rounds <= 50;
		for (int round = 0; round < rounds; round++) {
			gameNo++;
			round1results.add(referee.play(gameConfig, gameNo, showSpectatorBoard, logGameRecords));
		}

		List<GameResult> round2results = new ArrayList<GameResult>();

		referee.setPolicy(Player.BLACK,
				getPolicy(playerType2, gameConfig, Player.BLACK, turnLength, blackMoveLogged));
		referee.setPolicy(Player.WHITE,
				getPolicy(playerType1, gameConfig, Player.WHITE, turnLength, whiteMoveLogged));
		for (int round = 0; round < rounds; round++) {
			gameNo++;
			round2results.add(referee.play(gameConfig, gameNo, showSpectatorBoard, logGameRecords));
		}

		long endTime = System.currentTimeMillis();
		DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmss");
		try {

			File txtFile = new File("gotournament-"
					+ dateFormat.format(new Date()) + ".txt");
			FileWriter writer = new FileWriter(txtFile);

			try {
				if (!logGameRecords) {
					System.out.println("Each player is set to play more than 50 rounds as each color; omitting individual game .sgf log file output.");
				}
				
				logResults(writer, round1results, playerType1.toString(),
							playerType2.toString());
				logResults(writer, round2results, playerType2.toString(),
							playerType1.toString());
				writer.write("Elapsed Time: " + (endTime - startTime) / 1000.0
						+ " seconds.");
				System.out.println("Game tournament saved as "
						+ txtFile.getAbsolutePath());
			} finally {
				try {
					writer.close();
				} catch (IOException ioe) {
					ioe.printStackTrace();
				}
			}
		} catch (IOException ioe) {
			System.out.println("Unable to save game file due to IOException: "
					+ ioe.getMessage());
		}
	}

	private void logResults(FileWriter writer, List<GameResult> results,
			String player1, String player2) throws IOException {
		String header = "Cumulative results for " + results.size()
				+ " games (BLACK=" + player1 + ", WHITE=" + player2 + ")";

		System.out.println(header);
		writer.write(header);
		writer.write("\n");

		for (int i = 0; i < results.size(); i++) {
			String resultLine = (i + 1) + ". " + results.get(i);
			System.out.println(resultLine);
			writer.write(resultLine);
			writer.write("\n");
		}
		writer.flush();
	}

	private Policy getPolicy(PLAYER_TYPE playerType, GameConfig gameConfig,
			Player player, long turnLength, boolean moveLogged) {
		switch (playerType) {
		case HUMAN:
			return new HumanKeyboardInput();
		case HUMAN_GUI:
			return new HumanGuiInput(new Goban(gameConfig, player,""));
		case ROOT_PAR:
			return new RootParallelization(4, turnLength);
		case UCT:
			return new MonteCarloUCT(new RandomMovePolicy(), turnLength);
		case RANDOM:
			RandomMovePolicy randomMovePolicy = new RandomMovePolicy();
			randomMovePolicy.setLogging(moveLogged);
			return randomMovePolicy;
		case RAVE:
			return new MonteCarloAMAF(new RandomMovePolicy(), turnLength);
		default:
			throw new IllegalArgumentException("Invalid PLAYER_TYPE: "
					+ playerType);
		}
	}

}