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

import java.util.Map;

import com.google.inject.ImplementedBy;

import cz.cuni.amis.pogamut.base.agent.worldview.objects.IWorldObject;
import cz.cuni.amis.pogamut.base.agent.worldview.objects.IWorldObjectEvent;
import cz.cuni.amis.pogamut.base.agent.worldview.objects.IWorldObjectId;
import cz.cuni.amis.pogamut.base.communication.mediator.IMediatorOutput;
import cz.cuni.amis.pogamut.base.factory.guice.AgentScoped;
import cz.cuni.amis.pogamut.base3d.worldview.objects.IViewable;

/**
 * Interface for the world view.
 * <p><p>
 * It assumes that each world consists of two things:
 * <ol>
 * <li>events</li>
 * <li>objects</li>
 * </ol>
 * <p>
 * Possible event listeners:
 * <ol>
 * <li>general event</li>
 * <li>specific world object (of the specific id)</li>
 * <li>object category (according to interfaces / ancestor of the world object)</li>
 * </ol>
 * 
 * @author Jimmy
 */
@AgentScoped
@ImplementedBy(IStartableWorldView.class)
public interface IWorldView extends IMediatorOutput {
	
	// ======
	// EVENTS
	// ======
	
	/**
	 * Adds listener to a specified event. Note that the event listener must be able
	 * to handle events of the class 'event'. Note that listeners are stored as weak
	 * references so be sure that you have your own reference of listener.
	 * 
	 * @param event which events you want to receive
	 * @param listener where you want to handle those events
	 */
	public void addListener(Class<? extends IWorldEvent> event, WorldEventListener<? extends IWorldEvent> listener);

    /**
     * Adds listener that will be notified when object of given type first appears.
     * @param objectClass class of the object you are interested in
     * @param listener
     */
    public void addAppearedListener(Class<? extends IViewable> objectClass, WorldObjectEventListener<? extends IViewable> listener);

    /**
     * Adds listener that will be notified when object of given type disappears.
     * @param objectClass class of the object you are interested in
     * @param listener 
     */
    public void addDisappearedListener(Class<? extends IViewable> objectClass, WorldObjectEventListener<? extends IViewable> listener);

    /**
     * Adds listener that will be notified when object of given type is updated.
     * @param objectClass class of the object you are interested in
     * @param listener
     */
    public void addUpdatedListener(Class<? extends IWorldObject> objectClass, WorldObjectEventListener<? extends IWorldObject> listener);
	
    /**
	 * Adds listener for update on object of specified id and specified event.
	 * <p><p>
	 * Note that every implementation may introduce it's own events therefore if you want
	 * to know types of events you will receive consult JavaDoc of respective implementation of IWorldView interface.
	 * 
	 * @param objectId id of the object whose updates will be propagated to the listener
	 * @param event which events you want to receive
	 * @param listener listener on updates of the object
	 */
	public void addListener(IWorldObjectId objectId, Class<? extends IWorldObjectEvent> event, WorldEventListener<? extends IWorldObjectEvent> listener);
	
	/**
	 * Adds listener for all objects that implements or has ancestor of 'objectFeature'.
	 * @param objectFeature
	 * @param which events you want to receive for those objects
	 * @param listener
	 */
	@SuppressWarnings("unchecked")
	public void addListener(Class objectFeature, Class<? extends IWorldObjectEvent> event, WorldEventListener<? extends IWorldObjectEvent> listener);
		
	/**
	 * Checks whether this listener is hooked to the world view (via any addListener method).
	 * <p><p>
	 * May be time consuming...
	 * 
	 * @param listener
	 * @return
	 */
	public boolean isListening(WorldEventListener<? extends IWorldEvent> listener);
	
	/**
	 * Removes listener from every listeners category (event / object / object feature).
	 * <p><p>
	 * May be time consuming...
	 * 
	 * @param event
	 * @param listener
	 */
	public void removeListener(Class<? extends IWorldEvent> event, WorldEventListener<? extends IWorldEvent> listener);
		
	// =======
	// OBJECTS
	// =======
	
	/**
	 * Returns map of all objects that are present in the world view.
	 * <p><p>
	 * <b>WARNING:</b> the returned map is immutable! Read-only. 
	 */
	@SuppressWarnings("unchecked")
	public Map<Class, Map<IWorldObjectId, IWorldObject>> getAll();
	
	/**
	 * Returns map of all objects of a specific type that are present in the world view.
 	 * <p><p>
	 * <b>WARNING:</b> the returned map is immutable! Read-only. 
	 *  
	 * @param type
	 * @return
	 */
	public <T> Map<IWorldObjectId, T> getAll(Class<T> type);
	
}
