package net.woodyfolsom.msproj;

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

import net.woodyfolsom.msproj.policy.Policy;

import org.apache.log4j.Logger;


public class GameState {
	private static final Logger LOGGER = Logger.getLogger(GameState.class.getName());
	
	private int blackPrisoners = 0;
	private int whitePrisoners = 0;
	private GameBoard gameBoard;

	public GameState(int size) {
		if (size < 1 || size > 19) {
			throw new IllegalArgumentException("Invalid board size: " + size);
		}
		gameBoard = new GameBoard(size);
		LOGGER.info("Created new GameBoard of size " + size);
	}

	public GameState(GameState that) {
		this.blackPrisoners = that.blackPrisoners;
		this.whitePrisoners = that.whitePrisoners;
		gameBoard = new GameBoard(that.gameBoard);
	}
	
	public void clearBoard() {
		blackPrisoners = 0;
		whitePrisoners = 0;
		gameBoard.clear();
	}

	public int getBlackPrisoners() {
		return blackPrisoners;
	}

	public List<String> getEmptyCoords() {
		List<String> emptyCoords = new ArrayList<String>();
		
		for (int colIndex = 0; colIndex < gameBoard.getSize(); colIndex++) {
			for (int rowIndex = 0; rowIndex < gameBoard.getSize(); rowIndex++) {
				if (GameBoard.EMPTY_INTERSECTION == gameBoard.getSymbolAt(colIndex, rowIndex));
					emptyCoords.add(GameBoard.getCoordinate(colIndex,rowIndex));
			}
		}
		
		return emptyCoords;
	}
	
	public GameBoard getGameBoard() {
		return gameBoard;
	}
	
	public int getWhitePrisoners() {
		return whitePrisoners;
	}

