package sema;

import java.util.Collection;
import java.util.TreeMap;
import java.util.Vector;



/**
 * Basic agent, designed to easily model specific behaviours.
 * Extends {@link Element} and contains the fundamental nested {@link Action} class.
 * This <code>Agent</code> class is the basic shape for every agent able to undertake actions.
 */
public class Agent extends Element {
    
    /**
     * Creates a new agent in the specified world, and registers it to this world.
     * The caracteristic delay is the delay of reaction of the agent, in simulated unit.
     * It's used as a default value when reacting to a stimulus or making an action.
     * @param world the world which it has to register to.
     * @param area the area of its body on the map.
     * @param delay the caracteristic delay.
     * @param image the main image of the element (also used as an identity icon).
     * @see Area
     */
    public Agent(World world, Area area, float delay, java.awt.Image image) {
	super(world,area,image);
	this.setDelay(delay*(1+(2*(float)Math.random()-1)/100)); //le délai de l'agent est changé de au plus 1%, pour éviter la simultanéïté
    }
    
    
    private float delay;
    /**
     * Returns the caracteristic delay of the agent.
     * It is typically the delay of reaction of the agent.
     * @return the caracteristic delay of the agent.
     */
    public float getDelay() {
	return delay;
    }
    /**
     * Sets the caracteristic delay of the agent.
     * This delay must be positive.
     * @param delay the caracteristic delay of the agent. (must be >=0)
     */
    protected void setDelay(float delay) {
	if (delay>=0)
	    this.delay = delay;
    }
    
    
    
    /**
     * Registers an action.
     * It is automatically called when a new action is created.
     * It is, by default, an empty method, but it can be overrided,
     * to give an overview of the actions to the agent.<br>
     * The agent can store all his current and planned actions,
     * which can be very usefull for the {@link #acceptAction(Agent.Action)}.
     * @param a the action to register.
     */
    protected void registerAction(Action a){
    }
    
    /**
     * Unregisters an action.
     * It is called automatically when an action is ended.
     * It is, by default, an empty method, but it can be overrided
     * to give an overview of the actions to the agent. @see sema.Agent#registerAction(sema.Agent.Action).
     * @param a the action to unregister.
     */
    protected void unregisterAction(Action a){
    }
    
    
    /**
     * Global internal heuristic method of decision as to the actions to undertake.
     * It is called before each call to an action to determinate whether it should be
     * executed. If it returns 'false', the {@link sema.Agent.Action#whenRefused()} method is called
     * and the action who called <code>acceptAction</code> is killed.
     * This method has an overwhelming power of decision on the action,
     * which cannot ignore this decision.
     * The default <code>acceptAction</code> method always returns true.
     * It's designed to provide an easy-to-use main decision function to manage actions.
     */
    protected boolean acceptAction(Action action){
	return true;
    }
    
    /**
     * Basic action of an agent. It provides a simple way to spread the execution of an action during simulated time.
     * In fact, the execution of a method is instantanious in the virtual world (necessary to disconnect virtual and real time, and to
     * allow agents to evolve simultanely). However, to be coherent from the point of view of the simulation, an action must so be divided into shorter steps.
     * The event is the smallest possible action to be executed by the engine.<br>
     * An <code>Action</code> is a super event repeting itself, but before every call to an action method
     * (that is: {@link #step()}, {@link #resumedStep()} or {@link #firstStep()}),
     * it checks whether it can execute. If not, the {@link #whenRefused()} method will be called,
     * and the action will end (no more event will be executed from this action).<br>
     * When an action is executing, the corresponding method will be called: {@link #firstStep()} if it is the first event of the action,
     * {@link #step()} if we are in the normal loop, and {@link #resumedStep()} if it wakes up from a paused state.<br>
     * You can pause (means: not executing any events or methods for this action) an action at anytime with {@link #pause()}, but before pausing,
     * the action will call {@link #whenPaused()} to prepare the pause.
     * You can resume an action with {@link #resume()}. It will return false if failed to resume, that is if the action is ended after the resume attempt.
     * If a resume is done when the action was paused, {@link #resumedStep()} is executed.
     *
     * This encapsulation of actions provides an easy way to define behaviours.
     * The advantages of the action are the possibility to easily undertake several actions at the same time
     * for each agent and the automatic management of the parrallelism and steps.
     *@see Agent#registerAction
     *@see Agent#unregisterAction
     */
    public class Action {
	
