/*
 *   Author: Florian Fink, 2014
 *   Based on Code by: veqryn
 *   
 *   This project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version.
 *
 *   This project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License along with this project. If not, see http://www.gnu.org/licenses/.
 */

package usergames.mychess;

import games.strategy.engine.data.GameData;
import games.strategy.engine.data.GameMap;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.Territory;
import games.strategy.engine.data.Unit;
import games.strategy.grid.delegate.remote.IGridPlayDelegate;
import games.strategy.grid.player.GridAbstractAI;
import games.strategy.grid.ui.GridPlayData;
import games.strategy.grid.ui.IGridPlayData;

import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Random;

import mediocrechess.mediocre.board.Board;
import mediocrechess.mediocre.board.Move;
import mediocrechess.mediocre.engine.Engine;

/**
 * A Chess AI that uses the Mediocre Chess engine 
 * Based on HeuristicAI by veqryn
 */
public class CustomChessAI extends GridAbstractAI
{
	private Board board = new Board();
	
	public CustomChessAI(final String name, final String type)
	{
		super(name, type);
	}
	
	@Override
	protected void play()
	{
		// Unless the triplea.ai.pause system property is set to false,
		// pause for 0.8 seconds to give the impression of thinking
		pause();
		final PlayerID me = getPlayerID();
		final GameData data = getGameData();
		int searchDepth=data.getProperties().get("AI Search Depth", 5);
		
		// get our delegate for playing
		final IGridPlayDelegate playDel = (IGridPlayDelegate) this.getPlayerBridge().getRemoteDelegate();
		
		//set board
		String fen=mapToFen(data.getMap(),me);
		board.inputFen(fen);
		//move
		try {
			//get best move
			int bestmove = Engine.search(board, searchDepth, 0, 0, 0, false).line[0];
			//convert
			String moveString=Move.inputNotation(bestmove);
			String abc="abcdefgh";
			
			int fromCol=abc.indexOf(moveString.charAt(0));
			int fromRow=8-Integer.parseInt(moveString.substring(1, 2));
			int toCol=abc.indexOf(moveString.charAt(2));
			int toRow=8-Integer.parseInt(moveString.substring(3, 4));
			
			//do move
			Territory from=data.getMap().getTerritoryFromCoordinates(fromCol,fromRow);
			Territory to=data.getMap().getTerritoryFromCoordinates(toCol,toRow);
			
			doMove(from, to, data, playDel, me);
			
		} catch (IOException e) { //if something goes wrong, do a random move
			System.err.println("Mediocre could not find move; moving at random...");
			doRandomMove(data, playDel, me);
		}
		
		return;
	}
	
	/*
	 * Convert the current state of the board into a Fen string
	 */
	private String mapToFen(GameMap map, PlayerID currentPlayer){
		
		String fen="";
		for(int i=0;i<8;i++){
			String thisline="";
			int consecutiveEmptyTiles=0;
			
			for(int j=0;j<8;j++){
				
				Territory thistile=map.getTerritoryFromCoordinates(j,i);
				
				Iterator<Unit> unitsiterator=thistile.getUnits().getUnits().iterator();
				if(unitsiterator.hasNext()){
					if(consecutiveEmptyTiles>0)
						thisline=thisline+consecutiveEmptyTiles;
						
					//this is not an empty tile
					consecutiveEmptyTiles=0;
					
					Unit unit=unitsiterator.next();
					String unitName=unit.getType().getName();
					if(unitName.equals("knight"))
						unitName="n";
					
					thisline=thisline+(unit.getOwner().getName().equals("White")?unitName.toUpperCase():unitName).charAt(0);
				}
				else{
					//empty tile
					consecutiveEmptyTiles++;
				}
			}
			
			if(consecutiveEmptyTiles>0)
				thisline=thisline+consecutiveEmptyTiles;
			
			fen=fen+thisline+(i<7?"/":"");
		}
		
		//append current player
		fen=fen+" "+currentPlayer.getName().toLowerCase().charAt(0);
		
		
		return fen;
	}
	
	
	static final void doMove(final Territory start, final Territory end, final GameData data, final IGridPlayDelegate playDel, final PlayerID me)
	{
		String error;
		final IGridPlayData play = new GridPlayData(start, end, me);
		error = playDel.play(play);
		if (error != null)
		{
			System.err.println("Attempted Move Did Not Work: start: " + start.getName() + " end: " + end.getName());
			doRandomMove(data, playDel, me);
		}
	}
	
	static void doRandomMove(final GameData data, final IGridPlayDelegate playDel, final PlayerID me)
	{
		// Get the collection of territories from the map
		final Collection<Territory> territories = data.getMap().getTerritories();
		final Territory[] territoryArray = territories.toArray(new Territory[territories.size()]);
		final Random generator = new Random();
		int trymeStart;
		int trymeEnd;
		String error;
		// Randomly select a territory and try playing there
		// If that play isn't legal, try again
		do
		{
			trymeStart = generator.nextInt(territoryArray.length);
			trymeEnd = generator.nextInt(territoryArray.length);
			final IGridPlayData play = new GridPlayData(territoryArray[trymeStart], territoryArray[trymeEnd], me);
			error = playDel.play(play);
		} while (error != null);
	}
}
