package cz.cuni.amis.pogamut.ut2004.server.commands;

import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import cz.cuni.amis.pogamut.base.agent.worldview.IWorldView;
import cz.cuni.amis.pogamut.base.agent.worldview.WorldEventListener;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.DialogBegin;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.DialogCancel;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.DialogEnd;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.DialogItem;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.PlaySound;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.SetSendKeys;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.ShowText;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.DialogCommand;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.KeyEvent;
import cz.cuni.amis.pogamut.ut2004.server.AbstractUT2004Server;
import cz.cuni.amis.pogamut.ut2004.server.commands.dialog.Dialog;
import cz.cuni.amis.pogamut.ut2004.server.commands.dialog.DialogComponent;

/**
 * Class handling communication with player - e.g. displaying of text messages,
 * playing sounds, creating dialogues etc.
 * 
 * @author Radim Vansa <radim.vansa@matfyz.cz>
 */
public class PlayerCommunication extends ServerCommands {

	/**
	 * Dialog command listener
	 */
	protected class DialogCommandListener implements
			WorldEventListener<DialogCommand> {

		/**
		 * Constructor. Registers itself on the given WorldView object.
		 * 
		 * @param worldView
		 *            WorldView object to listen to.
		 */
		public DialogCommandListener(IWorldView worldView) {
			worldView.addListener(DialogCommand.class, this);
		}

		@Override
		public void notify(DialogCommand event) {
			Dialog dialog = dialogs.get(event.getId());
			if (dialog == null) {
				throw new IllegalArgumentException("No apropriate dialog found!");
			}
			dialog.setData(event.getData());
			if (dialog.processCommand(event.getSourceId(), event.getCommand())) {
				dialogs.remove(event.getId());
			}
		}

	}

	/**
	 * Key event listener
	 */
	protected class KeyEventListener implements WorldEventListener<KeyEvent> {

		/**
		 * Constructor. Registers itself on the given WorldView object.
		 * 
		 * @param worldView
		 *            WorldView object to listen to.
		 */
		public KeyEventListener(IWorldView worldView) {
			worldView.addListener(KeyEvent.class, this);
		}

		@Override
		public void notify(KeyEvent event) {
			List<IKeyEventListener> list = keyListenersMap.get(event
					.getPlayer());
			if (list == null) {
				return;
			}
			for (IKeyEventListener listener : list) {
				if (event.getAction().equalsIgnoreCase("PRESS")) {
					listener.keyPressed(event.getKey());
				} else if (event.getAction().equalsIgnoreCase("RELEASE")) {
					listener.keyReleased(event.getKey());
				} else if (event.getAction().equalsIgnoreCase("HOLD")) {
					listener.keyHeld(event.getKey());
				}
			}
		}

	}

	protected DialogCommandListener dialogCommandListener;

	protected KeyEventListener keyEventListener;

	protected Map<String, Dialog> dialogs = new HashMap<String, Dialog>();

	protected Map<String, List<IKeyEventListener>> keyListenersMap = new HashMap<String, List<IKeyEventListener>>();

	public PlayerCommunication(AbstractUT2004Server server, Logger log) {
		super(server, log);
		dialogCommandListener = new DialogCommandListener(server.getWorldView());
		keyEventListener = new KeyEventListener(server.getWorldView());
	}

	/**
	 * Displays a dialog on player's screen
	 * 
	 * @param dialog
	 */
	public void showDialog(Dialog dialog) {
		if (dialog.isDisplayed() || dialog.isFinished()) {
			throw new IllegalStateException("This dialog was already used.");
		}
		if (dialog.getTargetPlayer() == null) {
			throw new IllegalStateException("No player specified");
		}
		dialog.setPlayerCommunication(this);
		DialogBegin begin = dialog.createBeginCommand();
		server.getAct().act(begin);
		List<DialogItem> items = dialog.createItemCommands();
		for (DialogItem itemCommand : items) {
			itemCommand.setPlayer(dialog.getTargetPlayer());
			server.getAct().act(itemCommand);
		}
		DialogEnd end = dialog.createEndCommand();
		server.getAct().act(end);
		dialog.setDisplayed();
		dialogs.put(dialog.getId(), dialog);
	}

	/**
	 * Hides the dialog
	 * 
	 * @param dialog
	 */
	public void removeDialog(Dialog dialog) {
		DialogCancel dialogCancel = new DialogCancel();
		dialogCancel.setId(dialog.getId());
		dialogCancel.setPlayer(dialog.getTargetPlayer());
		server.getAct().act(dialogCancel);
		dialogs.remove(dialog.getId());
	}
	