	/*
	 *Creates a new action, that will begin after a call to the start method.
	 */
	
	public Action() {
	    registerAction(this);
	}
	
	
	/**
	 * Creates a new action that will begin in t simulated units.
	 */
	public Action(double t) {
	    registerAction(this);
	    start(t);
	}
	
	/**
	 * Starts the action 'immediatly' (from the agent point of view).
	 * To be coherent with the simulation of the agent, it waits for 'delay' simulated units.
	 * Does nothing and returns false if already started, paused or stopped.
	 */
	public final boolean start() {
	    return start(delay);
	}
	
	/**
	 * Starts the action in t simulated unit.
	 * Does nothing and returns false if already started or stopped.
	 * In the special case where start is called as the action where previously paused,
	 * the whenStartInPause method is called.
	 */
	public final boolean start(double t) {
	    if (hasBegun() || isDisabled()) return false;
	    if (isPaused()) whenStartInPause();
	    else
		new FirstEvent(t);
	    return true;
	}
	
	private boolean disabled = false;
	/**
	 * Returns true if the action has been definitively disabled, by the
	 * stop() method, because the action ended normally, or because it was
	 * refused by @link accepAction(Agent.Action).
	 */
	public final boolean isDisabled(){
	    return disabled;
	}
	private void disable(){
	    disabled = true;
	}
	
	
	private boolean paused = false;
	/**
	 * Returns true if the action is currently paused.
	 */
	public final boolean isPaused(){
	    return paused;
	}
	private void setPaused(boolean b){
	    paused = b;
	}
	
	private boolean beginned = false;
	/**
	 * Returns true if the action has begun.
	 */
	public final boolean hasBegun(){
	    return beginned;
	}
	
	private Continue nextEvent;
	
	/*
	 * Premier événement de l'action (appelée automatiquement au début de la nouvelle action).
	 */
	private class FirstEvent extends ElementEvent {
	    public FirstEvent(double t){
		super(t);
	    }
	    public void whatToDo(){
		if (!isDisabled()) {
		    if (acceptAction(Action.this)){
			float m = firstStep();
			beginned = true;
			if (m>=0)
			    nextEvent= new Continue(m);
			else {
			    whenEnd(); //succesful end
			    end();
			}
		    } else {
			whenRefused(); //refused
			end();
		    }
		}
	    }
	}
	
	//Evénement utilisé en interne pour poursuivre l'action.
	private class Continue extends ElementEvent {
	    public Continue(double t){
		super(t);
	    }
	    
	    public void whatToDo(){
		if (!isDisabled()) {
		    
		    if (acceptAction(Action.this)){
			float m = step();
			if (m>=0) { //continue
			    this.date = new Double(date.doubleValue()+m);
			    world.engine.add(this);
			} else  {
			    whenEnd(); //succesful end
			    end();
			}
		    } else {
			whenRefused(); //refused
			end();
		    }
		    
		}
	    }
	}
	
	/*
	 * Termine l'action. Est toujours appellé après un retour négatif de action() ou actionwhenbegin() ou actionwhenresume() ou actionwhenrefused()
	 */
	private final void end(){
	    disable();	//empèche le retour de pause
	    close(); //execute the last action's code.
	    unregisterAction(this);
	}
	
