package cz.cuni.amis.pogamut.base.agent;

import com.google.inject.Inject;

import cz.cuni.amis.pogamut.base.agent.exceptions.AgentException;
import cz.cuni.amis.pogamut.base.agent.worldview.IStartableWorldView;
import cz.cuni.amis.pogamut.base.agent.worldview.IWorldView;
import cz.cuni.amis.pogamut.base.communication.commands.Act;
import cz.cuni.amis.pogamut.base.communication.commands.IAct;
import cz.cuni.amis.pogamut.base.communication.commands.ICommandListener;
import cz.cuni.amis.pogamut.base.communication.commands.ICommandSerializer;
import cz.cuni.amis.pogamut.base.communication.exceptions.CommunicationException;
import cz.cuni.amis.pogamut.base.communication.messages.CommandMessage;
import cz.cuni.amis.pogamut.base.factory.guice.AgentScoped;
import cz.cuni.amis.pogamut.base.utils.logging.AgentLogger;

/**
 * Next step to embodied agents are those without the physical body
 * but with the ability to act inside the environment.
 * <p><p>
 * Example can be a world controller that is connected to the world gets all world
 * events (knows everything) and can alter the environment (sounds like a god, right? :-).
 * 
 * @author Jimmy
 */
@AgentScoped
public abstract class AbstractGhostAgent<WORLD_VIEW extends IStartableWorldView> extends AbstractObservingAgent<WORLD_VIEW> {
	
	/**
	 * Instance of the agent's will - provides means to act inside a world (act() method).
	 * <p><p>
	 * Accessible via getAct().
	 * <p><p>
	 * Note that your provided instance of the act can be obtained by calling getAct().getAct()
	 */	
	private AgentAct act;
	
	private ICommandSerializer commandSerializer;
	
	@Inject
	public AbstractGhostAgent(AgentLogger logger, WORLD_VIEW worldView, ICommandSerializer commandSerializer) {
		super(logger, worldView);
		this.commandSerializer = commandSerializer;
		this.act = new AgentAct(commandSerializer);
	}
	
	/**
	 * Decorator that removes the exception from the act() method making sending the
	 * commands more comfortable. If communication exception occurs it will terminateAgent().
	 * <p><p>
	 * It provides a getter for the original "act" not to destroy the inheritance.
	 * <p><p>
	 * Note that this is not a nice usage of design pattern but I didn't come up with
	 * better way to:
	 * <ul>
	 * <li>provide a comfortable (without exception) form of act</li>
	 * <li>seamlessly implement getAct()</li>
	 * <li>not to have another ugly method like getSafeAct() and getAct() along</li>
	 * <li>keeping the design divided to small classes</li>
	 * </ul>
	 * 
	 * @author Jimmy
	 *
	 * @param <MyAct>
	 */
	public class AgentAct implements IAct {
		
		private Act act;
		
		public AgentAct(ICommandSerializer serializer) {
			this.act = new Act(serializer, getLogger());
		}
		
		public Act getAct() {
			return act;
		}

		@Override
		public void act(CommandMessage command) {
			try {
				this.act.act(command);
			} catch (CommunicationException e) {
				e.logExceptionOnce(getLogger().platform());
				terminate(AgentStateType.FAILED, e.getMessage(), getAgentStopTimeoutMillis());
			}			
		}

		@Override
		public void addCommandListener(ICommandListener listener) {
			this.act.addCommandListener(listener);			
		}

		@Override
		public void removeCommandListener(ICommandListener listener) {
			this.act.removeCommandListener(listener);			
		}
		
	}

	/**
	 * Returns an object through which we may communicate with the world. Represents
	 * the agent effectors providing a low-level act() method. You have to instantiate
	 * CommandObject for yourself.
	 * @return
	 */
	public AgentAct getAct() {
		return act; 
	}
	
	@Override
	public void start() throws AgentException {
		try {
			commandSerializer.start();
		} catch (CommunicationException e) {
			throw new AgentException("Can't start the agent.", e, getLogger().platform(), this);
		}
		super.start();
	}

}
