/*
 *   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.othello.othelloDelegate;

import games.strategy.common.delegate.AbstractDelegate;
import games.strategy.engine.data.ChangeFactory;
import games.strategy.engine.data.CompositeChange;
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.engine.message.IRemote;
import games.strategy.grid.delegate.remote.IGridPlayDelegate;
import games.strategy.grid.ui.GridPlayData;
import games.strategy.grid.ui.IGridPlayData;
import games.strategy.grid.ui.display.IGridGameDisplay;

import java.util.Collection;
import java.util.HashSet;

/**
 * The play delegate for Othello. Also provides methods for checking plays used in OthelloMapPanel 
 */
public class PlayDelegate extends AbstractDelegate implements IGridPlayDelegate {

	public void start()
	{
		super.start();
		
		if (delegateCurrentlyRequiresUserInput())
		{
			final IGridGameDisplay display = (IGridGameDisplay) m_bridge.getDisplayChannelBroadcaster();
			display.setStatus(m_player.getName() + "'s turn. (Click to place stone)");
		}
	}
	
	/*
	 * return all captured territories for a given play and game state
	 */
	public static Collection<Territory> checkForCaptures(IGridPlayData play,
			PlayerID activePlayer, GameData mGameData) {

		HashSet<Territory> captures = new HashSet<>();
		Territory at = play.getStart();
		GameMap map = mGameData.getMap();

		// search in all directions
		for(int i=-1;i<=1;i++)
			for(int j=-1;j<=1;j++)
				if(i!=0||j!=0)
					captures.addAll(searchCaptures(at, i, j, activePlayer, map));

		return captures;
	}

	private static Collection<Territory> searchCaptures(Territory start,
			int dirX, int dirY, PlayerID activePlayer, GameMap map) {
		HashSet<Territory> captures = new HashSet<>();
		int i = 0;
		boolean validLine = false;
		while (true) { // search for lines of enemy stones in specified
						// direction
			i++;
			int xpos = start.getX() + dirX * i;
			int ypos = start.getY() + dirY * i;
			if (xpos < 0 || ypos < 0 || xpos >= map.getXDimension()
					|| ypos >= map.getYDimension())
				break;

			Territory currentPosition = map.getTerritoryFromCoordinates(xpos,
					ypos);
			if (currentPosition.getUnits().isEmpty())
				break;
			else {
				PlayerID stoneOwner=currentPosition.getUnits().getUnits().iterator().next().getOwner();
				if (stoneOwner.equals(activePlayer)) {
					validLine = true;
					break;
				} else {
					captures.add(currentPosition);
				}
			}

		}

		if (validLine)
			return captures;
		else
			return new HashSet<Territory>();
	}

	/*
	 * check if a given play is valid for the current game state
	 */
	public static String isValidPlay(IGridPlayData play, PlayerID activePlayer,
			GameData mGameData) {
		String error = null;
		if(!play.getStart().getUnits().isEmpty())
			error= "already a stone in this position";
		else if (checkForCaptures(play, activePlayer, mGameData).isEmpty()) {
			error = "no neighbouring lines of enemy stones";
		}
		return error;
	}
	
	public static Collection<IGridPlayData> getAllPossibleMoves(PlayerID activePlayer,GameData mGameData){
		HashSet<IGridPlayData> possibleMoves=new HashSet<>();
		
		for(Territory t:mGameData.getMap().getTerritories()){
			IGridPlayData play=new GridPlayData(t,activePlayer);
			if(isValidPlay(play,activePlayer,mGameData)==null){ //play is valid
				possibleMoves.add(play);
			}
		}
		
		return possibleMoves;
	}

	@Override
	public Class<? extends IRemote> getRemoteType() {
		return IGridPlayDelegate.class;
	}

	@Override
	public boolean delegateCurrentlyRequiresUserInput() {
		return true;
	}

	@Override
	public String play(IGridPlayData play) {
		
		if(play.isPass()){
			return null;
		}
		
		PlayerID me=play.getPlayerID();
		String error = isValidPlay(play, me, getData());
		if (error != null) //not a valid play
			return error;
		
		
		Collection<Territory> captures=checkForCaptures(play, me, m_bridge.getData());
		
		//write history
		m_bridge.getHistoryWriter().startEvent(play.toString());
		
		CompositeChange change = new CompositeChange();
		
		//add our new stone
		HashSet<Unit> unitSet=new HashSet<>();
		unitSet.add(getData().getUnitTypeList().getAllUnitTypes().iterator().next().create(m_player));
		change.add(ChangeFactory.addUnits(play.getStart(), unitSet));
		
		//flip captured stones
		for(Territory t: captures){
			Unit capturedStone=t.getUnits().getUnits().iterator().next();
			change.add(ChangeFactory.changeOwner(capturedStone, me, t));
		}
		
		m_bridge.addChange(change);
		
		return null;
	}

	@Override
	public void signalStatus(String status) {
		final IGridGameDisplay display = (IGridGameDisplay) m_bridge.getDisplayChannelBroadcaster();
		display.setStatus(status);

	}

}