	/**
	 * Pauses the action, which can be resumed with {@link #resume()}.
	 * The {@link #whenPaused()} method is called before it is effectively paused.
	 */
	public final void pause(){
	    if (hasBegun()) {
		whenPaused();  //code sans suite prépare la pause.
		if (nextEvent != null) {
		    nextEvent.delete();	//plus rien ne pourra se passer ans un apel à resume
		    nextEvent = null;
		}//nextEvent est null
	    }
	    setPaused(true);
	}
	
	private Wait waitEvent;
	
	/**
	 * Pauses the action for at most the given delay.
	 * @see #pause().
	 */
	public final void pause(double maxWaiting) {
	    whenPaused();  //code sans suite prépare la pause.
	    if (nextEvent != null) {
		nextEvent.delete();	//plus rien ne pourra se passer ans un apel à resume
		nextEvent = null;
	    }//nextEvent est null
	    setPaused(true);
	    waitEvent = new Wait(maxWaiting);
	}
	
	private class Wait extends ElementEvent {
	    public Wait(double t){
		super(t);
	    }
	    
	    public void whatToDo(){
		resume();
	    }
	}
	
	/**
	 * Stops the action and disables it.
	 * Before disabling, the {@link #whenStopped()} method is called.
	 */
	public final void stop(){
	    whenStopped(); //code sans suite prépare le end.
	    end();	//fin normale
	}
	
	/**
	 * Resumes an action previously paused.
	 * Returns true if the action is active after the call
	 * (even if it was already active before).
	 */
	public final boolean resume() {
	    
	    setPaused(false);
	    if (waitEvent != null) {
		waitEvent.delete();
		waitEvent = null;
	    }
	    
	    if (!hasBegun()) return false;
	    
	    if (!isDisabled() & !isPaused()) return true;
	    
	    if (!isDisabled()) {
		if (acceptAction(Action.this)){
		    float m = resumedStep();
		    if (m>=0) {
			nextEvent= new Continue(m);
			return true;
		    } else {
			whenEnd();
			end();
			return false;
		    }
		} else{
		    whenRefused();
		    end();
		    return false;
		}
	    } else return false;
	}
	
	
	/**
	 * Called at the begin of the action.
	 * By default, calls {@link #step()}.
	 */
	protected float firstStep(){
	    return step();
	}
	
	/**
	 * Called when the action is resumed.
	 * By default, calls {@link #step()}.
	 */
	protected float resumedStep(){
	    return step();
	}
	
	/**
	 * Called when the action is started with the start method, but was previously paused.
	 * By default, it performs the normal action of start.
	 * This is a technical method that should be ignored, excepted for special actions.
	 */
	protected void whenStartInPause(){
	}
	
	/**
	 * Called when the action is paused.
	 * By default, it does nothing.
	 */
	protected void whenPaused(){
	}
	
	
	/**
	 * Called when the action is going to be stopped, because of a
	 * call to the stop() method.
	 * The {@link #close()} method will be called after this one.
	 */
	protected void whenStopped(){
	}
	
	/**
	 * Called before stopping the action because it has been refused by
	 * {@link Agent#acceptAction(Agent.Action)}.
	 */
	protected void whenRefused(){
	}
	
	/**
	 * Called when a negative value has been returned by one of the step methods.
	 * This is the normal end of the action, that will generally mean that the goal
	 * has been reeached.
	 */
	protected void whenEnd() {
	    
	}
	
	/**
	 * Closing method, always executed as the last code of an action that has begun.
	 */
	protected void close(){
	}
	
	/**
	 * Body of the action. This is the main code to execute.
	 * The value returned is the time (in simulated units)
	 * to wait before the next call to the body.
	 * A negative value means that the action must end.
	 * The main idea of this function is that its execution is intended
	 * to be spread on a time that should be coherent with the simulation.<p>
	 * Typically, a moving agent that is walking would make a step towards a direction
	 * then another, and theses steps would be differents calls to step().
	 * Internal variables should be stocked in the action to allow a good step by step
	 * execution.
	 */
	protected float step(){
	    return 1;
	}
	
    }
}
