package net.woodyfolsom.msproj;

import java.io.IOException;
import java.nio.charset.Charset;

import net.woodyfolsom.msproj.Command.TYPE;
import net.woodyfolsom.msproj.policy.AlphaBeta;
import net.woodyfolsom.msproj.policy.Minimax;
import net.woodyfolsom.msproj.policy.Policy;
import net.woodyfolsom.msproj.policy.MonteCarloUCT;
import net.woodyfolsom.msproj.policy.RandomMovePolicy;

import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;


public class GoGame {
	private static final int INVALID_MOVE_GENERATOR = 1;
	private static final Logger LOGGER = Logger.getLogger(GoGame.class
			.getName());
	
	private boolean shutDown = false;
	private GameConfig gameConfig = new GameConfig();
	private GameState gameState = new GameState(9);
	private Policy moveGenerator;
	
	public GoGame(Policy moveGenerator) {
		this.moveGenerator = moveGenerator;
	}
	
	public static void main(String[] args) {
		configureLogging();
		if (args.length == 0) {
			Policy defaultMoveGenerator = new MonteCarloUCT(new RandomMovePolicy(), 2000L);
			LOGGER.info("No MoveGenerator specified.  Using default: " + defaultMoveGenerator.getClass().getName());
			new GoGame(defaultMoveGenerator).play();
		} else {
			new GoGame(createPolicy(args[0])).play();
		}
	}

	private static Policy createPolicy(String policyName) {
		if ("random".equals(policyName)) {
			return new RandomMovePolicy();
		} else if ("minimax".equals(policyName)) {
			return new Minimax();
		} else if ("alphabeta".equals(policyName)) {
			return new AlphaBeta();
		} else if ("montecarlo".equals(policyName)) {
			return new MonteCarloUCT(new RandomMovePolicy(), 2000L);
		} else {
			LOGGER.info("Unable to create Policy for unsupported name: " + policyName);
			System.exit(INVALID_MOVE_GENERATOR);
			//This line will never be executed but prevents the compiler from complaining.
			return null;
		}
	}
	
	private void executeCommand(Command cmd) {
		switch (cmd.getType()) {
		case INVALID:
			LOGGER.info("Invalid command ignored: " + cmd.getText());
			System.out.println("? Invalid command: " + cmd.getText() + "\n");
			break;
		case boardsize:
			gameState = new GameState(cmd.getIntField(1));
			System.out.println("=\n");
			break;
		case clear_board:
			gameState.clearBoard();
			System.out.println("=\n");
			break;
		case final_status_list:
			LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState));
			System.out.println("=\n");
			break;
		case genmove:
			LOGGER.info("Generating move for:\n" + gameState);
			
			String player = cmd.getStringField(1);
			String nextMove = moveGenerator.getAction(gameConfig, gameState,
					player);
			
			gameState.playStone(player, nextMove);
			LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState));
			System.out.println("="
					+ nextMove.toLowerCase() + "\n");
			
			break;
		case komi:
			gameConfig.setKomi(cmd.getDoubleField(1));
			System.out.println("=\n");
			break;
		case list_commands:
			StringBuilder sb = new StringBuilder("= ");
			for (TYPE type : Command.TYPE.values()) {
				if (type != TYPE.INVALID) {
					sb.append(type.toString());
					sb.append("\n");
				}
			}
			System.out.println(sb.toString());
			break;
		case name:
			System.out.println("= CS6601P1\n");
			break;
		case quit:
			System.out.println("=\n");
			shutDown = true;
			break;
		case play:
			if (gameState.playStone(cmd.getStringField(1), cmd
					.getStringField(2).toUpperCase())) {
				System.out.println("=\n");
			} else {
				String errMsg = "Unable to make requested play: "
						+ cmd.getText();
				LOGGER.info(errMsg);
				LOGGER.info("GameState: " + gameState);
				System.out.println("?" + errMsg + "\n");
			}
			LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState));
			break;
		case version:
			System.out.println("= 0.1\n");
			break;
		default:
			LOGGER.warn("An error occured.  Unhandled command: " + cmd);
		}
	}

	public void play() {
		byte[] buf = new byte[128];
		int inputLength;
		try {
			while (!shutDown && (inputLength = System.in.read(buf)) > 0) {
				String commandText = new String(buf, 0, inputLength,
						Charset.forName("UTF-8"));
				LOGGER.info("Command received: " + commandText);
				executeCommand(CommandParser.parse(commandText));
			}
		} catch (IOException ioe) {
			LOGGER.warn(ioe.getMessage());
		}
	}

	private static void configureLogging() {
		DOMConfigurator.configure("log4j.xml");
	}
}