	/**
	 * Updates currently displayed dialog
	 * 
	 * @param dialog
	 */
	public void updateDialog(Dialog dialog) {
		updateDialogComponent(dialog, dialog.getMainPanel());
	}
	
	/**
	 * Updates specific component of some displayed dialog
	 * 
	 * @param dialog
	 * @param component
	 */
	public void updateDialogComponent(Dialog dialog, DialogComponent component) {
		List<DialogItem> items = component.createItemCommands();
		for (DialogItem itemCommand : items) {
			itemCommand.setPlayer(dialog.getTargetPlayer());
			itemCommand.setDialogId(dialog.getId());
			itemCommand.setEffect("EDIT");
			server.getAct().act(itemCommand);
		}
	}

	/**
	 * Plays a sound to the player
	 * 
	 * @param playerName
	 *            Name of the affected player
	 * @param sound
	 *            Unreal reference to the sound, e.g.
	 *            "MyPackage.MySoundGroup.MySound". The sound has to be already
	 *            in some .uax file.
	 */
	public void playSound(String playerName, String sound) {
		PlaySound playSound = new PlaySound();
		playSound.setPlayer(playerName);
		playSound.setSound(sound);
		server.getAct().act(playSound);
	}

	/**
	 * Removes a text from player's screen
	 * 
	 * @param playerName
	 *            Name of the affected player
	 * @param message
	 *            Text of message that should disappear
	 */
	public void removeText(String playerName, String message) {
		ShowText showText = new ShowText();
		showText.setPlayer(playerName);
		showText.setText(message);
		showText.setShow(false);
		server.getAct().act(showText);
	}

	/**
	 * Adds a listener listening to player's key events
	 * 
	 * @param playerName
	 * @param listener
	 */
	public void addKeyEventListener(String playerName,
			IKeyEventListener listener) {
		List<IKeyEventListener> list = keyListenersMap.get(playerName);
		if (list == null) {
			list = new ArrayList<IKeyEventListener>();
			keyListenersMap.put(playerName, list);
		}
		if (list.size() == 0) {
			SetSendKeys setSendKeys = new SetSendKeys();
			setSendKeys.setPlayer(playerName);
			setSendKeys.setSend(true);
			server.getAct().act(setSendKeys);
		}
		list.add(listener);
	}

	/**
	 * Removes a listener listening to player's key events
	 * 
	 * @param playerName
	 * @param listener
	 */
	public void removeKeyEventListener(String playerName,
			IKeyEventListener listener) {
		List<IKeyEventListener> list = keyListenersMap.get(playerName);
		if (list == null) {
			return;
		}
		list.remove(listener);
		if (list.size() == 0) {
			SetSendKeys setSendKeys = new SetSendKeys();
			setSendKeys.setPlayer(playerName);
			setSendKeys.setSend(false);
			server.getAct().act(setSendKeys);
		}
	}

	/**
	 * Displays a text on top of screen in white color. The text will persist
	 * there until removed.
	 * 
	 * @param playerName
	 *            Name of the player who should see the message
	 * @param message
	 *            The message displayed
	 */
	public void showText(String playerName, String message) {
		showText(playerName, message, 0, new Color(255, 255, 255));
	}

	/**
	 * Displays a text on top of screen in white color.
	 * 
	 * @param playerName
	 *            Name of the player who should see the message
	 * @param message
	 *            The message displayed
	 * @param timeout
	 *            Amount of time (in seconds) after which the text disappears.
	 *            Zero means that the text will not disappear until removed.
	 */
	public void showText(String playerName, String message, double timeout) {
		showText(playerName, message, timeout, new Color(255, 255, 255));
	}

	/**
	 * Displays a text on top of screen
	 * 
	 * @param playerName
	 *            Name of the player who should see the message
	 * @param message
	 *            The message displayed
	 * @param timeout
	 *            Amount of time (in seconds) after which the text disappears.
	 *            Zero means that the text will not disappear until removed.
	 * @param color
	 *            Color in which will be the text printed
	 */
	public void showText(String playerName, String message, double timeout,
			Color color) {
		ShowText showText = new ShowText();
		showText.setPlayer(playerName);
		showText.setShow(true);
		showText.setText(message);
		showText.setTextColor(color);
		showText.setTime(timeout);
		server.getAct().act(showText);
	}
}