	public boolean playStone(String player, String coord) {
		//Opponent passes? Just ignore it.
		if (Policy.PASS.equalsIgnoreCase(coord)) {
			return true;
		}
		
		//LOGGER.info("Playing " + player + " at " + coord);
		
		char stoneColor;
		if ("b".equals(player)) {
			stoneColor = GameBoard.BLACK_STONE;
		} else if ("w".equals(player)) {
			stoneColor = GameBoard.WHITE_STONE;
		} else {
			return false;
		}
		
		char col = coord.charAt(0);
		int row = Integer.valueOf(coord.substring(1));
		
		//LOGGER.info("Playing " + stoneColor + " at " + col + row);
		return playStone(col,row, stoneColor);
	}
	/**
	 * Places a stone at the requested coordinate. Placement is legal if the
	 * coordinate is currently empty, has at least one liberty (empty neighbor),
	 * or at least one neighbor has this (recursive) property, or results in the
	 * capture and removal of at least one stone to produce a legal placement.
	 * Note also that placement is illegal if it would result in the capture of
	 * one's own group. Hence, placement must result in the capture of an
	 * adjacent stone or stones, if any.
	 * 
	 * @param colLabel
	 * @param row
	 * @param stone
	 * @return
	 */
	public boolean playStone(char colLabel, int rowNum, char stoneSymbol) {
		char currentStone = gameBoard.getSymbolAt(colLabel, rowNum);
		if (currentStone != GameBoard.EMPTY_INTERSECTION) {
			return false;
		}
		
		//Place stone as requested, then check for (1) captured neighbors and (2) illegal move due to 0 liberties.
		gameBoard.setSymbolAt(colLabel, rowNum, stoneSymbol);	
		
		//look for captured adjacent groups and increment the prisoner counter
		char opponentSymbol = GameBoard.getOpponentSymbol(stoneSymbol);
		
		int col = GameBoard.getColumnIndex(colLabel);
		int row = rowNum - 1;
		
		int prisonerCount = 0;
		if (col > 0 && gameBoard.getSymbolAt(col-1, row) == opponentSymbol) {
			int liberties = LibertyCounter.countLiberties(gameBoard, col-1, row, opponentSymbol,true);
			if (liberties == 0) {
				prisonerCount += gameBoard.replaceSymbol(GameBoard.MARKED_GROUP,GameBoard.EMPTY_INTERSECTION);
			} else {
				gameBoard.replaceSymbol(GameBoard.MARKED_GROUP, opponentSymbol);
			}
		}
		if (col < gameBoard.getSize() - 1 && gameBoard.getSymbolAt(col+1, row) == opponentSymbol) {
			int liberties = LibertyCounter.countLiberties(gameBoard, col+1, row, opponentSymbol,true);
			if (liberties == 0) {
				prisonerCount += gameBoard.replaceSymbol(GameBoard.MARKED_GROUP,GameBoard.EMPTY_INTERSECTION);
			} else {
				gameBoard.replaceSymbol(GameBoard.MARKED_GROUP, opponentSymbol);
			}
		}
		if (row > 0 && gameBoard.getSymbolAt(col, row-1) == opponentSymbol) {
			int liberties = LibertyCounter.countLiberties(gameBoard, col, row-1, opponentSymbol,true);
			if (liberties == 0) {
				prisonerCount += gameBoard.replaceSymbol(GameBoard.MARKED_GROUP,GameBoard.EMPTY_INTERSECTION);
			} else {
				gameBoard.replaceSymbol(GameBoard.MARKED_GROUP, opponentSymbol);
			}
		}
		if (row < gameBoard.getSize() - 1 && gameBoard.getSymbolAt(col, row+1) == opponentSymbol) {
			int liberties = LibertyCounter.countLiberties(gameBoard, col, row+1, opponentSymbol,true);
			if (liberties == 0) {
				prisonerCount += gameBoard.replaceSymbol(GameBoard.MARKED_GROUP,GameBoard.EMPTY_INTERSECTION);
			} else {
				gameBoard.replaceSymbol(GameBoard.MARKED_GROUP, opponentSymbol);
			}
		}
		
		if (stoneSymbol == GameBoard.BLACK_STONE) {
			blackPrisoners += prisonerCount;
		} else if (stoneSymbol == GameBoard.WHITE_STONE) {
			whitePrisoners += prisonerCount;
		}
		
		//Moved test for 0 liberties until after attempting to capture neighboring groups.
		if (0 == LibertyCounter.countLiberties(gameBoard, colLabel, rowNum, stoneSymbol)) {
			gameBoard.removeStone(colLabel,rowNum);
			return false;
		}
		return true;
	}
	
	public String toString() {
		int boardSize = gameBoard.getSize();
		StringBuilder sb = new StringBuilder("  ");
		for (int cIndex = 0; cIndex < boardSize; cIndex++) {
			sb.append(' ');
			if (cIndex < 'I'-'A') {
				sb.append((char)('A'+cIndex));
			} else {
				sb.append((char)('A' + cIndex + 1));
			}
		}
		//note the extra space
		sb.append(System.lineSeparator());
		for (int rIndex = boardSize - 1; rIndex >= 0; rIndex--) {
			if (rIndex < 9) {
				sb.append(' ');
			}
			sb.append(rIndex+1);
			for (int cIndex = 0; cIndex < boardSize; cIndex++) {
				sb.append(' ');
				sb.append(gameBoard.getSymbolAt(cIndex, rIndex));
			}
			sb.append(" " + (rIndex+1));
			if (rIndex == boardSize/2) {
				sb.append("     WHITE(O) has captured ");
			    sb.append(getWhitePrisoners());
			    sb.append(" stones");
			} else if (rIndex == boardSize/2 -1) {
				sb.append("     BLACK(X) has captured ");
				sb.append(getBlackPrisoners());
				sb.append(" stones");
			}
			sb.append(System.lineSeparator());
		}
		sb.append("  ");
		for (int cIndex = 0; cIndex < boardSize; cIndex++) {
			sb.append(' ');
			if (cIndex < 'I'-'A') {
				sb.append((char)('A'+cIndex));
			} else {
				sb.append((char)('A' + cIndex + 1));
			}
		}
		return sb.toString();
	}
}