diff --git a/AMAKFX/README.md b/AMAKFX/README.md
index 9bf1ac1c346140eee3787661c2c3da214377c7b5..4e966b1594a18ffbee697324a727d544130e40e3 100644
--- a/AMAKFX/README.md
+++ b/AMAKFX/README.md
@@ -1,13 +1,28 @@
-# This is the README from the old version of AMAK, some part may be outdated ! #
+# This is a fork of AMAK, now using JavaFX instead of Swing #
 [The original repo](https://bitbucket.org/perlesa/amak/overview)
 
-This repository contains a framework made to facilitate the development of multi-agent system. 
 
-Examples are available in the package fr.irit.smac.amak.examples.
 
-# [Click here to download the latest standalone version](https://bitbucket.org/perlesa/amak/raw/master/Release/AmakFramework-standalone.jar?at=master) #
+## AMAKFX changes (2019-07-25) :
+These changes were made for a particular project, without much care for backward compatibility. But the overall philosophy should be the same.
 
-# Recent changes #
+### Possible breaking changes :
++ Anything related with the GUI in Swing.
++ Support for asynchronous agent has been dropped, you may still use then, but no promise are made for the visualization.
+
+### Changes :
++ Build system is now maven.
++ Use JavaFX instead of Swing. JavaFX run on a different thread than the simulation, when updating the GUI from the simulation thread you must be extra careful for :
+  + Running JavaFX code on the JavaFX thread, by only using AMAKFX methods, or by learning how to use JavaFX's RunLater (see AMAKFX's [RunLaterHelper](src/fr/irit/smac/amak/tools/RunLaterHelper.java))
+  + Not overload JavaFX call queue with draw call, leading to unpredictable result. For that purpose a new option has been added to Configuration : waitForGui, default at true. Each simulation cycle, AMAK will wait for JavaFX call queue to be empty. But queue overload can still happen inside a cycle. Symptoms are : long freeze, part of GUI not updating, or becoming white.
++ The VUI has been overhauled, it now provide extra features :
+  + Drawable can detect events, and dispatch these events to linked drawables.
+  + VUI Explorer, a sided bar that show a list of visible Drawable in its VUI. Features : hovering an element in the VUI Explorer highlight it in the VUI. Search for element in the VUI Explorer with regex. Click an element to display additional info (when available).
++ Plotting is now done with JFreeChart, AMAKFX provide an helper class : AmakPlot.
++ Tabs can be drag-n-dropped to rearrange them. Dropping a tab outside the tab bar will open it in a new window (despite the mouse cursor showing it's impossible)
++ Changes on how logging work, the a Log object now accept multiple action. Added the Loggable interface,with default methods, allowing a class to easily log to a file.
+
+# Old AMAK README #
 
 ## 1.5.3 (11/28/2018) ##
 ### New features: none
diff --git a/AMAKFX/src/fr/irit/smac/amak/Agent.java b/AMAKFX/src/fr/irit/smac/amak/Agent.java
index 9685e065123e3ae277e7482e37e39789ce88c9f1..1de07ab93413f5fb8d9581c27fe6955f3862d71d 100644
--- a/AMAKFX/src/fr/irit/smac/amak/Agent.java
+++ b/AMAKFX/src/fr/irit/smac/amak/Agent.java
@@ -1,496 +1,517 @@
-package fr.irit.smac.amak;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import fr.irit.smac.amak.Amas.ExecutionPolicy;
-import fr.irit.smac.amak.tools.Log;
-
-/**
- * This class must be overridden by all agents
- * 
- * @author Alexandre Perles
- *
- * @param <A>
- *            The kind of Amas the agent refers to
- * @param <E>
- *            The kind of Environment the agent AND the Amas refer to
- */
-public abstract class Agent<A extends Amas<E>, E extends Environment> implements Runnable {
-	/**
-	 * Neighborhood of the agent (must refer to the same couple amas, environment
-	 */
-	protected final List<Agent<A, E>> neighborhood;
-	/**
-	 * Criticalities of the neighbors (and it self) as perceived at the beginning of
-	 * the agent's cycle
-	 */
-	protected final Map<Agent<A, E>, Double> criticalities = new HashMap<>();
-	/**
-	 * Last calculated criticality of the agent
-	 */
-	private double criticality;
-	/**
-	 * Amas the agent belongs to
-	 */
-	protected final A amas;
-	/**
-	 * Unique index to give unique id to each agent
-	 */
-	private static int uniqueIndex;
-
-	/**
-	 * The id of the agent
-	 */
-	private final int id;
-	/**
-	 * The order of execution of the agent as computed by
-	 * {@link Agent#_computeExecutionOrder()}
-	 */
-	private int executionOrder;
-	/**
-	 * The parameters that can be user in the initialization process
-	 * {@link Agent#onInitialization()}
-	 */
-	protected Object[] params;
-
-	/**
-	 * These phases are used to synchronize agents on phase
-	 * 
-	 * @see fr.irit.smac.amak.Amas.ExecutionPolicy
-	 * @author perles
-	 *
-	 */
-	public enum Phase {
-		/**
-		 * Agent is perceiving
-		 */
-		PERCEPTION,
-		/**
-		 * Agent is deciding and acting
-		 */
-		DECISION_AND_ACTION,
-		/**
-		 * Agent haven't started to perceive, decide or act
-		 */
-		INITIALIZING,
-		/**
-		 * Agent is ready to decide
-		 */
-		PERCEPTION_DONE,
-		/**
-		 * Agent is ready to perceive or die
-		 */
-		DECISION_AND_ACTION_DONE
-	}
-
-	/**
-	 * The current phase of the agent {@link Phase}
-	 */
-	protected Phase currentPhase = Phase.INITIALIZING;
-	
-	private boolean synchronous = true;
-
-	/**
-	 * The constructor automatically add the agent to the corresponding amas and
-	 * initialize the agent
-	 * 
-	 * @param amas
-	 *            Amas the agent belongs to
-	 * @param params
-	 *            The params to initialize the agent
-	 */
-	public Agent(A amas, Object... params) {
-		this.id = uniqueIndex++;
-		this.params = params;
-		this.amas = amas;
-		neighborhood = new ArrayList<>();
-		neighborhood.add(this);
-		onInitialization();
-		if (!Configuration.commandLineMode)
-			onRenderingInitialization();
-		
-
-		if (amas != null) {
-			this.amas._addAgent(this);
-		}
-	}
-
-	/**
-	 * Add neighbors to the agent
-	 * 
-	 * @param agents
-	 *            The list of agent that should be considered as neighbor
-	 */
-	@SafeVarargs
-	public final void addNeighbor(Agent<A, E>... agents) {
-		for (Agent<A, E> agent : agents) {
-			if (agent != null) {
-				neighborhood.add(agent);
-				criticalities.put(agent, Double.NEGATIVE_INFINITY);
-			}
-		}
-	}
-
-	/**
-	 * This method must be overridden by the agents. This method shouldn't make any
-	 * calls to internal representation an agent has on its environment because
-	 * these information maybe outdated.
-	 * 
-	 * @return the criticality at a given moment
-	 */
-	protected double computeCriticality() {
-		return Double.NEGATIVE_INFINITY;
-	}
-
-	protected void setAsynchronous() {
-		if (currentPhase != Phase.INITIALIZING)
-			Log.defaultLog.fatal("AMAK", "Asynchronous mode must be set during the initialization");
-		this.synchronous = false;
-	}
-	/**
-	 * This method must be overriden if you need to specify an execution order layer
-	 * 
-	 * @return the execution order layer
-	 */
-	protected int computeExecutionOrderLayer() {
-		return 0;
-	}
-
-	/**
-	 * This method is called at the beginning of an agent's cycle
-	 */
-	protected void onAgentCycleBegin() {
-
-	}
-
-	/**
-	 * This method is called at the end of an agent's cycle
-	 */
-	protected void onAgentCycleEnd() {
-
-	}
-
-	/**
-	 * This method corresponds to the perception phase of the agents and must be
-	 * overridden
-	 */
-	protected void onPerceive() {
-
-	}
-
-	/**
-	 * This method corresponds to the decision phase of the agents and must be
-	 * overridden
-	 */
-	protected void onDecide() {
-
-	}
-
-	/**
-	 * This method corresponds to the action phase of the agents and must be
-	 * overridden
-	 */
-	protected void onAct() {
-
-	}
-
-	/**
-	 * In this method the agent should expose some variables with its neighbor
-	 */
-	protected void onExpose() {
-
-	}
-
-	/**
-	 * This method should be used to update the representation of the agent for
-	 * example in a VUI
-	 */
-	public void onUpdateRender() {
-
-	}
-
-	/**
-	 * This method is now deprecated and should be replaced by onUpdateRender
-	 * 
-	 * @deprecated Must be replaced by {@link #onUpdateRender()}
-	 */
-	@Deprecated
-	protected final void onDraw() {
-
-	}
-
-	/**
-	 * Called when all initial agents have been created and are ready to be started
-	 */
-	protected void onReady() {
-
-	}
-
-	/**
-	 * Called by the framework when all initial agents have been created and are
-	 * almost ready to be started
-	 */
-	protected final void _onBeforeReady() {
-		criticality = computeCriticality();
-		executionOrder = _computeExecutionOrder();
-	}
-
-	/**
-	 * Called before all agents are created
-	 */
-	protected void onInitialization() {
-
-	}
-
-	/**
-	 * Replaced by onInitialization
-	 * 
-	 * @deprecated Must be replaced by {@link #onInitialization()}
-	 */
-	@Deprecated
-	protected final void onInitialize() {
-
-	}
-
-	/**
-	 * Called to initialize the rendering of the agent
-	 */
-	protected void onRenderingInitialization() {
-
-	}
-
-	/**
-	 * @deprecated This method is useless because the state of the agent is not
-	 *             supposed to evolve before or after its cycle. Use
-	 *             OnAgentCycleBegin/End instead.
-	 * 
-	 *             This method is final because it must not be implemented.
-	 *             Implement it will have no effect.
-	 */
-	@Deprecated
-	protected final void onSystemCycleBegin() {
-
-	}
-
-	/**
-	 * @deprecated This method is useless because the state of the agent is not
-	 *             supposed to evolve before or after its cycle. Use
-	 *             OnAgentCycleBegin/End instead.
-	 * 
-	 *             This method is final because it must not be implemented.
-	 *             Implement it will have no effect.
-	 */
-	@Deprecated
-	protected final void onSystemCycleEnd() {
-
-	}
-
-	/**
-	 * This method is called automatically and corresponds to a full cycle of an
-	 * agent
-	 */
-	@Override
-	public void run() {
-		ExecutionPolicy executionPolicy = amas.getExecutionPolicy();
-		if (executionPolicy == ExecutionPolicy.TWO_PHASES) {
-
-			currentPhase = nextPhase();
-			switch (currentPhase) {
-			case PERCEPTION:
-				phase1();
-				amas.informThatAgentPerceptionIsFinished();
-				break;
-			case DECISION_AND_ACTION:
-				phase2();
-				amas.informThatAgentDecisionAndActionAreFinished();
-				break;
-			default:
-				Log.defaultLog.fatal("AMAK", "An agent is being run in an invalid phase (%s)", currentPhase);
-			}
-		} else if (executionPolicy == ExecutionPolicy.ONE_PHASE) {
-			onePhaseCycle();
-			amas.informThatAgentPerceptionIsFinished();
-			amas.informThatAgentDecisionAndActionAreFinished();
-		}
-	}
-	public void onePhaseCycle() {
-		currentPhase = Phase.PERCEPTION;
-		phase1();
-		currentPhase = Phase.DECISION_AND_ACTION;
-		phase2();
-	}
-	/**
-	 * This method represents the perception phase of the agent
-	 */
-	protected final void phase1() {
-		onAgentCycleBegin();
-		perceive();
-		currentPhase = Phase.PERCEPTION_DONE;
-	}
-
-	/**
-	 * This method represents the decisionAndAction phase of the agent
-	 */
-	protected final void phase2() {
-		decideAndAct();
-		executionOrder = _computeExecutionOrder();
-		onExpose();
-		if (!Configuration.commandLineMode)
-			onUpdateRender();
-		onAgentCycleEnd();
-		currentPhase = Phase.DECISION_AND_ACTION_DONE;
-	}
-
-	/**
-	 * Determine which phase comes after another
-	 * 
-	 * @return the next phase
-	 */
-	private Phase nextPhase() {
-		switch (currentPhase) {
-		case PERCEPTION_DONE:
-			return Phase.DECISION_AND_ACTION;
-		case INITIALIZING:
-		case DECISION_AND_ACTION_DONE:
-		default:
-			return Phase.PERCEPTION;
-		}
-	}
-
-	/**
-	 * Compute the execution order from the layer and a random value. This method
-	 * shouldn't be overridden.
-	 * 
-	 * @return A number used by amak to determine which agent executes first
-	 */
-	protected int _computeExecutionOrder() {
-		return computeExecutionOrderLayer() * 10000 + amas.getEnvironment().getRandom().nextInt(10000);
-	}
-
-	/**
-	 * Perceive, decide and act
-	 */
-	void perceive() {
-		for (Agent<A, E> agent : neighborhood) {
-			criticalities.put(agent, agent.criticality);
-		}
-		onPerceive();
-		// Criticality of agent should be updated after perception AND after action
-		criticality = computeCriticality();
-		criticalities.put(this, criticality);
-	}
-
-	/**
-	 * A combination of decision and action as called by the framework
-	 */
-	private final void decideAndAct() {
-		onDecideAndAct();
-
-		criticality = computeCriticality();
-	}
-
-	/**
-	 * Decide and act These two phases can often be grouped
-	 */
-	protected void onDecideAndAct() {
-		onDecide();
-		onAct();
-	}
-
-	/**
-	 * Convenient method giving the most critical neighbor at a given moment
-	 * 
-	 * @param includingMe
-	 *            Should the agent also consider its own criticality
-	 * @return the most critical agent
-	 */
-	protected final Agent<A, E> getMostCriticalNeighbor(boolean includingMe) {
-		List<Agent<A, E>> criticalest = new ArrayList<>();
-		double maxCriticality = Double.NEGATIVE_INFINITY;
-
-		if (includingMe) {
-			criticalest.add(this);
-			maxCriticality = criticalities.getOrDefault(criticalest, Double.NEGATIVE_INFINITY);
-		}
-		for (Entry<Agent<A, E>, Double> e : criticalities.entrySet()) {
-			if (e.getValue() > maxCriticality) {
-				criticalest.clear();
-				maxCriticality = e.getValue();
-				criticalest.add(e.getKey());
-			} else if (e.getValue() == maxCriticality) {
-				criticalest.add(e.getKey());
-			}
-		}
-		if (criticalest.isEmpty())
-			return null;
-		return criticalest.get(getEnvironment().getRandom().nextInt(criticalest.size()));
-	}
-
-	/**
-	 * Get the latest computed execution order
-	 * 
-	 * @return the execution order
-	 */
-	public int getExecutionOrder() {
-		return executionOrder;
-	}
-
-	/**
-	 * Getter for the AMAS
-	 * 
-	 * @return the amas
-	 */
-	public A getAmas() {
-		return amas;
-	}
-
-	/**
-	 * Remove the agent from the system
-	 */
-	public void destroy() {
-		getAmas()._removeAgent(this);
-	}
-
-	/**
-	 * Agent toString
-	 */
-	@Override
-	public String toString() {
-		return String.format("Agent #%d", id);
-	}
-
-	/**
-	 * Getter for the current phase of the agent
-	 * 
-	 * @return the current phase
-	 */
-	public Phase getCurrentPhase() {
-		return currentPhase;
-	}
-
-	/**
-	 * Return the id of the agent
-	 * 
-	 * @return the id of the agent
-	 */
-	public int getId() {
-		return id;
-	}
-
-	/**
-	 * Getter for the environment
-	 * 
-	 * @return the environment
-	 */
-	public E getEnvironment() {
-		return getAmas().getEnvironment();
-	}
-
-	public boolean isSynchronous() {
-		return synchronous ;
-	}
-}
+package fr.irit.smac.amak;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import fr.irit.smac.amak.Amas.ExecutionPolicy;
+import fr.irit.smac.amak.tools.Log;
+import fr.irit.smac.amak.ui.AmasMultiUIWindow;
+
+/**
+ * This class must be overridden by all agents
+ * 
+ * @author Alexandre Perles
+ *
+ * @param <A>
+ *            The kind of Amas the agent refers to
+ * @param <E>
+ *            The kind of Environment the agent AND the Amas refer to
+ */
+public abstract class Agent<A extends Amas<E>, E extends Environment> implements Runnable {
+	
+	
+	/**
+	 * Neighborhood of the agent (must refer to the same couple amas, environment
+	 */
+	protected final List<Agent<A, E>> neighborhood;
+	/**
+	 * Criticalities of the neighbors (and it self) as perceived at the beginning of
+	 * the agent's cycle
+	 */
+	protected final Map<Agent<A, E>, Double> criticalities = new HashMap<>();
+	/**
+	 * Last calculated criticality of the agent
+	 */
+	private double criticality;
+	/**
+	 * Amas the agent belongs to
+	 */
+	protected final A amas;
+	/**
+	 * Unique index to give unique id to each agent
+	 */
+	private static int uniqueIndex;
+
+	/**
+	 * The id of the agent
+	 */
+	private final int id;
+	/**
+	 * The order of execution of the agent as computed by
+	 * {@link Agent#_computeExecutionOrder()}
+	 */
+	private int executionOrder;
+	/**
+	 * The parameters that can be user in the initialization process
+	 * {@link Agent#onInitialization()}
+	 */
+	protected Object[] params;
+
+	/**
+	 * These phases are used to synchronize agents on phase
+	 * 
+	 * @see fr.irit.smac.amak.Amas.ExecutionPolicy
+	 * @author perles
+	 *
+	 */
+	public enum Phase {
+		/**
+		 * Agent is perceiving
+		 */
+		PERCEPTION,
+		/**
+		 * Agent is deciding and acting
+		 */
+		DECISION_AND_ACTION,
+		/**
+		 * Agent haven't started to perceive, decide or act
+		 */
+		INITIALIZING,
+		/**
+		 * Agent is ready to decide
+		 */
+		PERCEPTION_DONE,
+		/**
+		 * Agent is ready to perceive or die
+		 */
+		DECISION_AND_ACTION_DONE
+	}
+
+	/**
+	 * The current phase of the agent {@link Phase}
+	 */
+	protected Phase currentPhase = Phase.INITIALIZING;
+	
+	private boolean synchronous = true;
+
+	/**
+	 * The constructor automatically add the agent to the corresponding amas and
+	 * initialize the agent
+	 * 
+	 * @param amas
+	 *            Amas the agent belongs to
+	 * @param params
+	 *            The params to initialize the agent
+	 */
+	public Agent(A amas, Object... params) {
+		this.id = uniqueIndex++;
+		this.params = params;
+		this.amas = amas;
+		neighborhood = new ArrayList<>();
+		neighborhood.add(this);
+		onInitialization();
+		if (!Configuration.commandLineMode)
+			onRenderingInitialization();
+		
+
+		if (amas != null) {
+			this.amas._addAgent(this);
+		}
+	}
+	
+	public Agent(AmasMultiUIWindow window, A amas, Object... params) {
+		
+		this.id = uniqueIndex++;
+		this.params = params;
+		this.amas = amas;
+		neighborhood = new ArrayList<>();
+		neighborhood.add(this);
+		onInitialization();
+		if (!Configuration.commandLineMode)
+			onRenderingInitialization();
+		
+
+		if (amas != null) {
+			this.amas._addAgent(this);
+		}
+	}
+
+	/**
+	 * Add neighbors to the agent
+	 * 
+	 * @param agents
+	 *            The list of agent that should be considered as neighbor
+	 */
+	@SafeVarargs
+	public final void addNeighbor(Agent<A, E>... agents) {
+		for (Agent<A, E> agent : agents) {
+			if (agent != null) {
+				neighborhood.add(agent);
+				criticalities.put(agent, Double.NEGATIVE_INFINITY);
+			}
+		}
+	}
+
+	/**
+	 * This method must be overridden by the agents. This method shouldn't make any
+	 * calls to internal representation an agent has on its environment because
+	 * these information maybe outdated.
+	 * 
+	 * @return the criticality at a given moment
+	 */
+	protected double computeCriticality() {
+		return Double.NEGATIVE_INFINITY;
+	}
+
+	protected void setAsynchronous() {
+		if (currentPhase != Phase.INITIALIZING)
+			Log.defaultLog.fatal("AMAK", "Asynchronous mode must be set during the initialization");
+		this.synchronous = false;
+	}
+	/**
+	 * This method must be overriden if you need to specify an execution order layer
+	 * 
+	 * @return the execution order layer
+	 */
+	protected int computeExecutionOrderLayer() {
+		return 0;
+	}
+
+	/**
+	 * This method is called at the beginning of an agent's cycle
+	 */
+	protected void onAgentCycleBegin() {
+
+	}
+
+	/**
+	 * This method is called at the end of an agent's cycle
+	 */
+	protected void onAgentCycleEnd() {
+
+	}
+
+	/**
+	 * This method corresponds to the perception phase of the agents and must be
+	 * overridden
+	 */
+	protected void onPerceive() {
+
+	}
+
+	/**
+	 * This method corresponds to the decision phase of the agents and must be
+	 * overridden
+	 */
+	protected void onDecide() {
+
+	}
+
+	/**
+	 * This method corresponds to the action phase of the agents and must be
+	 * overridden
+	 */
+	protected void onAct() {
+
+	}
+
+	/**
+	 * In this method the agent should expose some variables with its neighbor
+	 */
+	protected void onExpose() {
+
+	}
+
+	/**
+	 * This method should be used to update the representation of the agent for
+	 * example in a VUI
+	 */
+	public void onUpdateRender() {
+
+	}
+
+	/**
+	 * This method is now deprecated and should be replaced by onUpdateRender
+	 * 
+	 * @deprecated Must be replaced by {@link #onUpdateRender()}
+	 */
+	@Deprecated
+	protected final void onDraw() {
+
+	}
+
+	/**
+	 * Called when all initial agents have been created and are ready to be started
+	 */
+	protected void onReady() {
+
+	}
+
+	/**
+	 * Called by the framework when all initial agents have been created and are
+	 * almost ready to be started
+	 */
+	protected final void _onBeforeReady() {
+		criticality = computeCriticality();
+		executionOrder = _computeExecutionOrder();
+	}
+
+	/**
+	 * Called before all agents are created
+	 */
+	protected void onInitialization() {
+
+	}
+
+	/**
+	 * Replaced by onInitialization
+	 * 
+	 * @deprecated Must be replaced by {@link #onInitialization()}
+	 */
+	@Deprecated
+	protected final void onInitialize() {
+
+	}
+
+	/**
+	 * Called to initialize the rendering of the agent
+	 */
+	protected void onRenderingInitialization() {
+
+	}
+
+	/**
+	 * @deprecated This method is useless because the state of the agent is not
+	 *             supposed to evolve before or after its cycle. Use
+	 *             OnAgentCycleBegin/End instead.
+	 * 
+	 *             This method is final because it must not be implemented.
+	 *             Implement it will have no effect.
+	 */
+	@Deprecated
+	protected final void onSystemCycleBegin() {
+
+	}
+
+	/**
+	 * @deprecated This method is useless because the state of the agent is not
+	 *             supposed to evolve before or after its cycle. Use
+	 *             OnAgentCycleBegin/End instead.
+	 * 
+	 *             This method is final because it must not be implemented.
+	 *             Implement it will have no effect.
+	 */
+	@Deprecated
+	protected final void onSystemCycleEnd() {
+
+	}
+
+	/**
+	 * This method is called automatically and corresponds to a full cycle of an
+	 * agent
+	 */
+	@Override
+	public void run() {
+		
+		ExecutionPolicy executionPolicy = amas.getExecutionPolicy();
+		if (executionPolicy == ExecutionPolicy.TWO_PHASES) {
+
+			currentPhase = nextPhase();
+			switch (currentPhase) {
+			case PERCEPTION:
+				phase1();
+				amas.informThatAgentPerceptionIsFinished();
+				break;
+			case DECISION_AND_ACTION:
+				phase2();
+				amas.informThatAgentDecisionAndActionAreFinished();
+				break;
+			default:
+				Log.defaultLog.fatal("AMAK", "An agent is being run in an invalid phase (%s)", currentPhase);
+			}
+		} else if (executionPolicy == ExecutionPolicy.ONE_PHASE) {
+			onePhaseCycle();
+			amas.informThatAgentPerceptionIsFinished();
+			amas.informThatAgentDecisionAndActionAreFinished();
+		}
+	}
+	public void onePhaseCycle() {
+		currentPhase = Phase.PERCEPTION;
+		phase1();
+		currentPhase = Phase.DECISION_AND_ACTION;
+		phase2();
+	}
+	/**
+	 * This method represents the perception phase of the agent
+	 */
+	protected final void phase1() {
+		onAgentCycleBegin();
+		perceive();
+		currentPhase = Phase.PERCEPTION_DONE;
+	}
+
+	/**
+	 * This method represents the decisionAndAction phase of the agent
+	 */
+	protected final void phase2() {
+		decideAndAct();
+		executionOrder = _computeExecutionOrder();
+		onExpose();
+		if (!Configuration.commandLineMode)
+			onUpdateRender();
+		onAgentCycleEnd();
+		currentPhase = Phase.DECISION_AND_ACTION_DONE;
+	}
+
+	/**
+	 * Determine which phase comes after another
+	 * 
+	 * @return the next phase
+	 */
+	private Phase nextPhase() {
+		switch (currentPhase) {
+		case PERCEPTION_DONE:
+			return Phase.DECISION_AND_ACTION;
+		case INITIALIZING:
+		case DECISION_AND_ACTION_DONE:
+		default:
+			return Phase.PERCEPTION;
+		}
+	}
+
+	/**
+	 * Compute the execution order from the layer and a random value. This method
+	 * shouldn't be overridden.
+	 * 
+	 * @return A number used by amak to determine which agent executes first
+	 */
+	protected int _computeExecutionOrder() {
+		return computeExecutionOrderLayer() * 10000 + amas.getEnvironment().getRandom().nextInt(10000);
+	}
+
+	/**
+	 * Perceive, decide and act
+	 */
+	void perceive() {
+		for (Agent<A, E> agent : neighborhood) {
+			criticalities.put(agent, agent.criticality);
+		}
+		onPerceive();
+		// Criticality of agent should be updated after perception AND after action
+		criticality = computeCriticality();
+		criticalities.put(this, criticality);
+	}
+
+	/**
+	 * A combination of decision and action as called by the framework
+	 */
+	private final void decideAndAct() {
+		onDecideAndAct();
+
+		criticality = computeCriticality();
+	}
+
+	/**
+	 * Decide and act These two phases can often be grouped
+	 */
+	protected void onDecideAndAct() {
+		onDecide();
+		onAct();
+	}
+
+	/**
+	 * Convenient method giving the most critical neighbor at a given moment
+	 * 
+	 * @param includingMe
+	 *            Should the agent also consider its own criticality
+	 * @return the most critical agent
+	 */
+	protected final Agent<A, E> getMostCriticalNeighbor(boolean includingMe) {
+		List<Agent<A, E>> criticalest = new ArrayList<>();
+		double maxCriticality = Double.NEGATIVE_INFINITY;
+
+		if (includingMe) {
+			criticalest.add(this);
+			maxCriticality = criticalities.getOrDefault(criticalest, Double.NEGATIVE_INFINITY);
+		}
+		for (Entry<Agent<A, E>, Double> e : criticalities.entrySet()) {
+			if (e.getValue() > maxCriticality) {
+				criticalest.clear();
+				maxCriticality = e.getValue();
+				criticalest.add(e.getKey());
+			} else if (e.getValue() == maxCriticality) {
+				criticalest.add(e.getKey());
+			}
+		}
+		if (criticalest.isEmpty())
+			return null;
+		return criticalest.get(getEnvironment().getRandom().nextInt(criticalest.size()));
+	}
+
+	/**
+	 * Get the latest computed execution order
+	 * 
+	 * @return the execution order
+	 */
+	public int getExecutionOrder() {
+		return executionOrder;
+	}
+
+	/**
+	 * Getter for the AMAS
+	 * 
+	 * @return the amas
+	 */
+	public A getAmas() {
+		return amas;
+	}
+
+	/**
+	 * Remove the agent from the system
+	 */
+	public void destroy() {
+		getAmas()._removeAgent(this);
+	}
+
+	/**
+	 * Agent toString
+	 */
+	@Override
+	public String toString() {
+		return String.format("Agent #%d", id);
+	}
+
+	/**
+	 * Getter for the current phase of the agent
+	 * 
+	 * @return the current phase
+	 */
+	public Phase getCurrentPhase() {
+		return currentPhase;
+	}
+
+	/**
+	 * Return the id of the agent
+	 * 
+	 * @return the id of the agent
+	 */
+	public int getId() {
+		return id;
+	}
+
+	/**
+	 * Getter for the environment
+	 * 
+	 * @return the environment
+	 */
+	public E getEnvironment() {
+		return getAmas().getEnvironment();
+	}
+
+	public boolean isSynchronous() {
+		return synchronous ;
+	}
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/Amas.java b/AMAKFX/src/fr/irit/smac/amak/Amas.java
index a73d0e5107ef13fc638c7e17037858d93ae44c5d..965306ca9e864c7cab05e8149012192f0e71e70c 100644
--- a/AMAKFX/src/fr/irit/smac/amak/Amas.java
+++ b/AMAKFX/src/fr/irit/smac/amak/Amas.java
@@ -13,9 +13,11 @@ import java.util.stream.Collectors;
 
 import fr.irit.smac.amak.tools.Log;
 import fr.irit.smac.amak.tools.RunLaterHelper;
+import fr.irit.smac.amak.ui.AmasMultiUIWindow;
 import fr.irit.smac.amak.ui.MainWindow;
 import fr.irit.smac.amak.ui.SchedulerToolbar;
 import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.VUIMulti;
 
 /**
  * This class must be overridden by multi-agent systems
@@ -26,6 +28,10 @@ import fr.irit.smac.amak.ui.VUI;
  *            The environment of the MAS
  */
 public class Amas<E extends Environment> implements Schedulable {
+	
+	public AmasMultiUIWindow amasMultiUIWindow;
+	public VUIMulti vuiMulti;
+	
 	/**
 	 * List of agents present in the system
 	 */
@@ -155,6 +161,43 @@ public class Amas<E extends Environment> implements Schedulable {
 			this.onRenderingInitialization();
 		this.scheduler.unlock();
 	}
+	
+	public Amas(AmasMultiUIWindow window, VUIMulti vui, E environment, Scheduling scheduling, Object... params) {
+		
+		if(!Configuration.commandLineMode) {
+			amasMultiUIWindow = window;
+			vuiMulti = vui;
+			amasMultiUIWindow.addTabbedPanel(vuiMulti.title, vuiMulti.getPanel());
+		}
+		
+		
+		executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(Configuration.allowedSimultaneousAgentsExecution);
+		
+		//this.scheduler = environment.getScheduler();
+		if (scheduling == Scheduling.DEFAULT) {
+
+			this.scheduler = Scheduler.getDefaultMultiUIScheduler(window);
+			this.scheduler.add(this);
+		} else {
+			this.scheduler = new Scheduler(this);
+			if (scheduling == Scheduling.UI && !Configuration.commandLineMode) {
+				amasMultiUIWindow.addToolbar(new SchedulerToolbar("Amas #" + id, getScheduler()));
+			}
+		}
+		
+		this.scheduler.lock();
+		this.params = params;
+		this.environment = environment;
+		this.onInitialConfiguration();
+		executionPolicy = Configuration.executionPolicy;
+		this.onInitialAgentsCreation();
+
+		addPendingAgents();
+		this.onReady();
+		if (!Configuration.commandLineMode)
+			this.onRenderingInitialization();
+		this.scheduler.unlock();
+	}
 
 	/**
 	 * The method in which the rendering initialization should be made. For example,
@@ -371,7 +414,19 @@ public class Amas<E extends Environment> implements Schedulable {
 	 * {@link Amas#onRenderingInitialization}
 	 */
 	protected void onUpdateRender() {
-		VUI.get().updateCanvas();
+		if(Configuration.multiUI) {
+			vuiMulti.updateCanvas();
+		}else {
+			VUI.get().updateCanvas();
+		}
+		
+	}
+	
+	
+
+	
+	public VUIMulti getVUIMulti() {
+		return vuiMulti;
 	}
 
 	/**
diff --git a/AMAKFX/src/fr/irit/smac/amak/Configuration.java b/AMAKFX/src/fr/irit/smac/amak/Configuration.java
index bc8050f8b87e296f7497acf1932764c02508c4f2..34c301cc8ae3334a7c8f49476db23e17af8a6b65 100644
--- a/AMAKFX/src/fr/irit/smac/amak/Configuration.java
+++ b/AMAKFX/src/fr/irit/smac/amak/Configuration.java
@@ -47,4 +47,6 @@ public class Configuration {
 	 * By default AMAK will wait for 1 sec before updating the plots
 	 */
 	public static double plotMilliSecondsUpdate = 1000;
+	
+	public static boolean multiUI = false;
 }
diff --git a/AMAKFX/src/fr/irit/smac/amak/Environment.java b/AMAKFX/src/fr/irit/smac/amak/Environment.java
index 68433e456f0c7b95e24b646a47e8806d67dd4666..e048ea299ca9fb0433762c93d30b570cca6d19b8 100644
--- a/AMAKFX/src/fr/irit/smac/amak/Environment.java
+++ b/AMAKFX/src/fr/irit/smac/amak/Environment.java
@@ -1,145 +1,170 @@
-package fr.irit.smac.amak;
-
-import java.util.Random;
-
-import fr.irit.smac.amak.ui.MainWindow;
-import fr.irit.smac.amak.ui.SchedulerToolbar;
-
-/**
- * This class must be overridden by environments
- * 
- * @author Alexandre Perles
- *
- */
-public abstract class Environment implements Schedulable {
-
-	/**
-	 * Unique index to give unique id to each environment
-	 */
-	private static int uniqueIndex;
-
-	/**
-	 * The id of the environment
-	 */
-	private final int id = uniqueIndex++;
-
-	/**
-	 * The parameters that are passed to {@link Environment#onInitialization()}
-	 */
-	protected Object[] params;
-	/**
-	 * Random object common to the amas
-	 */
-	private Random random = new Random();
-	/**
-	 * The scheduler of the environment
-	 */
-	private Scheduler scheduler;
-
-	/**
-	 * Constructor
-	 * 
-	 * @param _scheduling
-	 *            The scheduling of the environment
-	 * @param params
-	 *            The parameters to initialize the environment
-	 */
-	public Environment(Scheduling _scheduling, Object... params) {
-		if (_scheduling == Scheduling.DEFAULT) {
-			this.scheduler = Scheduler.getDefaultScheduler();
-			this.scheduler.add(this);
-		} else {
-			this.scheduler = new Scheduler(this);
-			if (_scheduling == Scheduling.UI && !Configuration.commandLineMode)
-				MainWindow.addToolbar(new SchedulerToolbar("Environment #" + id, getScheduler()));
-		}
-		this.scheduler.lock();
-		this.params = params;
-		onInitialization();
-		onInitialEntitiesCreation();
-		if (!Configuration.commandLineMode)
-			onRenderingInitialization();
-		this.scheduler.unlock();
-	}
-
-	/**
-	 * Override this method is you wish to render environment. For example, you can
-	 * use this method to create a VUI drawable object.
-	 */
-	private void onRenderingInitialization() {
-	}
-
-	/**
-	 * Getter for the scheduler
-	 * 
-	 * @return the scheduler
-	 */
-	public Scheduler getScheduler() {
-		return scheduler;
-	}
-
-	/**
-	 * Set the seed for the common random object. This method should be called at
-	 * the very beginning of the initialization process
-	 * 
-	 * @param _seed
-	 *            The seed to initialize the random object
-	 */
-	public void setSeed(long _seed) {
-		random = new Random(_seed);
-	}
-
-	/**
-	 * This method is called during the initialization process of the environment
-	 */
-	public void onInitialization() {
-	}
-
-	/**
-	 * This method is called after the initialization process of the environment to
-	 * create entities
-	 */
-	public void onInitialEntitiesCreation() {
-	}
-
-	/**
-	 * This method is called at each cycle of the environment
-	 */
-	public void onCycle() {
-	}
-
-	@Override
-	public boolean stopCondition() {
-		return false;
-	}
-
-	@Override
-	public final void cycle() {
-		onCycle();
-		if (!Configuration.commandLineMode)
-			onUpdateRender();
-	}
-
-	/**
-	 * Override this method to update rendering related to the environment
-	 */
-	protected void onUpdateRender() {
-	}
-
-	/**
-	 * Getter for the random object
-	 * 
-	 * @return the random object
-	 */
-	public Random getRandom() {
-		return random;
-	}
-
-	@Override
-	public void onSchedulingStarts() {
-	}
-
-	@Override
-	public void onSchedulingStops() {
-	}
-}
+package fr.irit.smac.amak;
+
+import java.util.Random;
+
+import fr.irit.smac.amak.ui.AmasMultiUIWindow;
+import fr.irit.smac.amak.ui.MainWindow;
+import fr.irit.smac.amak.ui.SchedulerToolbar;
+
+/**
+ * This class must be overridden by environments
+ * 
+ * @author Alexandre Perles
+ *
+ */
+public abstract class Environment implements Schedulable {
+	
+	public AmasMultiUIWindow amasMultiUIWindow;
+
+	/**
+	 * Unique index to give unique id to each environment
+	 */
+	private static int uniqueIndex;
+
+	/**
+	 * The id of the environment
+	 */
+	private final int id = uniqueIndex++;
+
+	/**
+	 * The parameters that are passed to {@link Environment#onInitialization()}
+	 */
+	protected Object[] params;
+	/**
+	 * Random object common to the amas
+	 */
+	private Random random = new Random();
+	/**
+	 * The scheduler of the environment
+	 */
+	private Scheduler scheduler;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param _scheduling
+	 *            The scheduling of the environment
+	 * @param params
+	 *            The parameters to initialize the environment
+	 */
+	public Environment(Scheduling _scheduling, Object... params) {
+		if (_scheduling == Scheduling.DEFAULT) {
+			this.scheduler = Scheduler.getDefaultScheduler();
+			this.scheduler.add(this);
+		} else {
+			this.scheduler = new Scheduler(this);
+			if (_scheduling == Scheduling.UI && !Configuration.commandLineMode)
+				MainWindow.addToolbar(new SchedulerToolbar("Environment #" + id, getScheduler()));
+		}
+		this.scheduler.lock();
+		this.params = params;
+		onInitialization();
+		onInitialEntitiesCreation();
+		if (!Configuration.commandLineMode)
+			onRenderingInitialization();
+		this.scheduler.unlock();
+	}
+	
+	public Environment(AmasMultiUIWindow window, Scheduling _scheduling, Object... params) {
+		amasMultiUIWindow = window;
+//		if (_scheduling == Scheduling.DEFAULT) {
+//			this.scheduler = Scheduler.getDefaultMultiUIScheduler(window);
+//			this.scheduler.add(this);
+//		} else {
+//		this.scheduler = new Scheduler(this);
+//		if (_scheduling == Scheduling.UI && !Configuration.commandLineMode)
+//			amasMultiUIWindow.addToolbar(new SchedulerToolbar("Environment #" + id, getScheduler()));
+//		}
+//
+//		this.scheduler.lock();
+		this.params = params;
+		onInitialization();
+		onInitialEntitiesCreation();
+		if (!Configuration.commandLineMode)
+			onRenderingInitialization();
+//		this.scheduler.unlock();
+	}
+
+	/**
+	 * Override this method is you wish to render environment. For example, you can
+	 * use this method to create a VUI drawable object.
+	 */
+	private void onRenderingInitialization() {
+	}
+
+	/**
+	 * Getter for the scheduler
+	 * 
+	 * @return the scheduler
+	 */
+	public Scheduler getScheduler() {
+		return scheduler;
+	}
+
+	/**
+	 * Set the seed for the common random object. This method should be called at
+	 * the very beginning of the initialization process
+	 * 
+	 * @param _seed
+	 *            The seed to initialize the random object
+	 */
+	public void setSeed(long _seed) {
+		random = new Random(_seed);
+	}
+
+	/**
+	 * This method is called during the initialization process of the environment
+	 */
+	public void onInitialization() {
+	}
+
+	/**
+	 * This method is called after the initialization process of the environment to
+	 * create entities
+	 */
+	public void onInitialEntitiesCreation() {
+	}
+
+	/**
+	 * This method is called at each cycle of the environment
+	 */
+	public void onCycle() {
+	}
+
+	@Override
+	public boolean stopCondition() {
+		return false;
+	}
+
+	@Override
+	public final void cycle() {
+		onCycle();
+		if (!Configuration.commandLineMode)
+			onUpdateRender();
+	}
+
+	/**
+	 * Override this method to update rendering related to the environment
+	 */
+	protected void onUpdateRender() {
+	}
+
+	/**
+	 * Getter for the random object
+	 * 
+	 * @return the random object
+	 */
+	public Random getRandom() {
+		return random;
+	}
+
+	@Override
+	public void onSchedulingStarts() {
+	}
+
+	@Override
+	public void onSchedulingStops() {
+	}
+	
+
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/Scheduler.java b/AMAKFX/src/fr/irit/smac/amak/Scheduler.java
index bffeeb2ce6a8d7e7a2422d165b41817e2e8b44c6..2c6a1a18701474de8807f168378aca26f69b8ee8 100644
--- a/AMAKFX/src/fr/irit/smac/amak/Scheduler.java
+++ b/AMAKFX/src/fr/irit/smac/amak/Scheduler.java
@@ -1,341 +1,357 @@
-package fr.irit.smac.amak;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.ConcurrentModificationException;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Queue;
-import java.util.Set;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.function.Consumer;
-
-import fr.irit.smac.amak.ui.MainWindow;
-import fr.irit.smac.amak.ui.SchedulerToolbar;
-
-/**
- * A scheduler associated to a MAS
- * 
- * @author Alexandre Perles
- *
- */
-public class Scheduler implements Runnable, Serializable {
-	/**
-	 * Unique ID meant to handle serialization correctly
-	 */
-	private static final long serialVersionUID = -4765899565369100376L;
-	/**
-	 * The schedulables object handled by the scheduler
-	 */
-	private final Set<Schedulable> schedulables = new LinkedHashSet<>();
-	/**
-	 * The state of the scheduler {@link State}
-	 */
-	private State state;
-	/**
-	 * The sleep time in ms between each cycle
-	 */
-	private int sleep;
-	/**
-	 * A lock to protect the state
-	 */
-	private final ReentrantLock stateLock = new ReentrantLock();
-	/**
-	 * Method that is called when the scheduler stops
-	 */
-	private Consumer<Scheduler> onStop;
-	/**
-	 * The methods called when the speed is changed. Useful to change the value of
-	 * the GUI slider of {@link SchedulerToolbar}
-	 */
-	private List<Consumer<Scheduler>> onChange = new ArrayList<>();
-	/**
-	 * The idea is to prevent scheduler from launching if the schedulables are not
-	 * yet fully ready
-	 */
-	private int locked = 0;
-	/**
-	 * The default scheduler
-	 */
-	private static Scheduler defaultScheduler;
-	/**
-	 * The schedulables that must be added
-	 */
-	private Queue<Schedulable> pendingAdditionSchedulables = new LinkedList<>();
-	/**
-	 * The schedulables that must be removed
-	 */
-	private Queue<Schedulable> pendingRemovalSchedulables = new LinkedList<>();
-
-	/**
-	 * State of the scheduler
-	 *
-	 */
-	public enum State {
-		/**
-		 * The scheduler is running
-		 */
-		RUNNING,
-		/**
-		 * The scheduler is paused
-		 */
-		IDLE,
-		/**
-		 * The scheduler is expected to stop at the end at the current cycle
-		 */
-		PENDING_STOP
-
-	}
-
-	/**
-	 * Constructor which set the initial state and auto start if requested
-	 * 
-	 * @param _schedulables
-	 *            the corresponding schedulables
-	 */
-	public Scheduler(Schedulable... _schedulables) {
-
-		for (Schedulable schedulable : _schedulables) {
-			this.add(schedulable);
-		}
-		this.state = State.IDLE;
-	}
-
-	/**
-	 * Create or return the default scheduler
-	 * 
-	 * @return The default scheduler
-	 */
-	public static Scheduler getDefaultScheduler() {
-		if (defaultScheduler == null) {
-			defaultScheduler = new Scheduler();
-			if (!Configuration.commandLineMode) {
-				MainWindow.instance();
-				SchedulerToolbar st = new SchedulerToolbar("Default", defaultScheduler);
-				MainWindow.addToolbar(st);
-			}
-		}
-		return defaultScheduler;
-	}
-
-	/**
-	 * Set the delay between two cycles and launch the scheduler if it is not
-	 * running
-	 * 
-	 * @param i
-	 *            the delay between two cycles
-	 */
-	public void startWithSleep(int i) {
-		if (locked > 0) {
-
-			synchronized (onChange) {
-				onChange.forEach(c -> c.accept(this));
-			}
-			return;
-		}
-		setSleep(i);
-		stateLock.lock();
-		switch (state) {
-		case IDLE:
-			state = State.RUNNING;
-			new Thread(this).start();
-			break;
-		default:
-			break;
-		}
-		stateLock.unlock();
-		synchronized (onChange) {
-			onChange.forEach(c -> c.accept(this));
-		}
-	}
-
-	/**
-	 * Start (or continue) with no delay between cycles
-	 */
-	public void start() {
-		startWithSleep(Schedulable.DEFAULT_SLEEP);
-	}
-
-	/**
-	 * Execute one cycle
-	 */
-	public void step() {
-		if (locked > 0) {
-			synchronized (onChange) {
-				onChange.forEach(c -> c.accept(this));
-			}
-			return;
-		}
-		this.setSleep(0);
-		stateLock.lock();
-		switch (state) {
-		case IDLE:
-			state = State.PENDING_STOP;
-			new Thread(this).start();
-			break;
-		default:
-			break;
-
-		}
-		stateLock.unlock();
-		synchronized (onChange) {
-			onChange.forEach(c -> c.accept(this));
-		}
-	}
-
-	/**
-	 * Stop the scheduler if it is running
-	 */
-	public void stop() {
-		stateLock.lock();
-		switch (state) {
-		case RUNNING:
-			state = State.PENDING_STOP;
-			break;
-		default:
-			break;
-
-		}
-		stateLock.unlock();
-		synchronized (onChange) {
-			onChange.forEach(c -> c.accept(this));
-		}
-	}
-
-	/**
-	 * Threaded run method
-	 */
-	@Override
-	public void run() {
-		treatPendingSchedulables();
-		for (Schedulable schedulable : schedulables) {
-			schedulable.onSchedulingStarts();
-		}
-		boolean mustStop;
-		do {
-			for (Schedulable schedulable : schedulables) {
-				schedulable.cycle();
-			}
-			if (getSleep() != 0) {
-				try {
-					Thread.sleep(getSleep());
-				} catch (final InterruptedException e) {
-					e.printStackTrace();
-				}
-			}
-			mustStop = false;
-			for (Schedulable schedulable : schedulables) {
-				mustStop |= schedulable.stopCondition();
-			}
-		} while (state == State.RUNNING && !mustStop);
-		stateLock.lock();
-		state = State.IDLE;
-		stateLock.unlock();
-
-		for (Schedulable schedulable : schedulables) {
-			schedulable.onSchedulingStops();
-		}
-		treatPendingSchedulables();
-		if (onStop != null)
-			onStop.accept(this);
-	}
-
-	/**
-	 * Effectively Add or Remove the schedulables that were added or removed during
-	 * a cycle to avoid {@link ConcurrentModificationException}
-	 */
-	private void treatPendingSchedulables() {
-		while (!pendingAdditionSchedulables.isEmpty())
-			schedulables.add(pendingAdditionSchedulables.poll());
-		while (!pendingRemovalSchedulables.isEmpty())
-			schedulables.remove(pendingRemovalSchedulables.poll());
-
-	}
-
-	/**
-	 * Set the method that must be executed when the system is stopped
-	 * 
-	 * @param _onStop
-	 *            Consumer method
-	 */
-	public final void setOnStop(Consumer<Scheduler> _onStop) {
-		this.onStop = _onStop;
-	}
-
-	/**
-	 * Add a method that must be executed when the scheduler speed is changed
-	 * 
-	 * @param _onChange
-	 *            Consumer method
-	 */
-	public final void addOnChange(Consumer<Scheduler> _onChange) {
-		synchronized (onChange) {
-			this.onChange.add(_onChange);
-		}
-	}
-
-	/**
-	 * Is the scheduler running ?
-	 * 
-	 * @return true if the scheduler is running
-	 */
-	public boolean isRunning() {
-		return state == State.RUNNING;
-	}
-
-	/**
-	 * Getter for the sleep time
-	 * 
-	 * @return the sleep time
-	 */
-
-	public int getSleep() {
-		return sleep;
-	}
-
-	/**
-	 * Setter for the sleep time
-	 * 
-	 * @param sleep
-	 *            The time between each cycle
-	 */
-	public void setSleep(int sleep) {
-		this.sleep = sleep;
-	}
-
-	/**
-	 * Plan to add a schedulable
-	 * 
-	 * @param _schedulable
-	 *            the schedulable to add
-	 */
-	public void add(Schedulable _schedulable) {
-		this.pendingAdditionSchedulables.add(_schedulable);
-	}
-
-	/**
-	 * Plan to remove a schedulable
-	 * 
-	 * @param _schedulable
-	 *            the schedulable to remove
-	 */
-	public void remove(Schedulable _schedulable) {
-		this.pendingRemovalSchedulables.add(_schedulable);
-	}
-
-	/**
-	 * Soft lock the scheduler to avoid a too early running
-	 */
-	public void lock() {
-		locked++;
-	}
-
-	/**
-	 * Soft unlock the scheduler to avoid a too early running
-	 */
-	public void unlock() {
-		locked--;
-	}
-
-}
+package fr.irit.smac.amak;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.ConcurrentModificationException;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
+
+import fr.irit.smac.amak.ui.AmasMultiUIWindow;
+import fr.irit.smac.amak.ui.MainWindow;
+import fr.irit.smac.amak.ui.SchedulerToolbar;
+
+/**
+ * A scheduler associated to a MAS
+ * 
+ * @author Alexandre Perles
+ *
+ */
+public class Scheduler implements Runnable, Serializable {
+	/**
+	 * Unique ID meant to handle serialization correctly
+	 */
+	private static final long serialVersionUID = -4765899565369100376L;
+	/**
+	 * The schedulables object handled by the scheduler
+	 */
+	private final Set<Schedulable> schedulables = new LinkedHashSet<>();
+	/**
+	 * The state of the scheduler {@link State}
+	 */
+	private State state;
+	/**
+	 * The sleep time in ms between each cycle
+	 */
+	private int sleep;
+	/**
+	 * A lock to protect the state
+	 */
+	private final ReentrantLock stateLock = new ReentrantLock();
+	/**
+	 * Method that is called when the scheduler stops
+	 */
+	private Consumer<Scheduler> onStop;
+	/**
+	 * The methods called when the speed is changed. Useful to change the value of
+	 * the GUI slider of {@link SchedulerToolbar}
+	 */
+	private List<Consumer<Scheduler>> onChange = new ArrayList<>();
+	/**
+	 * The idea is to prevent scheduler from launching if the schedulables are not
+	 * yet fully ready
+	 */
+	private int locked = 0;
+	/**
+	 * The default scheduler
+	 */
+	private static Scheduler defaultScheduler;
+	
+
+	/**
+	 * The schedulables that must be added
+	 */
+	private Queue<Schedulable> pendingAdditionSchedulables = new LinkedList<>();
+	/**
+	 * The schedulables that must be removed
+	 */
+	private Queue<Schedulable> pendingRemovalSchedulables = new LinkedList<>();
+
+	/**
+	 * State of the scheduler
+	 *
+	 */
+	public enum State {
+		/**
+		 * The scheduler is running
+		 */
+		RUNNING,
+		/**
+		 * The scheduler is paused
+		 */
+		IDLE,
+		/**
+		 * The scheduler is expected to stop at the end at the current cycle
+		 */
+		PENDING_STOP
+
+	}
+
+	/**
+	 * Constructor which set the initial state and auto start if requested
+	 * 
+	 * @param _schedulables
+	 *            the corresponding schedulables
+	 */
+	public Scheduler(Schedulable... _schedulables) {
+
+		for (Schedulable schedulable : _schedulables) {
+			this.add(schedulable);
+		}
+		this.state = State.IDLE;
+	}
+
+	/**
+	 * Create or return the default scheduler
+	 * 
+	 * @return The default scheduler
+	 */
+	public static Scheduler getDefaultScheduler() {
+		if (defaultScheduler == null) {
+			defaultScheduler = new Scheduler();
+			if (!Configuration.commandLineMode) {
+				MainWindow.instance();
+				SchedulerToolbar st = new SchedulerToolbar("Default", defaultScheduler);
+				MainWindow.addToolbar(st);
+			}
+		}
+		return defaultScheduler;
+	}
+	
+	
+	
+	
+	
+	public static Scheduler getDefaultMultiUIScheduler(AmasMultiUIWindow window) {
+		Scheduler multiUIScheduler = new Scheduler();
+			if (!Configuration.commandLineMode) {
+				SchedulerToolbar st = new SchedulerToolbar("Default", multiUIScheduler);
+				window.addToolbar(st);
+			}
+		return multiUIScheduler;
+	}
+
+	/**
+	 * Set the delay between two cycles and launch the scheduler if it is not
+	 * running
+	 * 
+	 * @param i
+	 *            the delay between two cycles
+	 */
+	public void startWithSleep(int i) {
+		if (locked > 0) {
+
+			synchronized (onChange) {
+				onChange.forEach(c -> c.accept(this));
+			}
+			return;
+		}
+		setSleep(i);
+		stateLock.lock();
+		switch (state) {
+		case IDLE:
+			state = State.RUNNING;
+			new Thread(this).start();
+			break;
+		default:
+			break;
+		}
+		stateLock.unlock();
+		synchronized (onChange) {
+			onChange.forEach(c -> c.accept(this));
+		}
+	}
+
+	/**
+	 * Start (or continue) with no delay between cycles
+	 */
+	public void start() {
+		startWithSleep(Schedulable.DEFAULT_SLEEP);
+	}
+
+	/**
+	 * Execute one cycle
+	 */
+	public void step() {
+		if (locked > 0) {
+			synchronized (onChange) {
+				onChange.forEach(c -> c.accept(this));
+			}
+			return;
+		}
+		this.setSleep(0);
+		stateLock.lock();
+		switch (state) {
+		case IDLE:
+			state = State.PENDING_STOP;
+			new Thread(this).start();
+			break;
+		default:
+			break;
+
+		}
+		stateLock.unlock();
+		synchronized (onChange) {
+			onChange.forEach(c -> c.accept(this));
+		}
+	}
+
+	/**
+	 * Stop the scheduler if it is running
+	 */
+	public void stop() {
+		stateLock.lock();
+		switch (state) {
+		case RUNNING:
+			state = State.PENDING_STOP;
+			break;
+		default:
+			break;
+
+		}
+		stateLock.unlock();
+		synchronized (onChange) {
+			onChange.forEach(c -> c.accept(this));
+		}
+	}
+
+	/**
+	 * Threaded run method
+	 */
+	@Override
+	public void run() {
+		treatPendingSchedulables();
+		for (Schedulable schedulable : schedulables) {
+			schedulable.onSchedulingStarts();
+		}
+		boolean mustStop;
+		do {
+			for (Schedulable schedulable : schedulables) {
+				schedulable.cycle();
+			}
+			if (getSleep() != 0) {
+				try {
+					Thread.sleep(getSleep());
+				} catch (final InterruptedException e) {
+					e.printStackTrace();
+				}
+			}
+			mustStop = false;
+			for (Schedulable schedulable : schedulables) {
+				mustStop |= schedulable.stopCondition();
+			}
+		} while (state == State.RUNNING && !mustStop);
+		stateLock.lock();
+		state = State.IDLE;
+		stateLock.unlock();
+
+		for (Schedulable schedulable : schedulables) {
+			schedulable.onSchedulingStops();
+		}
+		treatPendingSchedulables();
+		if (onStop != null)
+			onStop.accept(this);
+	}
+
+	/**
+	 * Effectively Add or Remove the schedulables that were added or removed during
+	 * a cycle to avoid {@link ConcurrentModificationException}
+	 */
+	private void treatPendingSchedulables() {
+		while (!pendingAdditionSchedulables.isEmpty())
+			schedulables.add(pendingAdditionSchedulables.poll());
+		while (!pendingRemovalSchedulables.isEmpty())
+			schedulables.remove(pendingRemovalSchedulables.poll());
+
+	}
+
+	/**
+	 * Set the method that must be executed when the system is stopped
+	 * 
+	 * @param _onStop
+	 *            Consumer method
+	 */
+	public final void setOnStop(Consumer<Scheduler> _onStop) {
+		this.onStop = _onStop;
+	}
+
+	/**
+	 * Add a method that must be executed when the scheduler speed is changed
+	 * 
+	 * @param _onChange
+	 *            Consumer method
+	 */
+	public final void addOnChange(Consumer<Scheduler> _onChange) {
+		synchronized (onChange) {
+			this.onChange.add(_onChange);
+		}
+	}
+
+	/**
+	 * Is the scheduler running ?
+	 * 
+	 * @return true if the scheduler is running
+	 */
+	public boolean isRunning() {
+		return state == State.RUNNING;
+	}
+
+	/**
+	 * Getter for the sleep time
+	 * 
+	 * @return the sleep time
+	 */
+
+	public int getSleep() {
+		return sleep;
+	}
+
+	/**
+	 * Setter for the sleep time
+	 * 
+	 * @param sleep
+	 *            The time between each cycle
+	 */
+	public void setSleep(int sleep) {
+		this.sleep = sleep;
+	}
+
+	/**
+	 * Plan to add a schedulable
+	 * 
+	 * @param _schedulable
+	 *            the schedulable to add
+	 */
+	public void add(Schedulable _schedulable) {
+		this.pendingAdditionSchedulables.add(_schedulable);
+	}
+
+	/**
+	 * Plan to remove a schedulable
+	 * 
+	 * @param _schedulable
+	 *            the schedulable to remove
+	 */
+	public void remove(Schedulable _schedulable) {
+		this.pendingRemovalSchedulables.add(_schedulable);
+	}
+
+	/**
+	 * Soft lock the scheduler to avoid a too early running
+	 */
+	public void lock() {
+		locked++;
+	}
+
+	/**
+	 * Soft unlock the scheduler to avoid a too early running
+	 */
+	public void unlock() {
+		locked--;
+	}
+
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/asyncrandomants/AsyncAntsLaunchExample.java b/AMAKFX/src/fr/irit/smac/amak/examples/asyncrandomants/AsyncAntsLaunchExample.java
deleted file mode 100644
index 7b295f64e71a469ace234de3e827fee863546929..0000000000000000000000000000000000000000
--- a/AMAKFX/src/fr/irit/smac/amak/examples/asyncrandomants/AsyncAntsLaunchExample.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package fr.irit.smac.amak.examples.asyncrandomants;
-
-import fr.irit.smac.amak.Configuration;
-import fr.irit.smac.amak.examples.randomants.AntExample;
-import fr.irit.smac.amak.examples.randomants.AntHillExample;
-import fr.irit.smac.amak.examples.randomants.WorldExample;
-import fr.irit.smac.amak.ui.MainWindow;
-import javafx.scene.control.Label;
-import javafx.scene.layout.Pane;
-
-/**
- * Class aiming at starting the mas-less ants system
- * 
- * @author perles
- *
- */
-public class AsyncAntsLaunchExample {
-
-	/**
-	 * Launch method
-	 * 
-	 * @param args
-	 *            Main arguments
-	 */
-	public static void main(String[] args) {
-		Configuration.allowedSimultaneousAgentsExecution = 4;
-		WorldExample env = new WorldExample();
-		AntHillExample amas = new AntHillExample(env);
-		for (int i = 0; i < 50; i++)
-			new AntExample(amas, 0, 0);
-		
-		Pane panel = new Pane();
-		String content = "Async AntHill simulation\n\n" + "Ants move randomly.\n"
-				+ "This demo is here to show AMAK asynchronous agent capacities.";
-		Label label = new Label(content);
-		label.setStyle("-fx-font-weight: bold;");
-		panel.getChildren().add(label);
-		MainWindow.setLeftPanel(panel);
-	}
-}
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/asyncrandomants/package-info.java b/AMAKFX/src/fr/irit/smac/amak/examples/asyncrandomants/package-info.java
deleted file mode 100644
index 789de13bcda89c7b064448c0c987cb26fe666abd..0000000000000000000000000000000000000000
--- a/AMAKFX/src/fr/irit/smac/amak/examples/asyncrandomants/package-info.java
+++ /dev/null
@@ -1,5 +0,0 @@
-/**
- * This package contains an example showing ants moving randomly.
- * In this example, agents are fully asynchronous.
- */
-package fr.irit.smac.amak.examples.asyncrandomants;
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntExample.java b/AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntExample.java
index 8186b0d4b27f736e337391f0110cb3340d7b2d2d..9a847c638d781660d25ca8c7f246d6fea72d7116 100644
--- a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntExample.java
+++ b/AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntExample.java
@@ -1,88 +1,88 @@
-package fr.irit.smac.amak.examples.randomants;
-
-import fr.irit.smac.amak.Agent;
-import fr.irit.smac.amak.ui.VUI;
-import fr.irit.smac.amak.ui.drawables.DrawableImage;
-
-public class AntExample extends Agent<AntHillExample, WorldExample> {
-	
-	private boolean dead = false; 
-	
-	/**
-	 * X coordinate of the ant in the world
-	 */
-	public double dx;
-	/**
-	 * Y coordinate of the ant in the world
-	 */
-	public double dy;
-	/**
-	 * Angle in radians
-	 */
-	private double angle = Math.random() * Math.PI * 2;
-	private DrawableImage image;
-
-	/**
-	 * Constructor of the ant
-	 * 
-	 * @param amas
-	 *            the amas the ant belongs to
-	 * @param startX
-	 *            Initial X coordinate
-	 * @param startY
-	 *            Initial Y coordinate
-	 */
-	public AntExample(AntHillExample amas, double startX, double startY) {
-		super(amas, startX, startY);
-	}
-	@Override
-	public void onInitialization() {
-		dx = (double) params[0];
-		dy = (double) params[1];
-	}
-
-	@Override
-	protected void onRenderingInitialization() {
-		image = VUI.get().createAndAddImage(dx, dy, "file:resources/ant.png");
-		image.setName("Ant "+getId());
-	}
-
-	/**
-	 * Move in a random direction
-	 */
-	@Override
-	protected void onDecideAndAct() {
-		double random = amas.getEnvironment().getRandom().nextGaussian();
-		angle += random * 0.1;
-		dx += Math.cos(angle);
-		dy += Math.sin(angle);
-		while (dx >= getAmas().getEnvironment().getWidth() / 2)
-			dx -= getAmas().getEnvironment().getWidth();
-		while (dy >= getAmas().getEnvironment().getHeight() / 2)
-			dy -= getAmas().getEnvironment().getHeight();
-		while (dx < -getAmas().getEnvironment().getWidth() / 2)
-			dx += getAmas().getEnvironment().getWidth();
-		while (dy < -getAmas().getEnvironment().getHeight() / 2)
-			dy += getAmas().getEnvironment().getHeight();
-
-		if (amas.getEnvironment().getRandom().nextDouble() < 0.001) {
-			dead = true;
-			destroy();
-		}
-
-		if (amas.getEnvironment().getRandom().nextDouble() < 0.001) {
-			new AntExample(getAmas(), dx, dy);
-		}
-	}
-
-	@Override
-	public void onUpdateRender() {
-		image.move(dx, dy);
-		image.setAngle(angle);
-		image.setInfo("Ant "+getId()+"\nPosition "+dx+" "+dy+"\nAngle "+angle);
-		if(dead) {
-			image.setFilename("file:Resources/ant_dead.png");
-			image.setInfo("Ant "+getId()+"\nPosition "+dx+" "+dy+"\nAngle "+angle+"\nDead");
-		}
-	}
-}
+package fr.irit.smac.amak.examples.randomants;
+
+import fr.irit.smac.amak.Agent;
+import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.drawables.DrawableImage;
+
+public class AntExample extends Agent<AntHillExample, WorldExample> {
+	
+	private boolean dead = false; 
+	
+	/**
+	 * X coordinate of the ant in the world
+	 */
+	public double dx;
+	/**
+	 * Y coordinate of the ant in the world
+	 */
+	public double dy;
+	/**
+	 * Angle in radians
+	 */
+	private double angle = Math.random() * Math.PI * 2;
+	private DrawableImage image;
+
+	/**
+	 * Constructor of the ant
+	 * 
+	 * @param amas
+	 *            the amas the ant belongs to
+	 * @param startX
+	 *            Initial X coordinate
+	 * @param startY
+	 *            Initial Y coordinate
+	 */
+	public AntExample(AntHillExample amas, double startX, double startY) {
+		super(amas, startX, startY);
+	}
+	@Override
+	public void onInitialization() {
+		dx = (double) params[0];
+		dy = (double) params[1];
+	}
+
+	@Override
+	protected void onRenderingInitialization() {
+		image = VUI.get().createAndAddImage(dx, dy, "file:resources/ant.png");
+		image.setName("Ant "+getId());
+	}
+
+	/**
+	 * Move in a random direction
+	 */
+	@Override
+	protected void onDecideAndAct() {
+		double random = amas.getEnvironment().getRandom().nextGaussian();
+		angle += random * 0.1;
+		dx += Math.cos(angle);
+		dy += Math.sin(angle);
+		while (dx >= getAmas().getEnvironment().getWidth() / 2)
+			dx -= getAmas().getEnvironment().getWidth();
+		while (dy >= getAmas().getEnvironment().getHeight() / 2)
+			dy -= getAmas().getEnvironment().getHeight();
+		while (dx < -getAmas().getEnvironment().getWidth() / 2)
+			dx += getAmas().getEnvironment().getWidth();
+		while (dy < -getAmas().getEnvironment().getHeight() / 2)
+			dy += getAmas().getEnvironment().getHeight();
+
+		if (amas.getEnvironment().getRandom().nextDouble() < 0.001) {
+			dead = true;
+			destroy();
+		}
+
+		if (amas.getEnvironment().getRandom().nextDouble() < 0.001) {
+			new AntExample(getAmas(), dx, dy);
+		}
+	}
+
+	@Override
+	public void onUpdateRender() {
+		image.move(dx, dy);
+		image.setAngle(angle);
+		image.setInfo("Ant "+getId()+"\nPosition "+dx+" "+dy+"\nAngle "+angle);
+		if(dead) {
+			image.setFilename("file:Resources/ant_dead.png");
+			image.setInfo("Ant "+getId()+"\nPosition "+dx+" "+dy+"\nAngle "+angle+"\nDead");
+		}
+	}
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/WorldExample.java b/AMAKFX/src/fr/irit/smac/amak/examples/randomants/WorldExample.java
index 2c6ea56a9ef181a9cf191d87669c578c3dd7e24b..88156754ae2aaa969ed1b24c99ad7143f3854987 100644
--- a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/WorldExample.java
+++ b/AMAKFX/src/fr/irit/smac/amak/examples/randomants/WorldExample.java
@@ -1,28 +1,28 @@
-package fr.irit.smac.amak.examples.randomants;
-
-import fr.irit.smac.amak.Environment;
-import fr.irit.smac.amak.Scheduling;
-
-public class WorldExample extends Environment {
-	public WorldExample(Object...params) {
-		super(Scheduling.DEFAULT, params);
-	}
-
-	private int width;
-	private int height;
-
-	public int getWidth() {
-		return width;
-	}
-
-	public int getHeight() {
-		return height;
-	}
-
-	@Override
-	public void onInitialization() {
-		this.width = 800;
-		this.height = 600;
-	}
-
-}
+package fr.irit.smac.amak.examples.randomants;
+
+import fr.irit.smac.amak.Environment;
+import fr.irit.smac.amak.Scheduling;
+
+public class WorldExample extends Environment {
+	public WorldExample(Object...params) {
+		super(Scheduling.DEFAULT, params);
+	}
+
+	private int width;
+	private int height;
+
+	public int getWidth() {
+		return width;
+	}
+
+	public int getHeight() {
+		return height;
+	}
+
+	@Override
+	public void onInitialization() {
+		this.width = 800;
+		this.height = 600;
+	}
+
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntExampleMutliUI.java b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntExampleMutliUI.java
new file mode 100644
index 0000000000000000000000000000000000000000..a8b731ca2be36cc141828375e17721adcf89ea4e
--- /dev/null
+++ b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntExampleMutliUI.java
@@ -0,0 +1,92 @@
+package fr.irit.smac.amak.examples.randomantsMultiUi;
+
+import fr.irit.smac.amak.Agent;
+import fr.irit.smac.amak.ui.AmasMultiUIWindow;
+import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.drawables.DrawableImage;
+
+public class AntExampleMutliUI extends Agent<AntHillExampleMultiUI, WorldExampleMultiUI> {
+	
+	
+	
+	private boolean dead = false; 
+	
+	/**
+	 * X coordinate of the ant in the world
+	 */
+	public double dx;
+	/**
+	 * Y coordinate of the ant in the world
+	 */
+	public double dy;
+	/**
+	 * Angle in radians
+	 */
+	private double angle = Math.random() * Math.PI * 2;
+	private DrawableImage image;
+
+	/**
+	 * Constructor of the ant
+	 * 
+	 * @param amas
+	 *            the amas the ant belongs to
+	 * @param startX
+	 *            Initial X coordinate
+	 * @param startY
+	 *            Initial Y coordinate
+	 */
+	public AntExampleMutliUI(AmasMultiUIWindow window, AntHillExampleMultiUI amas, double startX, double startY) {
+		super(window, amas, startX, startY);
+	}
+	@Override
+	public void onInitialization() {
+		dx = (double) params[0];
+		dy = (double) params[1];
+	}
+
+	@Override
+	protected void onRenderingInitialization() {
+		image =  getAmas().getVUIMulti().createAndAddImage(dx, dy, "file:resources/ant.png");
+		image.setName("Ant "+getId());
+	}
+
+	/**
+	 * Move in a random direction
+	 */
+	@Override
+	protected void onDecideAndAct() {
+
+		double random = amas.getEnvironment().getRandom().nextGaussian();
+		angle += random * 0.1;
+		dx += Math.cos(angle);
+		dy += Math.sin(angle);
+		while (dx >= getAmas().getEnvironment().getWidth() / 2)
+			dx -= getAmas().getEnvironment().getWidth();
+		while (dy >= getAmas().getEnvironment().getHeight() / 2)
+			dy -= getAmas().getEnvironment().getHeight();
+		while (dx < -getAmas().getEnvironment().getWidth() / 2)
+			dx += getAmas().getEnvironment().getWidth();
+		while (dy < -getAmas().getEnvironment().getHeight() / 2)
+			dy += getAmas().getEnvironment().getHeight();
+
+		if (amas.getEnvironment().getRandom().nextDouble() < 0.001) {
+			dead = true;
+			destroy();
+		}
+
+		if (amas.getEnvironment().getRandom().nextDouble() < 0.001) {
+			new AntExampleMutliUI(getAmas().amasMultiUIWindow, getAmas(), dx, dy);
+		}
+	}
+
+	@Override
+	public void onUpdateRender() {
+		image.move(dx, dy);
+		image.setAngle(angle);
+		image.setInfo("Ant "+getId()+"\nPosition "+dx+" "+dy+"\nAngle "+angle);
+		if(dead) {
+			image.setFilename("file:Resources/ant_dead.png");
+			image.setInfo("Ant "+getId()+"\nPosition "+dx+" "+dy+"\nAngle "+angle+"\nDead");
+		}
+	}
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntHillExampleMultiUI.java b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntHillExampleMultiUI.java
new file mode 100644
index 0000000000000000000000000000000000000000..344561afb24ba67df1e4fa1031eab902844ff01f
--- /dev/null
+++ b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntHillExampleMultiUI.java
@@ -0,0 +1,37 @@
+package fr.irit.smac.amak.examples.randomantsMultiUi;
+
+import fr.irit.smac.amak.Amas;
+import fr.irit.smac.amak.Scheduling;
+import fr.irit.smac.amak.tools.RunLaterHelper;
+import fr.irit.smac.amak.ui.AmasMultiUIWindow;
+import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.VUIMulti;
+import fr.irit.smac.amak.ui.drawables.DrawableString;
+
+public class AntHillExampleMultiUI extends Amas<WorldExampleMultiUI> {
+
+	private DrawableString antsCountLabel;
+
+	public AntHillExampleMultiUI(AmasMultiUIWindow window, VUIMulti vui, WorldExampleMultiUI env) {
+		super(window, vui, env, Scheduling.DEFAULT);
+	}
+
+	@Override
+	protected void onRenderingInitialization() {
+		vuiMulti.createAndAddImage(20, 20, "file:Resources/ant.png").setFixed().setLayer(10).setShowInExplorer(false);
+		antsCountLabel = (DrawableString) vuiMulti.createAndAddString(45, 25, "Ants count").setFixed().setLayer(10).setShowInExplorer(false);
+	}
+
+	@Override
+	protected void onInitialAgentsCreation() {
+		for (int i = 0; i < 50; i++) {
+			new AntExampleMutliUI(amasMultiUIWindow, this, 0, 0);
+		}
+			
+	}
+
+	@Override
+	protected void onSystemCycleEnd() {
+		RunLaterHelper.runLater(()->antsCountLabel.setText("Ants count: " + getAgents().size()));
+	}
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntsLaunchExampleMultiUI.java b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntsLaunchExampleMultiUI.java
new file mode 100644
index 0000000000000000000000000000000000000000..9fec860bf0a96823e3bebd1851b17bf05cad77a6
--- /dev/null
+++ b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntsLaunchExampleMultiUI.java
@@ -0,0 +1,111 @@
+package fr.irit.smac.amak.examples.randomantsMultiUi;
+
+import fr.irit.smac.amak.Configuration;
+import fr.irit.smac.amak.ui.AmasMultiUIWindow;
+import fr.irit.smac.amak.ui.MainWindow;
+import fr.irit.smac.amak.ui.VUIMulti;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.Slider;
+import javafx.scene.layout.Pane;
+import javafx.stage.Stage;
+
+public class AntsLaunchExampleMultiUI extends Application{
+
+	
+	public static void main (String[] args) {
+		
+		
+		Application.launch(args);
+		
+	
+	}
+
+	@Override
+	public void start(Stage primaryStage) throws Exception {
+		
+		Configuration.multiUI=true;
+		Configuration.commandLineMode =false;
+		
+		
+		AmasMultiUIWindow window = new AmasMultiUIWindow("Random Ants Multi UI 1");
+		AmasMultiUIWindow window2 = new AmasMultiUIWindow("Random Ants Multi UI 2");
+		
+		
+		WorldExampleMultiUI env = new WorldExampleMultiUI(window);
+		WorldExampleMultiUI env2 = new WorldExampleMultiUI(window2);
+		
+
+		AntHillExampleMultiUI ants = new AntHillExampleMultiUI(window, new VUIMulti("Ants VUI 1"), env);
+		AntHillExampleMultiUI ants2 = new AntHillExampleMultiUI(window2, new VUIMulti("Ants VUI 2"), env2);
+		
+		startTask(ants, 500, 10);
+		
+		startTask(ants2, 250, 30);
+		
+		
+		
+			
+	}
+	
+	public void startTask(AntHillExampleMultiUI amas, long wait, int cycles) 
+    {
+        // Create a Runnable
+        Runnable task = new Runnable()
+        {
+            public void run()
+            {
+                runTask(amas, wait, cycles);
+            }
+        };
+ 
+        // Run the task in a background thread
+        Thread backgroundThread = new Thread(task);
+        // Terminate the running thread if the application exits
+        backgroundThread.setDaemon(true);
+        // Start the thread
+        backgroundThread.start();
+    }
+	
+	public void runTask(AntHillExampleMultiUI amas, long wait, int cycles) 
+    {
+        for(int i = 0; i < cycles; i++) 
+        {
+            try
+            {
+                // Get the Status
+                final String status = "Processing " + i + " of " + cycles;
+                 
+                // Update the Label on the JavaFx Application Thread        
+                Platform.runLater(new Runnable() 
+                {
+                    @Override
+                    public void run() 
+                    {
+                    	amas.cycle();
+                    	System.out.println(status);
+                    }
+                });
+         
+                Thread.sleep(wait);
+            }
+            catch (InterruptedException e) 
+            {
+                e.printStackTrace();
+            }
+        }
+    }   
+
+	
+	@Override
+	public void stop() throws Exception {
+		super.stop();
+		System.exit(0);
+	}
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/WorldExampleMultiUI.java b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/WorldExampleMultiUI.java
new file mode 100644
index 0000000000000000000000000000000000000000..ffb5c0f1f034f6826152b0bcdd10c42356c5a186
--- /dev/null
+++ b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/WorldExampleMultiUI.java
@@ -0,0 +1,29 @@
+package fr.irit.smac.amak.examples.randomantsMultiUi;
+
+import fr.irit.smac.amak.Environment;
+import fr.irit.smac.amak.Scheduling;
+import fr.irit.smac.amak.ui.AmasMultiUIWindow;
+
+public class WorldExampleMultiUI extends Environment {
+	public WorldExampleMultiUI(AmasMultiUIWindow window, Object...params) {
+		super(window, Scheduling.DEFAULT, params);
+	}
+
+	private int width;
+	private int height;
+
+	public int getWidth() {
+		return width;
+	}
+
+	public int getHeight() {
+		return height;
+	}
+
+	@Override
+	public void onInitialization() {
+		this.width = 800;
+		this.height = 600;
+	}
+
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/ui/AmakPlot.java b/AMAKFX/src/fr/irit/smac/amak/ui/AmakPlot.java
index 60a58286244634a53702a0cea0126b6a792c29aa..a50dcea052955e308ffca135ce65c6d0998898a2 100644
--- a/AMAKFX/src/fr/irit/smac/amak/ui/AmakPlot.java
+++ b/AMAKFX/src/fr/irit/smac/amak/ui/AmakPlot.java
@@ -36,6 +36,10 @@ public class AmakPlot {
 	public static void add(AmakPlot chart) {
 		MainWindow.addTabbedPanel(chart.name, new ChartViewer(chart.chart));
 	}
+	
+	public static void add(AmasMultiUIWindow window, AmakPlot chart) {
+		window.addTabbedPanel(chart.name, new ChartViewer(chart.chart));
+	}
 	/* ----- */
 	
 	private String name;
@@ -119,6 +123,79 @@ public class AmakPlot {
 		this(name, chart, true);
 	}
 	
+	
+	/**
+	 * Create a chart
+	 * @param name the name of the chart, used as the tab name.
+	 * @param chartType {@link ChartType#LINE} or {@link ChartType#BAR}
+	 * @param xAxisLabel label for the x (horizontal) axis 
+	 * @param yAxisLabel label for the y (vertical) axis
+	 * @param autoAdd automatically make an {@link AmakPlot#add(AmakPlot)} call ?
+	 */
+	public AmakPlot(AmasMultiUIWindow window, String name, ChartType chartType, String xAxisLabel, String yAxisLabel, boolean autoAdd) {
+		this.name = name;
+		seriesCollection = new XYSeriesCollection();
+		switch (chartType) {
+		case BAR:
+			chart = ChartFactory.createXYBarChart(name, xAxisLabel, false, yAxisLabel, seriesCollection);
+			break;
+		case LINE:
+			chart = ChartFactory.createXYLineChart(name, xAxisLabel, yAxisLabel, seriesCollection);
+			if(useSamplingRenderer) {
+				chart.getXYPlot().setRenderer(new SamplingXYLineRenderer());
+			}
+			XYPlot plot = (XYPlot)chart.getPlot();
+			plot.setDomainGridlinesVisible(true);
+	        plot.setDomainGridlinePaint(Color.lightGray);
+	        plot.setRangeGridlinePaint(Color.lightGray);
+			break;
+		default:
+			System.err.println("AmakPlot : unknow ChartType \""+chartType+"\".");
+			break;
+		}
+		chart.setAntiAlias(false);
+		chart.getPlot().setBackgroundPaint(Color.WHITE);
+		if(autoAdd) {
+			add(window, this);
+		}
+	}
+	
+	/**
+	 * Create a chart and add it to the main window.
+	 * @param name the name of the chart, used as the tab name.
+	 * @param chartType {@link ChartType#LINE} or {@link ChartType#BAR}
+	 * @param xAxisLabel label for the x (horizontal) axis 
+	 * @param yAxisLabel label for the y (vertical) axis
+	 */
+	public AmakPlot(AmasMultiUIWindow window, String name, ChartType chartType, String xAxisLabel, String yAxisLabel) {
+		this(window, name, chartType, xAxisLabel, yAxisLabel, true);
+	}
+	
+	
+	/**
+	 * Create a chart out of a JFreeChart.
+	 * Make sure that your chart use an {@link XYSeriesCollection} as dataset.
+	 * @param name the name of the chart, used as the tab name.
+	 * @param chart the {@link JFreeChart} using a {@link XYSeriesCollection} for dataset.
+	 * @param autoAdd automatically make an {@link AmakPlot#add(AmakPlot)} call ?
+	 */
+	public AmakPlot(AmasMultiUIWindow window, String name, JFreeChart chart, boolean autoAdd) {
+		this.name = name;
+		this.seriesCollection = (XYSeriesCollection) chart.getXYPlot().getDataset();
+		this.chart = chart;
+		add(window, this);
+	}
+	
+	/**
+	 * Create a chart out of a JFreeChart and add it to the main window.
+	 * Make sure that your chart use an {@link XYSeriesCollection} as dataset.
+	 * @param name the name of the chart, used as the tab name.
+	 * @param chart the {@link JFreeChart} using a {@link XYSeriesCollection} for dataset.
+	 */
+	public AmakPlot(AmasMultiUIWindow window, String name, JFreeChart chart) {
+		this(window, name, chart, true);
+	}
+	
 	public String getName() {
 		return name;
 	}
diff --git a/AMAKFX/src/fr/irit/smac/amak/ui/AmasMultiUIWindow.java b/AMAKFX/src/fr/irit/smac/amak/ui/AmasMultiUIWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9519a5c743659f6affa4b5a278d1659ffd7eda8
--- /dev/null
+++ b/AMAKFX/src/fr/irit/smac/amak/ui/AmasMultiUIWindow.java
@@ -0,0 +1,252 @@
+package fr.irit.smac.amak.ui;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.management.InstanceAlreadyExistsException;
+
+import fr.irit.smac.amak.Information;
+import fr.irit.smac.amak.tools.RunLaterHelper;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.Menu;
+import javafx.scene.control.MenuBar;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TabPane;
+import javafx.scene.control.ToolBar;
+import javafx.scene.image.Image;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+import javafx.stage.WindowEvent;
+
+/**
+ * This window is the main one of an AMAS developed using AMAK. It contains a
+ * toolbar panel and various spaces for panels
+ * 
+ * @author of the original version (the Swing one) Alexandre Perles, Marcillaud
+ *         Guilhem
+ *
+ */
+public class AmasMultiUIWindow extends Stage{
+//	/**
+//	 * The window itself
+//	 */
+//	public Stage stage;
+	/**
+	 * The panel which contains the toolbar
+	 */
+	public ToolBar toolbarPanel;
+
+	/**
+	 * The main pane of AMAK
+	 */
+	public BorderPane organizationPane;
+	
+	/**
+	 * The menu bar of the window
+	 */
+	public MenuBar menuBar;
+	/**
+	 * The menus
+	 */
+	public HashMap<String, Menu> menus = new HashMap<String, Menu>();
+	/**
+	 * The panel in which panels with tab can be added
+	 */
+	public TabPane tabbedPanel;
+
+	
+
+	/**
+	 * Create the frame.
+	 * 
+	 * @throws InstanceAlreadyExistsException
+	 *             if the MainWindow has already been instantiated. This constructor
+	 *             should be used by the Application of JavaFX only.
+	 */
+	public AmasMultiUIWindow(String title) {
+
+		RunLaterHelper.runLater(() -> {	
+			
+		VBox root = new VBox();
+		
+		// Creation of the menu bar (Top)
+		menuBar = new MenuBar();
+		root.getChildren().add(menuBar);
+		
+		// Border organization
+		organizationPane = new BorderPane();
+		organizationPane.setMinSize(200, 200); //that way we avoid 0 size, which can cause problems
+		root.getChildren().add(organizationPane);
+		VBox.setVgrow(organizationPane, Priority.ALWAYS);
+		
+		// Creation of scene
+		this.setTitle(title);
+		Scene scene = new Scene(root, 450, 300);
+		//stage = primaryStage;
+		this.setScene(scene);
+		this.setOnCloseRequest(new EventHandler<WindowEvent>() {
+			@Override
+			public void handle(WindowEvent event) {
+				Platform.exit();
+			}
+		});
+
+		// Creation of the toolbar (Bottom)
+		toolbarPanel = new ToolBar();
+		organizationPane.setBottom(toolbarPanel);
+
+		// Creation of the right part of the split pane (Center Right)
+		tabbedPanel = new TabPane();
+		organizationPane.setCenter(tabbedPanel);
+
+		// Creation of the close menu item
+		MenuItem menuItem = new MenuItem("Close");
+		menuItem.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				System.exit(0);
+			}
+		});
+		addToMenu("Options", menuItem);
+
+		menuBar.getMenus().add(new Menu("AMAKFX v" + Information.VERSION));
+
+		this.show();
+			
+		});	
+
+	}
+
+//	@Override
+//	public void start(Stage primaryStage) throws Exception {
+//		
+//	}
+
+	/**
+	 * Add an action when the JavaFX app close.
+	 * 
+	 * @param onClose
+	 *            The action to be executed when the window is closed
+	 */
+	public static void addOnCloseAction(Runnable onClose) {
+		Runtime.getRuntime().addShutdownHook(new Thread() {
+		    public void run() { onClose.run(); }
+		});
+	}
+	
+//	@Override
+//	public void stop() throws Exception {
+//		super.stop();
+//		System.exit(0);
+//	}
+
+	/**
+	 * Change the icon of the window
+	 * 
+	 * @param filename
+	 *            The filename of the icon
+	 */
+	public  void setWindowIcon(String filename) {
+		RunLaterHelper.runLater(() -> this.getIcons().add(new Image(filename)));
+	}
+
+	/**
+	 * Change the title of the main window
+	 * 
+	 * @param title
+	 *            The new title
+	 */
+	public void setWindowTitle(String title) {
+		RunLaterHelper.runLater(() -> this.setTitle(title));
+	}
+	
+	/**
+	 * Add a button in the menu options
+	 * 
+	 * @param title
+	 *            The title of the button
+	 * @param event
+	 *            The action to be executed
+	 */
+	public  void addOptionsItem(String title, EventHandler<ActionEvent> event) {
+		MenuItem menuItem = new MenuItem(title);
+		menuItem.setOnAction(event);
+		RunLaterHelper.runLater(() -> addToMenu("Options", menuItem));
+	}
+
+	/**
+	 * Add a tool in the toolbar.
+	 * 
+	 * @param tool
+	 */
+	public  void addToolbar(Node tool) {
+		RunLaterHelper.runLater(() -> toolbarPanel.getItems().add(tool));
+	}
+
+	/**
+	 * Set a panel to the left
+	 * 
+	 * @param panel
+	 *            The panel
+	 */
+	public void setLeftPanel(Node panel) {
+		RunLaterHelper.runLater(() -> organizationPane.setLeft(panel));
+	}
+
+	/**
+	 * Set a panel to the right
+	 * 
+	 * @param panel
+	 *            The panel
+	 */
+	public void setRightPanel(Node panel) {
+		RunLaterHelper.runLater(() -> organizationPane.setRight(panel));
+	}
+
+	/**
+	 * Return the unique instance of MainWindow, may create it.
+	 * 
+	 * @return instance
+	 */
+	
+	
+
+
+	/**
+	 * Add a panel with a tab
+	 * 
+	 * @param title
+	 *            The title of the tab
+	 * @param panel
+	 *            The panel to add
+	 */
+	public void addTabbedPanel(String title, Node panel) {
+		Tab t = new DraggableTab(title, panel);
+		RunLaterHelper.runLater(() -> tabbedPanel.getTabs().add(t));
+	}
+	
+	/**
+	 * Add a {@link MenuItem} to a {@link Menu}. May create the menu and add it to the menu bar.
+	 * @param menuName the name of the menu where the item will be added.
+	 * @param item the item to be added.
+	 */
+	public void addToMenu(String menuName, MenuItem item) {
+		//instance();
+		if( !menus.containsKey(menuName) ) {
+			Menu m = new Menu(menuName);
+			menus.put(menuName,m);
+			RunLaterHelper.runLater(() -> menuBar.getMenus().add(m));
+		}
+		RunLaterHelper.runLater(() -> menus.get(menuName).getItems().add(item));
+	}
+}
\ No newline at end of file
diff --git a/AMAKFX/src/fr/irit/smac/amak/ui/VUI.java b/AMAKFX/src/fr/irit/smac/amak/ui/VUI.java
index 136cb472ac33fbffb9e2f7c7ddda551fe2384d81..2037681065b7736b2c48bb4eea8348a843194faf 100644
--- a/AMAKFX/src/fr/irit/smac/amak/ui/VUI.java
+++ b/AMAKFX/src/fr/irit/smac/amak/ui/VUI.java
@@ -1,577 +1,579 @@
-package fr.irit.smac.amak.ui;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.locks.ReentrantLock;
-
-import fr.irit.smac.amak.tools.RunLaterHelper;
-import fr.irit.smac.amak.ui.drawables.Drawable;
-import fr.irit.smac.amak.ui.drawables.DrawableImage;
-import fr.irit.smac.amak.ui.drawables.DrawablePoint;
-import fr.irit.smac.amak.ui.drawables.DrawableRectangle;
-import fr.irit.smac.amak.ui.drawables.DrawableString;
-import javafx.event.ActionEvent;
-import javafx.event.EventHandler;
-import javafx.geometry.Insets;
-import javafx.scene.control.Button;
-import javafx.scene.control.Label;
-import javafx.scene.control.ToolBar;
-import javafx.scene.control.Tooltip;
-import javafx.scene.input.MouseEvent;
-import javafx.scene.input.ScrollEvent;
-import javafx.scene.layout.Background;
-import javafx.scene.layout.BackgroundFill;
-import javafx.scene.layout.BorderPane;
-import javafx.scene.layout.CornerRadii;
-import javafx.scene.layout.Pane;
-import javafx.scene.paint.Color;
-import javafx.scene.shape.Rectangle;
-import javafx.scene.text.TextAlignment;
-
-/**
- * 
- * Vectorial UI: This class allows to create dynamic rendering with zoom and
- * move capacities
- * 
- * @author of original version (the Swing one) perles
- *
- */
-public class VUI {
-	/**
-	 * The toolbar of the VUI.
-	 */
-	public ToolBar toolbar;
-	
-	/**
-	 * The VUI explorer.
-	 * @see VuiExplorer
-	 */
-	private VuiExplorer vuiExplorer;
-	
-	/**
-	 * List of objects currently being drawn by the VUI
-	 */
-	private List<Drawable> drawables = new LinkedList<>();
-	/**
-	 * Lock to avoid concurrent modification on the list {@link #drawables}
-	 */
-	private ReentrantLock drawablesLock = new ReentrantLock();
-
-	/**
-	 * A static map to facilitate access to different instances of VUI
-	 */
-	private static Map<String, VUI> instances = new HashMap<>();
-
-	/**
-	 * The horizontal offset of the drawing zone. Used to allow the user to move the
-	 * view.
-	 */
-	private double worldOffsetX;
-
-	/**
-	 * The vertical offset of the drawing zone. Used to allow the user to move the
-	 * view.
-	 */
-	private double worldOffsetY;
-
-	/**
-	 * The last horizontal position of the mouse when dragging
-	 */
-	protected Double lastDragX;
-
-	/**
-	 * The last vertical position of the mouse when dragging
-	 */
-	protected Double lastDragY;
-
-	/**
-	 * The main panel of the VUI
-	 */
-	private BorderPane panel;
-
-	/**
-	 * The canvas on which all is drawn
-	 */
-	private Pane canvas;
-
-	/**
-	 * Label aiming at showing information about the VUI (zoom and offset)
-	 */
-	private Label statusLabel;
-
-	/**
-	 * The default value of the {@link #zoom}
-	 */
-	private double defaultZoom = 100;
-	/**
-	 * The default horizontal position of the view
-	 */
-	private double defaultWorldCenterX = 0;
-	/**
-	 * The default vertical position of the view
-	 */
-	private double defaultWorldCenterY = 0;
-	/**
-	 * The value of the zoom. 100 means 1/1 scale
-	 */
-	protected double zoom = defaultZoom;
-
-	/**
-	 * The horizontal position of the view
-	 */
-	private double worldCenterX = defaultWorldCenterX;
-
-	/**
-	 * The vertical position of the view
-	 */
-	private double worldCenterY = defaultWorldCenterY;
-
-	/**
-	 * Used to be sure that only one thread at the same time create a VUI
-	 */
-	private static ReentrantLock instanceLock = new ReentrantLock();
-
-	/**
-	 * Get the default VUI
-	 * 
-	 * @return the default VUI
-	 */
-	public static VUI get() {
-		if(!instances.containsKey("Default"))
-			MainWindow.addTabbedPanel("Default VUI", get("Default").getPanel());
-		return get("Default");
-	}
-
-	/**
-	 * Create or get a VUI.<br/>
-	 * You have add its panel to the MainWindow yourself.
-	 * 
-	 * @param id
-	 *            The unique id of the VUI
-	 * @return The VUI with id "id"
-	 */
-	public static VUI get(String id) {
-		instanceLock.lock();
-		if (!instances.containsKey(id)) {
-			VUI value = new VUI(id);
-			instances.put(id, value);
-			instanceLock.unlock();
-			return value;
-		}
-		instanceLock.unlock();
-		return instances.get(id);
-	}
-
-	/**
-	 * Constructor of the VUI. This one is private as it can only be created through
-	 * static method.
-	 * 
-	 * @param title
-	 *            The title used for the vui
-	 */
-	private VUI(String title) {
-		Semaphore done = new Semaphore(0);
-		RunLaterHelper.runLater(() -> {
-			panel = new BorderPane();
-
-			toolbar = new ToolBar();
-			statusLabel = new Label("status");
-			statusLabel.setTextAlignment(TextAlignment.LEFT);
-			toolbar.getItems().add(statusLabel);
-			panel.setBottom(toolbar);
-
-			Button resetButton = new Button("Reset");
-			resetButton.setOnAction(new EventHandler<ActionEvent>() {
-				@Override
-				public void handle(ActionEvent event) {
-					zoom = defaultZoom;
-					worldCenterX = defaultWorldCenterX;
-					worldCenterY = defaultWorldCenterY;
-					updateCanvas();
-				}
-			});
-			toolbar.getItems().add(resetButton);
-
-			canvas = new Pane();
-			canvas.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
-			// clip the canvas (avoid drawing outside of it)
-			Rectangle clip = new Rectangle(0, 0, 0, 0);
-			clip.widthProperty().bind(canvas.widthProperty());
-			clip.heightProperty().bind(canvas.heightProperty());
-			canvas.setClip(clip);
-			
-			canvas.setOnMousePressed(new EventHandler<MouseEvent>() {
-				@Override
-				public void handle(MouseEvent event) {
-					lastDragX = event.getX();
-					lastDragY = event.getY();
-				}
-			});
-			canvas.setOnMouseExited(new EventHandler<MouseEvent>() {
-				@Override
-				public void handle(MouseEvent event) {
-					lastDragX = null;
-					lastDragY = null;
-				}
-			});
-			canvas.setOnMouseDragged(new EventHandler<MouseEvent>() {
-				@Override
-				public void handle(MouseEvent event) {
-					try {
-						double transX = screenToWorldDistance(event.getX() - lastDragX);
-						double transY = screenToWorldDistance(event.getY() - lastDragY);
-						worldCenterX += transX;
-						worldCenterY += transY;
-						worldOffsetX += transX;
-						worldOffsetY += transY;
-						lastDragX = event.getX();
-						lastDragY = event.getY();
-						updateCanvas();
-					} catch (Exception ez) {
-						// Catch exception occurring when mouse is out of the canvas
-					}
-				}
-			});
-
-			canvas.setOnScroll(new EventHandler<ScrollEvent>() {
-				@Override
-				public void handle(ScrollEvent event) {
-					double wdx = screenToWorldDistance(canvas.getWidth() / 2 - event.getX());
-					double wdy = screenToWorldDistance(canvas.getHeight() / 2 - event.getY());
-					zoom += event.getDeltaY() / event.getMultiplierY() * 10;
-					if (zoom < 10)
-						zoom = 10;
-
-					double wdx2 = screenToWorldDistance(canvas.getWidth() / 2 - event.getX());
-					double wdy2 = screenToWorldDistance(canvas.getHeight() / 2 - event.getY());
-					worldCenterX -= wdx2 - wdx;
-					worldCenterY -= wdy2 - wdy;
-					updateCanvas();
-				}
-			});
-
-			panel.setCenter(canvas);
-			
-			//add VuiExplorer
-			vuiExplorer = new VuiExplorer(this);
-			panel.setLeft(vuiExplorer);
-			Button veButton = new Button("VUI explorer");
-			veButton.setOnAction(new EventHandler<ActionEvent>() {
-				@Override
-				public void handle(ActionEvent event) {
-					panel.setLeft(vuiExplorer);
-				}
-			});
-			veButton.setTooltip(new Tooltip("Show the VUI explorer if it was hidden."));
-			toolbar.getItems().add(veButton);
-			
-			done.release();
-		});
-		try {
-			done.acquire();
-		} catch (InterruptedException e) {
-			System.err.println("Failed to make sure that the VUI is correctly initialized.");
-			e.printStackTrace();
-		}
-	}
-
-	/**
-	 * Convert a distance in the world to its equivalent on the screen
-	 * 
-	 * @param d
-	 *            the in world distance
-	 * @return the on screen distance
-	 */
-	public double worldToScreenDistance(double d) {
-		return d * getZoomFactor();
-	}
-
-	/**
-	 * Convert a distance on the screen to its equivalent in the world
-	 * 
-	 * @param d
-	 *            the on screen distance
-	 * @return the in world distance
-	 */
-	public double screenToWorldDistance(double d) {
-		return d / getZoomFactor();
-	}
-
-	/**
-	 * Convert a X in the world to its equivalent on the screen
-	 * 
-	 * @param x
-	 *            the X in world
-	 *
-	 * @return the X on screen distance
-	 */
-	public double worldToScreenX(double x) {
-		return (x + getWorldOffsetX()) * getZoomFactor();
-	}
-
-	/**
-	 * A value that must be multiplied to scale objects
-	 * 
-	 * @return the zoom factor
-	 */
-	public double getZoomFactor() {
-		return zoom / 100;
-	}
-
-	/**
-	 * Convert a Y in the world to its equivalent on the screen
-	 * 
-	 * @param y
-	 *            the Y in world
-	 *
-	 * @return the Y on screen distance
-	 */
-	public double worldToScreenY(double y) {
-		return (-y + getWorldOffsetY()) * getZoomFactor();
-	}
-
-	/**
-	 * Convert a X on the screen to its equivalent in the world
-	 * 
-	 * @param x
-	 *            the X on screen
-	 *
-	 * @return the X in the world distance
-	 */
-	public double screenToWorldX(double x) {
-		return x / getZoomFactor() - getWorldOffsetX();
-	}
-
-	/**
-	 * Convert a Y on the screen to its equivalent in the world
-	 * 
-	 * @param y
-	 *            the Y on screen
-	 *
-	 * @return the Y in the world distance
-	 */
-	public double screenToWorldY(double y) {
-		return -y / getZoomFactor() + getWorldOffsetY();
-	}
-
-	/**
-	 * Add a drawable to the VUI.
-	 * 
-	 * @param d
-	 *            the new drawable
-	 */
-	public void add(Drawable d) {
-		d.setVUI(this);
-		RunLaterHelper.runLater(()-> canvas.getChildren().add(d.getNode()));
-		drawablesLock.lock();
-		drawables.add(d);
-		drawablesLock.unlock();
-		updateCanvas();
-	}
-	
-	/**
-	 * Remove a drawable from the VUI.
-	 * 
-	 * @param d
-	 *            the new drawable
-	 */
-	public void remove(Drawable d) {
-		drawablesLock.lock();
-		drawables.remove(d);
-		drawablesLock.unlock();
-		RunLaterHelper.runLater(()-> canvas.getChildren().remove(d.getNode()));
-		updateCanvas();
-	}
-	
-	/**
-	 * Remove all drawables from the VUI.
-	 */
-	public void clear() {
-		drawablesLock.lock();
-		drawables.clear();
-		RunLaterHelper.runLater(()->canvas.getChildren().clear());
-		drawablesLock.unlock();
-	}
-
-	/**
-	 * Refresh the canvas
-	 */
-	public void updateCanvas() {
-		final double w = canvas.getWidth();
-		final double h = canvas.getHeight();
-
-		setWorldOffsetX(worldCenterX + screenToWorldDistance(w / 2));
-		setWorldOffsetY(worldCenterY + screenToWorldDistance(h / 2));
-
-		drawablesLock.lock();
-		Collections.sort(drawables, (o1, o2) -> o1.getLayer() - o2.getLayer());
-		for (Drawable d : drawables)
-			RunLaterHelper.runLater(()-> d.onDraw());
-		drawablesLock.unlock();
-
-		RunLaterHelper.runLater(() -> {
-			statusLabel.setText(String.format("Zoom: %.2f Center: (%.2f,%.2f)", zoom, worldCenterX, worldCenterY));
-		});
-		
-		RunLaterHelper.runLater(()-> vuiExplorer.update(true));
-	}
-
-	/**
-	 * Get the width of the canvas
-	 * 
-	 * @return the canvas width
-	 */
-	public double getCanvasWidth() {
-		return canvas.getWidth();
-	}
-
-	/**
-	 * Get the height of the canvas
-	 * 
-	 * @return the canvas height
-	 */
-	public double getCanvasHeight() {
-		return canvas.getHeight();
-	}
-
-	/**
-	 * Get the value that must be added to the X coordinate of in world object
-	 * 
-	 * @return the X offset
-	 */
-	public double getWorldOffsetX() {
-		return worldOffsetX;
-	}
-
-	/**
-	 * Set the value that must be added to the X coordinate of in world object
-	 * 
-	 * @param offsetX
-	 *            the X offset
-	 */
-	public void setWorldOffsetX(double offsetX) {
-		this.worldOffsetX = offsetX;
-	}
-
-	/**
-	 * Get the value that must be added to the Y coordinate of in world object
-	 * 
-	 * @return the Y offset
-	 */
-	public double getWorldOffsetY() {
-		return worldOffsetY;
-	}
-
-	/**
-	 * Set the value that must be added to the Y coordinate of in world object
-	 * 
-	 * @param offsetY
-	 *            the Y offset
-	 */
-	public void setWorldOffsetY(double offsetY) {
-		this.worldOffsetY = offsetY;
-	}
-
-	/**
-	 * Create a point and start rendering it
-	 * 
-	 * @param dx
-	 *            the x coordinate
-	 * @param dy
-	 *            the y coordinate
-	 * @return the point object
-	 */
-	public DrawablePoint createAndAddPoint(double dx, double dy) {
-		DrawablePoint drawablePoint = new DrawablePoint(dx, dy);
-		add(drawablePoint);
-		return drawablePoint;
-	}
-
-	/**
-	 * Create a rectangle and start rendering it
-	 * 
-	 * @param x
-	 *            the x coordinate
-	 * @param y
-	 *            the y coordinate
-	 * @param w
-	 *            the width
-	 * @param h
-	 *            the height
-	 * @return the rectangle object
-	 */
-	public DrawableRectangle createAndAddRectangle(double x, double y, double w, double h) {
-		DrawableRectangle d = new DrawableRectangle(x, y, w, h);
-		add(d);
-		return d;
-	}
-
-	/**
-	 * Set the default configuration of the view
-	 * 
-	 * @param zoom
-	 *            the initial zoom value
-	 * @param worldCenterX
-	 *            the initial X center value
-	 * @param worldCenterY
-	 *            the initial Y center value
-	 */
-	public void setDefaultView(double zoom, double worldCenterX, double worldCenterY) {
-		this.zoom = zoom;
-		this.worldCenterX = worldCenterX;
-		this.worldCenterY = worldCenterY;
-		this.defaultZoom = zoom;
-		this.defaultWorldCenterX = worldCenterX;
-		this.defaultWorldCenterY = worldCenterY;
-	}
-
-	/**
-	 * Create an image and start rendering it
-	 * 
-	 * @param dx
-	 *            the x coordinate
-	 * @param dy
-	 *            the y coordinate
-	 * @param filename
-	 *            the filename of the image
-	 * @return the created image
-	 */
-	public DrawableImage createAndAddImage(double dx, double dy, String filename) {
-		DrawableImage image = new DrawableImage(dx, dy, filename);
-		add(image);
-		return image;
-	}
-
-	/**
-	 * Create a string and start rendering it
-	 * 
-	 * @param dx
-	 *            the x coordinate
-	 * @param dy
-	 *            the y coordinate
-	 * @param text
-	 *            the text to display
-	 * @return the created string
-	 */
-	public DrawableString createAndAddString(int dx, int dy, String text) {
-		DrawableString ds = new DrawableString(dx, dy, text);
-		add(ds);
-		return ds;
-	}
-
-	public Pane getCanvas() {
-		return canvas;
-	}
-	
-	public BorderPane getPanel() {
-		return panel;
-	}
-	
-	public List<Drawable> getDrawables() {
-		return drawables;
-	}
-}
+package fr.irit.smac.amak.ui;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.locks.ReentrantLock;
+
+import fr.irit.smac.amak.tools.RunLaterHelper;
+import fr.irit.smac.amak.ui.drawables.Drawable;
+import fr.irit.smac.amak.ui.drawables.DrawableImage;
+import fr.irit.smac.amak.ui.drawables.DrawablePoint;
+import fr.irit.smac.amak.ui.drawables.DrawableRectangle;
+import fr.irit.smac.amak.ui.drawables.DrawableString;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Insets;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.ToolBar;
+import javafx.scene.control.Tooltip;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.input.ScrollEvent;
+import javafx.scene.layout.Background;
+import javafx.scene.layout.BackgroundFill;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.CornerRadii;
+import javafx.scene.layout.Pane;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.text.TextAlignment;
+
+/**
+ * 
+ * Vectorial UI: This class allows to create dynamic rendering with zoom and
+ * move capacities
+ * 
+ * @author of original version (the Swing one) perles
+ *
+ */
+public class VUI {
+	/**
+	 * The toolbar of the VUI.
+	 */
+	public ToolBar toolbar;
+	
+	/**
+	 * The VUI explorer.
+	 * @see VuiExplorer
+	 */
+	private VuiExplorer vuiExplorer;
+	
+	/**
+	 * List of objects currently being drawn by the VUI
+	 */
+	private List<Drawable> drawables = new LinkedList<>();
+	/**
+	 * Lock to avoid concurrent modification on the list {@link #drawables}
+	 */
+	private ReentrantLock drawablesLock = new ReentrantLock();
+
+	/**
+	 * A static map to facilitate access to different instances of VUI
+	 */
+	private static Map<String, VUI> instances = new HashMap<>();
+
+	/**
+	 * The horizontal offset of the drawing zone. Used to allow the user to move the
+	 * view.
+	 */
+	private double worldOffsetX;
+
+	/**
+	 * The vertical offset of the drawing zone. Used to allow the user to move the
+	 * view.
+	 */
+	private double worldOffsetY;
+
+	/**
+	 * The last horizontal position of the mouse when dragging
+	 */
+	protected Double lastDragX;
+
+	/**
+	 * The last vertical position of the mouse when dragging
+	 */
+	protected Double lastDragY;
+
+	/**
+	 * The main panel of the VUI
+	 */
+	private BorderPane panel;
+
+	/**
+	 * The canvas on which all is drawn
+	 */
+	private Pane canvas;
+
+	/**
+	 * Label aiming at showing information about the VUI (zoom and offset)
+	 */
+	private Label statusLabel;
+
+	/**
+	 * The default value of the {@link #zoom}
+	 */
+	private double defaultZoom = 100;
+	/**
+	 * The default horizontal position of the view
+	 */
+	private double defaultWorldCenterX = 0;
+	/**
+	 * The default vertical position of the view
+	 */
+	private double defaultWorldCenterY = 0;
+	/**
+	 * The value of the zoom. 100 means 1/1 scale
+	 */
+	protected double zoom = defaultZoom;
+
+	/**
+	 * The horizontal position of the view
+	 */
+	private double worldCenterX = defaultWorldCenterX;
+
+	/**
+	 * The vertical position of the view
+	 */
+	private double worldCenterY = defaultWorldCenterY;
+
+	/**
+	 * Used to be sure that only one thread at the same time create a VUI
+	 */
+	private static ReentrantLock instanceLock = new ReentrantLock();
+
+	/**
+	 * Get the default VUI
+	 * 
+	 * @return the default VUI
+	 */
+	public static VUI get() {
+		if(!instances.containsKey("Default"))
+			MainWindow.addTabbedPanel("Default VUI", get("Default").getPanel());
+		return get("Default");
+	}
+	
+	
+
+	/**
+	 * Create or get a VUI.<br/>
+	 * You have add its panel to the MainWindow yourself.
+	 * 
+	 * @param id
+	 *            The unique id of the VUI
+	 * @return The VUI with id "id"
+	 */
+	public static VUI get(String id) {
+		instanceLock.lock();
+		if (!instances.containsKey(id)) {
+			VUI value = new VUI(id);
+			instances.put(id, value);
+			instanceLock.unlock();
+			return value;
+		}
+		instanceLock.unlock();
+		return instances.get(id);
+	}
+
+	/**
+	 * Constructor of the VUI. This one is private as it can only be created through
+	 * static method.
+	 * 
+	 * @param title
+	 *            The title used for the vui
+	 */
+	private VUI(String title) {
+		Semaphore done = new Semaphore(0);
+		RunLaterHelper.runLater(() -> {
+			panel = new BorderPane();
+
+			toolbar = new ToolBar();
+			statusLabel = new Label("status");
+			statusLabel.setTextAlignment(TextAlignment.LEFT);
+			toolbar.getItems().add(statusLabel);
+			panel.setBottom(toolbar);
+
+			Button resetButton = new Button("Reset");
+			resetButton.setOnAction(new EventHandler<ActionEvent>() {
+				@Override
+				public void handle(ActionEvent event) {
+					zoom = defaultZoom;
+					worldCenterX = defaultWorldCenterX;
+					worldCenterY = defaultWorldCenterY;
+					updateCanvas();
+				}
+			});
+			toolbar.getItems().add(resetButton);
+
+			canvas = new Pane();
+			canvas.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
+			// clip the canvas (avoid drawing outside of it)
+			Rectangle clip = new Rectangle(0, 0, 0, 0);
+			clip.widthProperty().bind(canvas.widthProperty());
+			clip.heightProperty().bind(canvas.heightProperty());
+			canvas.setClip(clip);
+			
+			canvas.setOnMousePressed(new EventHandler<MouseEvent>() {
+				@Override
+				public void handle(MouseEvent event) {
+					lastDragX = event.getX();
+					lastDragY = event.getY();
+				}
+			});
+			canvas.setOnMouseExited(new EventHandler<MouseEvent>() {
+				@Override
+				public void handle(MouseEvent event) {
+					lastDragX = null;
+					lastDragY = null;
+				}
+			});
+			canvas.setOnMouseDragged(new EventHandler<MouseEvent>() {
+				@Override
+				public void handle(MouseEvent event) {
+					try {
+						double transX = screenToWorldDistance(event.getX() - lastDragX);
+						double transY = screenToWorldDistance(event.getY() - lastDragY);
+						worldCenterX += transX;
+						worldCenterY += transY;
+						worldOffsetX += transX;
+						worldOffsetY += transY;
+						lastDragX = event.getX();
+						lastDragY = event.getY();
+						updateCanvas();
+					} catch (Exception ez) {
+						// Catch exception occurring when mouse is out of the canvas
+					}
+				}
+			});
+
+			canvas.setOnScroll(new EventHandler<ScrollEvent>() {
+				@Override
+				public void handle(ScrollEvent event) {
+					double wdx = screenToWorldDistance(canvas.getWidth() / 2 - event.getX());
+					double wdy = screenToWorldDistance(canvas.getHeight() / 2 - event.getY());
+					zoom += event.getDeltaY() / event.getMultiplierY() * 10;
+					if (zoom < 10)
+						zoom = 10;
+
+					double wdx2 = screenToWorldDistance(canvas.getWidth() / 2 - event.getX());
+					double wdy2 = screenToWorldDistance(canvas.getHeight() / 2 - event.getY());
+					worldCenterX -= wdx2 - wdx;
+					worldCenterY -= wdy2 - wdy;
+					updateCanvas();
+				}
+			});
+
+			panel.setCenter(canvas);
+			
+			//add VuiExplorer
+			vuiExplorer = new VuiExplorer(this);
+			panel.setLeft(vuiExplorer);
+			Button veButton = new Button("VUI explorer");
+			veButton.setOnAction(new EventHandler<ActionEvent>() {
+				@Override
+				public void handle(ActionEvent event) {
+					panel.setLeft(vuiExplorer);
+				}
+			});
+			veButton.setTooltip(new Tooltip("Show the VUI explorer if it was hidden."));
+			toolbar.getItems().add(veButton);
+			
+			done.release();
+		});
+		try {
+			done.acquire();
+		} catch (InterruptedException e) {
+			System.err.println("Failed to make sure that the VUI is correctly initialized.");
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Convert a distance in the world to its equivalent on the screen
+	 * 
+	 * @param d
+	 *            the in world distance
+	 * @return the on screen distance
+	 */
+	public double worldToScreenDistance(double d) {
+		return d * getZoomFactor();
+	}
+
+	/**
+	 * Convert a distance on the screen to its equivalent in the world
+	 * 
+	 * @param d
+	 *            the on screen distance
+	 * @return the in world distance
+	 */
+	public double screenToWorldDistance(double d) {
+		return d / getZoomFactor();
+	}
+
+	/**
+	 * Convert a X in the world to its equivalent on the screen
+	 * 
+	 * @param x
+	 *            the X in world
+	 *
+	 * @return the X on screen distance
+	 */
+	public double worldToScreenX(double x) {
+		return (x + getWorldOffsetX()) * getZoomFactor();
+	}
+
+	/**
+	 * A value that must be multiplied to scale objects
+	 * 
+	 * @return the zoom factor
+	 */
+	public double getZoomFactor() {
+		return zoom / 100;
+	}
+
+	/**
+	 * Convert a Y in the world to its equivalent on the screen
+	 * 
+	 * @param y
+	 *            the Y in world
+	 *
+	 * @return the Y on screen distance
+	 */
+	public double worldToScreenY(double y) {
+		return (-y + getWorldOffsetY()) * getZoomFactor();
+	}
+
+	/**
+	 * Convert a X on the screen to its equivalent in the world
+	 * 
+	 * @param x
+	 *            the X on screen
+	 *
+	 * @return the X in the world distance
+	 */
+	public double screenToWorldX(double x) {
+		return x / getZoomFactor() - getWorldOffsetX();
+	}
+
+	/**
+	 * Convert a Y on the screen to its equivalent in the world
+	 * 
+	 * @param y
+	 *            the Y on screen
+	 *
+	 * @return the Y in the world distance
+	 */
+	public double screenToWorldY(double y) {
+		return -y / getZoomFactor() + getWorldOffsetY();
+	}
+
+	/**
+	 * Add a drawable to the VUI.
+	 * 
+	 * @param d
+	 *            the new drawable
+	 */
+	public void add(Drawable d) {
+		d.setVUI(this);
+		RunLaterHelper.runLater(()-> canvas.getChildren().add(d.getNode()));
+		drawablesLock.lock();
+		drawables.add(d);
+		drawablesLock.unlock();
+		updateCanvas();
+	}
+	
+	/**
+	 * Remove a drawable from the VUI.
+	 * 
+	 * @param d
+	 *            the new drawable
+	 */
+	public void remove(Drawable d) {
+		drawablesLock.lock();
+		drawables.remove(d);
+		drawablesLock.unlock();
+		RunLaterHelper.runLater(()-> canvas.getChildren().remove(d.getNode()));
+		updateCanvas();
+	}
+	
+	/**
+	 * Remove all drawables from the VUI.
+	 */
+	public void clear() {
+		drawablesLock.lock();
+		drawables.clear();
+		RunLaterHelper.runLater(()->canvas.getChildren().clear());
+		drawablesLock.unlock();
+	}
+
+	/**
+	 * Refresh the canvas
+	 */
+	public void updateCanvas() {
+		final double w = canvas.getWidth();
+		final double h = canvas.getHeight();
+
+		setWorldOffsetX(worldCenterX + screenToWorldDistance(w / 2));
+		setWorldOffsetY(worldCenterY + screenToWorldDistance(h / 2));
+
+		drawablesLock.lock();
+		Collections.sort(drawables, (o1, o2) -> o1.getLayer() - o2.getLayer());
+		for (Drawable d : drawables)
+			RunLaterHelper.runLater(()-> d.onDraw());
+		drawablesLock.unlock();
+
+		RunLaterHelper.runLater(() -> {
+			statusLabel.setText(String.format("Zoom: %.2f Center: (%.2f,%.2f)", zoom, worldCenterX, worldCenterY));
+		});
+		
+		RunLaterHelper.runLater(()-> vuiExplorer.update(true));
+	}
+
+	/**
+	 * Get the width of the canvas
+	 * 
+	 * @return the canvas width
+	 */
+	public double getCanvasWidth() {
+		return canvas.getWidth();
+	}
+
+	/**
+	 * Get the height of the canvas
+	 * 
+	 * @return the canvas height
+	 */
+	public double getCanvasHeight() {
+		return canvas.getHeight();
+	}
+
+	/**
+	 * Get the value that must be added to the X coordinate of in world object
+	 * 
+	 * @return the X offset
+	 */
+	public double getWorldOffsetX() {
+		return worldOffsetX;
+	}
+
+	/**
+	 * Set the value that must be added to the X coordinate of in world object
+	 * 
+	 * @param offsetX
+	 *            the X offset
+	 */
+	public void setWorldOffsetX(double offsetX) {
+		this.worldOffsetX = offsetX;
+	}
+
+	/**
+	 * Get the value that must be added to the Y coordinate of in world object
+	 * 
+	 * @return the Y offset
+	 */
+	public double getWorldOffsetY() {
+		return worldOffsetY;
+	}
+
+	/**
+	 * Set the value that must be added to the Y coordinate of in world object
+	 * 
+	 * @param offsetY
+	 *            the Y offset
+	 */
+	public void setWorldOffsetY(double offsetY) {
+		this.worldOffsetY = offsetY;
+	}
+
+	/**
+	 * Create a point and start rendering it
+	 * 
+	 * @param dx
+	 *            the x coordinate
+	 * @param dy
+	 *            the y coordinate
+	 * @return the point object
+	 */
+	public DrawablePoint createAndAddPoint(double dx, double dy) {
+		DrawablePoint drawablePoint = new DrawablePoint(dx, dy);
+		add(drawablePoint);
+		return drawablePoint;
+	}
+
+	/**
+	 * Create a rectangle and start rendering it
+	 * 
+	 * @param x
+	 *            the x coordinate
+	 * @param y
+	 *            the y coordinate
+	 * @param w
+	 *            the width
+	 * @param h
+	 *            the height
+	 * @return the rectangle object
+	 */
+	public DrawableRectangle createAndAddRectangle(double x, double y, double w, double h) {
+		DrawableRectangle d = new DrawableRectangle(x, y, w, h);
+		add(d);
+		return d;
+	}
+
+	/**
+	 * Set the default configuration of the view
+	 * 
+	 * @param zoom
+	 *            the initial zoom value
+	 * @param worldCenterX
+	 *            the initial X center value
+	 * @param worldCenterY
+	 *            the initial Y center value
+	 */
+	public void setDefaultView(double zoom, double worldCenterX, double worldCenterY) {
+		this.zoom = zoom;
+		this.worldCenterX = worldCenterX;
+		this.worldCenterY = worldCenterY;
+		this.defaultZoom = zoom;
+		this.defaultWorldCenterX = worldCenterX;
+		this.defaultWorldCenterY = worldCenterY;
+	}
+
+	/**
+	 * Create an image and start rendering it
+	 * 
+	 * @param dx
+	 *            the x coordinate
+	 * @param dy
+	 *            the y coordinate
+	 * @param filename
+	 *            the filename of the image
+	 * @return the created image
+	 */
+	public DrawableImage createAndAddImage(double dx, double dy, String filename) {
+		DrawableImage image = new DrawableImage(dx, dy, filename);
+		add(image);
+		return image;
+	}
+
+	/**
+	 * Create a string and start rendering it
+	 * 
+	 * @param dx
+	 *            the x coordinate
+	 * @param dy
+	 *            the y coordinate
+	 * @param text
+	 *            the text to display
+	 * @return the created string
+	 */
+	public DrawableString createAndAddString(int dx, int dy, String text) {
+		DrawableString ds = new DrawableString(dx, dy, text);
+		add(ds);
+		return ds;
+	}
+
+	public Pane getCanvas() {
+		return canvas;
+	}
+	
+	public BorderPane getPanel() {
+		return panel;
+	}
+	
+	public List<Drawable> getDrawables() {
+		return drawables;
+	}
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/ui/VUIMulti.java b/AMAKFX/src/fr/irit/smac/amak/ui/VUIMulti.java
new file mode 100644
index 0000000000000000000000000000000000000000..eeeb4862787055d93d93e4eb772108c06dab4e44
--- /dev/null
+++ b/AMAKFX/src/fr/irit/smac/amak/ui/VUIMulti.java
@@ -0,0 +1,546 @@
+package fr.irit.smac.amak.ui;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.locks.ReentrantLock;
+
+import fr.irit.smac.amak.tools.RunLaterHelper;
+import fr.irit.smac.amak.ui.drawables.Drawable;
+import fr.irit.smac.amak.ui.drawables.DrawableImage;
+import fr.irit.smac.amak.ui.drawables.DrawablePoint;
+import fr.irit.smac.amak.ui.drawables.DrawableRectangle;
+import fr.irit.smac.amak.ui.drawables.DrawableString;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Insets;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.ToolBar;
+import javafx.scene.control.Tooltip;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.input.ScrollEvent;
+import javafx.scene.layout.Background;
+import javafx.scene.layout.BackgroundFill;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.CornerRadii;
+import javafx.scene.layout.Pane;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.text.TextAlignment;
+
+/**
+ * 
+ * Vectorial UI: This class allows to create dynamic rendering with zoom and
+ * move capacities
+ * 
+ * @author of original version (the Swing one) perles
+ *
+ */
+public class VUIMulti {
+	
+	public String title;
+	
+	/**
+	 * The toolbar of the VUI.
+	 */
+	public ToolBar toolbar;
+	
+	/**
+	 * The VUI explorer.
+	 * @see VuiExplorer
+	 */
+	private VuiExplorer vuiExplorer;
+	
+	/**
+	 * List of objects currently being drawn by the VUI
+	 */
+	private List<Drawable> drawables = new LinkedList<>();
+	/**
+	 * Lock to avoid concurrent modification on the list {@link #drawables}
+	 */
+	private ReentrantLock drawablesLock = new ReentrantLock();
+
+	/**
+	 * A static map to facilitate access to different instances of VUI
+	 */
+	//private static Map<String, VUIMulti> instances = new HashMap<>();
+
+	/**
+	 * The horizontal offset of the drawing zone. Used to allow the user to move the
+	 * view.
+	 */
+	private double worldOffsetX;
+
+	/**
+	 * The vertical offset of the drawing zone. Used to allow the user to move the
+	 * view.
+	 */
+	private double worldOffsetY;
+
+	/**
+	 * The last horizontal position of the mouse when dragging
+	 */
+	protected Double lastDragX;
+
+	/**
+	 * The last vertical position of the mouse when dragging
+	 */
+	protected Double lastDragY;
+
+	/**
+	 * The main panel of the VUI
+	 */
+	private BorderPane panel;
+
+	/**
+	 * The canvas on which all is drawn
+	 */
+	private Pane canvas;
+
+	/**
+	 * Label aiming at showing information about the VUI (zoom and offset)
+	 */
+	private Label statusLabel;
+
+	/**
+	 * The default value of the {@link #zoom}
+	 */
+	private double defaultZoom = 100;
+	/**
+	 * The default horizontal position of the view
+	 */
+	private double defaultWorldCenterX = 0;
+	/**
+	 * The default vertical position of the view
+	 */
+	private double defaultWorldCenterY = 0;
+	/**
+	 * The value of the zoom. 100 means 1/1 scale
+	 */
+	protected double zoom = defaultZoom;
+
+	/**
+	 * The horizontal position of the view
+	 */
+	private double worldCenterX = defaultWorldCenterX;
+
+	/**
+	 * The vertical position of the view
+	 */
+	private double worldCenterY = defaultWorldCenterY;
+
+	
+	
+
+	
+	/**
+	 * Constructor of the VUI. This one is private as it can only be created through
+	 * static method.
+	 * 
+	 * @param title
+	 *            The title used for the vui
+	 */
+	public VUIMulti(String titleValue) {
+		
+		RunLaterHelper.runLater(() -> {
+		
+		this.title = titleValue;
+		panel = new BorderPane();
+
+		toolbar = new ToolBar();
+		statusLabel = new Label("status");
+		statusLabel.setTextAlignment(TextAlignment.LEFT);
+		toolbar.getItems().add(statusLabel);
+		panel.setBottom(toolbar);
+
+		Button resetButton = new Button("Reset");
+		resetButton.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				zoom = defaultZoom;
+				worldCenterX = defaultWorldCenterX;
+				worldCenterY = defaultWorldCenterY;
+				updateCanvas();
+			}
+		});
+		toolbar.getItems().add(resetButton);
+
+		canvas = new Pane();
+		canvas.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
+		// clip the canvas (avoid drawing outside of it)
+		Rectangle clip = new Rectangle(0, 0, 0, 0);
+		clip.widthProperty().bind(canvas.widthProperty());
+		clip.heightProperty().bind(canvas.heightProperty());
+		canvas.setClip(clip);
+		
+		canvas.setOnMousePressed(new EventHandler<MouseEvent>() {
+			@Override
+			public void handle(MouseEvent event) {
+				lastDragX = event.getX();
+				lastDragY = event.getY();
+			}
+		});
+		canvas.setOnMouseExited(new EventHandler<MouseEvent>() {
+			@Override
+			public void handle(MouseEvent event) {
+				lastDragX = null;
+				lastDragY = null;
+			}
+		});
+		canvas.setOnMouseDragged(new EventHandler<MouseEvent>() {
+			@Override
+			public void handle(MouseEvent event) {
+				try {
+					double transX = screenToWorldDistance(event.getX() - lastDragX);
+					double transY = screenToWorldDistance(event.getY() - lastDragY);
+					worldCenterX += transX;
+					worldCenterY += transY;
+					worldOffsetX += transX;
+					worldOffsetY += transY;
+					lastDragX = event.getX();
+					lastDragY = event.getY();
+					updateCanvas();
+				} catch (Exception ez) {
+					// Catch exception occurring when mouse is out of the canvas
+				}
+			}
+		});
+
+		canvas.setOnScroll(new EventHandler<ScrollEvent>() {
+			@Override
+			public void handle(ScrollEvent event) {
+				double wdx = screenToWorldDistance(canvas.getWidth() / 2 - event.getX());
+				double wdy = screenToWorldDistance(canvas.getHeight() / 2 - event.getY());
+				zoom += event.getDeltaY() / event.getMultiplierY() * 10;
+				if (zoom < 10)
+					zoom = 10;
+
+				double wdx2 = screenToWorldDistance(canvas.getWidth() / 2 - event.getX());
+				double wdy2 = screenToWorldDistance(canvas.getHeight() / 2 - event.getY());
+				worldCenterX -= wdx2 - wdx;
+				worldCenterY -= wdy2 - wdy;
+				updateCanvas();
+			}
+		});
+
+		panel.setCenter(canvas);
+		
+		//add VuiExplorer
+		vuiExplorer = new VuiExplorer(this);
+		panel.setLeft(vuiExplorer);
+		Button veButton = new Button("VUI explorer");
+		veButton.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				panel.setLeft(vuiExplorer);
+			}
+		});
+		veButton.setTooltip(new Tooltip("Show the VUI explorer if it was hidden."));
+		toolbar.getItems().add(veButton);
+			
+
+		});	
+		
+	}
+
+	/**
+	 * Convert a distance in the world to its equivalent on the screen
+	 * 
+	 * @param d
+	 *            the in world distance
+	 * @return the on screen distance
+	 */
+	public double worldToScreenDistance(double d) {
+		return d * getZoomFactor();
+	}
+
+	/**
+	 * Convert a distance on the screen to its equivalent in the world
+	 * 
+	 * @param d
+	 *            the on screen distance
+	 * @return the in world distance
+	 */
+	public double screenToWorldDistance(double d) {
+		return d / getZoomFactor();
+	}
+
+	/**
+	 * Convert a X in the world to its equivalent on the screen
+	 * 
+	 * @param x
+	 *            the X in world
+	 *
+	 * @return the X on screen distance
+	 */
+	public double worldToScreenX(double x) {
+		return (x + getWorldOffsetX()) * getZoomFactor();
+	}
+
+	/**
+	 * A value that must be multiplied to scale objects
+	 * 
+	 * @return the zoom factor
+	 */
+	public double getZoomFactor() {
+		return zoom / 100;
+	}
+
+	/**
+	 * Convert a Y in the world to its equivalent on the screen
+	 * 
+	 * @param y
+	 *            the Y in world
+	 *
+	 * @return the Y on screen distance
+	 */
+	public double worldToScreenY(double y) {
+		return (-y + getWorldOffsetY()) * getZoomFactor();
+	}
+
+	/**
+	 * Convert a X on the screen to its equivalent in the world
+	 * 
+	 * @param x
+	 *            the X on screen
+	 *
+	 * @return the X in the world distance
+	 */
+	public double screenToWorldX(double x) {
+		return x / getZoomFactor() - getWorldOffsetX();
+	}
+
+	/**
+	 * Convert a Y on the screen to its equivalent in the world
+	 * 
+	 * @param y
+	 *            the Y on screen
+	 *
+	 * @return the Y in the world distance
+	 */
+	public double screenToWorldY(double y) {
+		return -y / getZoomFactor() + getWorldOffsetY();
+	}
+
+	/**
+	 * Add a drawable to the VUI.
+	 * 
+	 * @param d
+	 *            the new drawable
+	 */
+	public void add(Drawable d) {
+		
+		d.setVUIMulti(this);
+		RunLaterHelper.runLater(()-> canvas.getChildren().add(d.getNode()));
+		drawablesLock.lock();
+		drawables.add(d);
+		drawablesLock.unlock();
+		updateCanvas();
+	}
+	
+	/**
+	 * Remove a drawable from the VUI.
+	 * 
+	 * @param d
+	 *            the new drawable
+	 */
+	public void remove(Drawable d) {
+		drawablesLock.lock();
+		drawables.remove(d);
+		drawablesLock.unlock();
+		RunLaterHelper.runLater(()-> canvas.getChildren().remove(d.getNode()));
+		updateCanvas();
+	}
+	
+	/**
+	 * Remove all drawables from the VUI.
+	 */
+	public void clear() {
+		drawablesLock.lock();
+		drawables.clear();
+		RunLaterHelper.runLater(()->canvas.getChildren().clear());
+		drawablesLock.unlock();
+	}
+
+	/**
+	 * Refresh the canvas
+	 */
+	public void updateCanvas() {
+		final double w = canvas.getWidth();
+		final double h = canvas.getHeight();
+
+		setWorldOffsetX(worldCenterX + screenToWorldDistance(w / 2));
+		setWorldOffsetY(worldCenterY + screenToWorldDistance(h / 2));
+
+		drawablesLock.lock();
+		Collections.sort(drawables, (o1, o2) -> o1.getLayer() - o2.getLayer());
+		for (Drawable d : drawables)
+			RunLaterHelper.runLater(()-> d.onDraw());
+		drawablesLock.unlock();
+
+		RunLaterHelper.runLater(() -> {
+			statusLabel.setText(String.format("Zoom: %.2f Center: (%.2f,%.2f)", zoom, worldCenterX, worldCenterY));
+		});
+		
+		RunLaterHelper.runLater(()-> vuiExplorer.update(true));
+	}
+
+	/**
+	 * Get the width of the canvas
+	 * 
+	 * @return the canvas width
+	 */
+	public double getCanvasWidth() {
+		return canvas.getWidth();
+	}
+
+	/**
+	 * Get the height of the canvas
+	 * 
+	 * @return the canvas height
+	 */
+	public double getCanvasHeight() {
+		return canvas.getHeight();
+	}
+
+	/**
+	 * Get the value that must be added to the X coordinate of in world object
+	 * 
+	 * @return the X offset
+	 */
+	public double getWorldOffsetX() {
+		return worldOffsetX;
+	}
+
+	/**
+	 * Set the value that must be added to the X coordinate of in world object
+	 * 
+	 * @param offsetX
+	 *            the X offset
+	 */
+	public void setWorldOffsetX(double offsetX) {
+		this.worldOffsetX = offsetX;
+	}
+
+	/**
+	 * Get the value that must be added to the Y coordinate of in world object
+	 * 
+	 * @return the Y offset
+	 */
+	public double getWorldOffsetY() {
+		return worldOffsetY;
+	}
+
+	/**
+	 * Set the value that must be added to the Y coordinate of in world object
+	 * 
+	 * @param offsetY
+	 *            the Y offset
+	 */
+	public void setWorldOffsetY(double offsetY) {
+		this.worldOffsetY = offsetY;
+	}
+
+	/**
+	 * Create a point and start rendering it
+	 * 
+	 * @param dx
+	 *            the x coordinate
+	 * @param dy
+	 *            the y coordinate
+	 * @return the point object
+	 */
+	public DrawablePoint createAndAddPoint(double dx, double dy) {
+		DrawablePoint drawablePoint = new DrawablePoint(dx, dy);
+		add(drawablePoint);
+		return drawablePoint;
+	}
+
+	/**
+	 * Create a rectangle and start rendering it
+	 * 
+	 * @param x
+	 *            the x coordinate
+	 * @param y
+	 *            the y coordinate
+	 * @param w
+	 *            the width
+	 * @param h
+	 *            the height
+	 * @return the rectangle object
+	 */
+	public DrawableRectangle createAndAddRectangle(double x, double y, double w, double h) {
+		DrawableRectangle d = new DrawableRectangle(x, y, w, h);
+		add(d);
+		return d;
+	}
+
+	/**
+	 * Set the default configuration of the view
+	 * 
+	 * @param zoom
+	 *            the initial zoom value
+	 * @param worldCenterX
+	 *            the initial X center value
+	 * @param worldCenterY
+	 *            the initial Y center value
+	 */
+	public void setDefaultView(double zoom, double worldCenterX, double worldCenterY) {
+		this.zoom = zoom;
+		this.worldCenterX = worldCenterX;
+		this.worldCenterY = worldCenterY;
+		this.defaultZoom = zoom;
+		this.defaultWorldCenterX = worldCenterX;
+		this.defaultWorldCenterY = worldCenterY;
+	}
+
+	/**
+	 * Create an image and start rendering it
+	 * 
+	 * @param dx
+	 *            the x coordinate
+	 * @param dy
+	 *            the y coordinate
+	 * @param filename
+	 *            the filename of the image
+	 * @return the created image
+	 */
+	public DrawableImage createAndAddImage(double dx, double dy, String filename) {
+		DrawableImage image = new DrawableImage(dx, dy, filename);
+		add(image);
+		return image;
+	}
+
+	/**
+	 * Create a string and start rendering it
+	 * 
+	 * @param dx
+	 *            the x coordinate
+	 * @param dy
+	 *            the y coordinate
+	 * @param text
+	 *            the text to display
+	 * @return the created string
+	 */
+	public DrawableString createAndAddString(int dx, int dy, String text) {
+		DrawableString ds = new DrawableString(dx, dy, text);
+		add(ds);
+		return ds;
+	}
+
+	public Pane getCanvas() {
+		return canvas;
+	}
+	
+	public BorderPane getPanel() {
+		return panel;
+	}
+	
+	public List<Drawable> getDrawables() {
+		return drawables;
+	}
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/ui/VuiExplorer.java b/AMAKFX/src/fr/irit/smac/amak/ui/VuiExplorer.java
index 957e6a8ae9c4507c27f9832abb99d7023ab19883..dbb94088b1911433c82eccdd7a3c5c2a5170c067 100644
--- a/AMAKFX/src/fr/irit/smac/amak/ui/VuiExplorer.java
+++ b/AMAKFX/src/fr/irit/smac/amak/ui/VuiExplorer.java
@@ -23,13 +23,14 @@ import javafx.scene.layout.VBox;
 import javafx.scene.paint.Color;
 
 /**
- * A piece of GUI allowing to see and look for contexts.
+ * A piece of GUI allowing to see and look for agents.
  * @author Hugo
  *
  */
 public class VuiExplorer extends ScrollPane {
 
-	private VUI vui;
+	private VUI vui = null;
+	private VUIMulti vuiMulti = null;
 	
 	private VBox vbox;
 	private TitledPane contextsPane;
@@ -102,6 +103,74 @@ public class VuiExplorer extends ScrollPane {
 		RunLaterHelper.runLater(()->vui.getPanel().setLeft(this));
 
 	}
+	
+	
+	public VuiExplorer(VUIMulti vuiMlt) {
+		this.vuiMulti = vuiMlt;
+
+		this.setMaxWidth(Double.MAX_VALUE);
+		this.setMaxHeight(Double.MAX_VALUE);
+
+		vbox = new VBox();
+		vbox.setFillWidth(true);
+		this.setContent(vbox);
+
+		// refresh, close, and collapseAll button
+		HBox hboxButtons = new HBox();
+		Button refresh = new Button("Refresh");
+		refresh.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				update();
+			}
+		});
+		Button close = new Button("Close");
+		close.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				vuiMulti.getPanel().setLeft(null);
+			}
+		});
+		Button collapseAll = new Button("Collapse all");
+		collapseAll.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				collapseAll();
+			}
+		});
+		hboxButtons.getChildren().addAll(refresh, close, collapseAll);
+		
+		// check box
+		autoRefresh = new CheckBox("Auto refresh");
+		autoRefresh.setTooltip(new Tooltip("Try to automatically refresh the VUI explorer when the VUI is updated."));
+		
+		// search bar
+		search = new TextField();
+		search.setPromptText("regular expression");
+		// update list on change
+		search.textProperty().addListener(new ChangeListener<String>() {
+			@Override
+			public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+				search.setStyle(null);
+				try {
+					update();
+				} catch (PatternSyntaxException ex) {
+					search.setStyle("-fx-border-color: red;");
+				}
+			}
+		});
+
+		cpVBox = new VBox();
+		contextsPane = new TitledPane("Drawables", cpVBox);
+
+		vbox.getChildren().addAll(hboxButtons, autoRefresh, search, contextsPane);
+		update();
+		
+		// Add to vui
+		RunLaterHelper.runLater(()->vuiMulti.getPanel().setLeft(this));
+
+	}
+	
 
 	public void update(boolean auto) {
 		if(auto && autoRefresh.isSelected()) {
@@ -113,7 +182,13 @@ public class VuiExplorer extends ScrollPane {
 	 * Update the list of context
 	 */
 	public void update() {
-		List<Drawable> drawableList = vui.getDrawables();
+		List<Drawable> drawableList = null;
+		if(vui != null) {
+			drawableList = vui.getDrawables();
+		}
+		if(vuiMulti != null) {
+			drawableList = vuiMulti.getDrawables();
+		}
 		// crude color sort
 		drawableList.sort(new Comparator<Drawable>() {
 			@Override
@@ -142,7 +217,13 @@ public class VuiExplorer extends ScrollPane {
 	}
 	
 	private void collapseAll() {
-		List<Drawable> drawableList = vui.getDrawables();
+		List<Drawable> drawableList = null;
+		if(vui != null) {
+			drawableList = vui.getDrawables();
+		}
+		if(vuiMulti != null) {
+			drawableList = vuiMulti.getDrawables();
+		}
 		for(Drawable d : drawableList) {
 			if(d.showInExplorer && d.isVisible()) {
 				Drawable mini = d.getLinkedDrawable("mini");
diff --git a/AMAKFX/src/fr/irit/smac/amak/ui/drawables/Drawable.java b/AMAKFX/src/fr/irit/smac/amak/ui/drawables/Drawable.java
index ec5ce1fe94b59134da30c00edbd7368c6c8470f3..377377c5904d07114052daeb12d959a6664a725d 100644
--- a/AMAKFX/src/fr/irit/smac/amak/ui/drawables/Drawable.java
+++ b/AMAKFX/src/fr/irit/smac/amak/ui/drawables/Drawable.java
@@ -1,605 +1,618 @@
-package fr.irit.smac.amak.ui.drawables;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import fr.irit.smac.amak.tools.RunLaterHelper;
-import fr.irit.smac.amak.ui.VUI;
-import javafx.event.Event;
-import javafx.event.EventHandler;
-import javafx.scene.Node;
-import javafx.scene.input.MouseEvent;
-import javafx.scene.paint.Color;
-
-/**
- * A drawable is an object that can be drawn by the {@link VUI} system
- * 
- * @author of original version (the Swing one) Alexandre Perles
- *
- */
-public abstract class Drawable {
-	
-	/**
-	 * If this drawable should be shown in the vui explorer.
-	 */
-	public boolean showInExplorer = true;
-
-	/**
-	 * Default style applied to drawable node.
-	 */
-	protected static String defaultStyle = "-fx-stroke: black; -fx-stroke-width: 1;";
-	
-	/**
-	 * Linked drawables will receive the same event when using dispatchEvent.
-	 */
-	private HashMap<String, Drawable> linkedDrawable = new HashMap<String, Drawable>();
-	
-	/**
-	 * The horizontal position of the object
-	 */
-	protected double x;
-	/**
-	 * The vertical position of the object
-	 */
-	private double y;
-	/**
-	 * The width of the object
-	 */
-	private double width;
-	
-	/**
-	 * The real height
-	 */
-	protected double height;
-
-	/**
-	 * Does only the border must be displayed ?
-	 */
-	protected boolean strokeMode = false;
-
-	/**
-	 * The color of the object
-	 */
-	protected Color color = Color.BLACK;
-	
-	/**
-	 * The VUI on which the object is drawn
-	 */
-	protected VUI vui;
-	
-	/**
-	 * The order of drawing. An higher layer is drawn on top of the other.
-	 */
-	protected int layer = 0;
-	
-	/**
-	 * The angle of rotation of the object
-	 */
-	private double angle;
-	
-	/**
-	 * A fixed object doesn't move with the view. It can be used for HUD
-	 */
-	private boolean fixed = false;
-	
-	/**
-	 * Must the object be drawn ?
-	 */
-	private boolean visible = true;
-	
-	/**
-	 * Is the drawable expanded ?
-	 * @see Drawable#onMouseClick(MouseEvent)
-	 * @see Drawable#expand()
-	 * @see Drawable#collapse()
-	 */
-	private boolean expanded = false;
-	
-	/**
-	 * If relevant, the name of the drawable, usually it's the name
-	 * of the agent represented by this drawable.
-	 */
-	private String name;
-	
-	/**
-	 * If relevant, additional info on the drawable, usually it's the
-	 * state of the agent represented by this drawable.
-	 */
-	private String info;
-
-	/**
-	 * Constructor of the object
-	 * 
-	 * @param vui
-	 *            the VUI on which the object must be drawn
-	 * @param dx
-	 *            the x real position
-	 * @param dy
-	 *            the y real position
-	 * @param width
-	 *            the real width
-	 * @param height
-	 *            the real height
-	 */
-	protected Drawable(double dx, double dy, double width, double height) {
-		move(dx, dy);
-		setWidth(width);
-		setHeight(height);
-	}
-	
-	/**
-	 * If you wish to use some default settings for your drawable.<br/>
-	 * Must be called AFTER the node for your drawable has been created.
-	 */
-	protected void defaultInit() {
-		getNode().setStyle(defaultStyle);
-		
-		getNode().addEventHandler(MouseEvent.ANY, new EventHandler<MouseEvent>() {
-			@Override
-			public void handle(MouseEvent event) {
-				dispatchEvent(event);
-			}
-		});
-	}
-	
-	/**
-	 * Compute the width as it must be displayed on screen. Given the zoom factor,
-	 * the width displayed can be different than the real width.
-	 * 
-	 * @return the width
-	 */
-	public double getRenderedWidth() {
-		if (isFixed())
-			return width;
-		else
-			return vui.worldToScreenDistance(width);
-	}
-
-	/**
-	 * Set the real width of the object
-	 * 
-	 * @param width
-	 *            The new width
-	 */
-	public void setWidth(double width) {
-		this.width = width;
-	}
-
-	/**
-	 * Compute the height as it must be displayed on screen. Given the zoom factor,
-	 * the height displayed can be different than the real height.
-	 * 
-	 * @return the width
-	 */
-	public double getRenderedHeight() {
-		if (isFixed())
-			return height;
-		else
-			return vui.worldToScreenDistance(height);
-	}
-
-	/**
-	 * Set the real height of the object
-	 * 
-	 * @param height
-	 *            The new height
-	 */
-	public void setHeight(double height) {
-		this.height = height;
-	}
-
-	/**
-	 * Get the real width
-	 * 
-	 * @return the real width
-	 */
-	public double getWidth() {
-		return width;
-	}
-
-	/**
-	 * Get the real height
-	 * 
-	 * @return the real height
-	 */
-	public double getHeight() {
-		return height;
-	}
-
-	
-
-	/**
-	 * Getter for the fixed attribute
-	 * 
-	 * @return if the obejct is fixed
-	 */
-	public boolean isFixed() {
-		return fixed;
-	}
-
-	/**
-	 * Getter for the angle attribute
-	 * 
-	 * @return the angle
-	 */
-	public double getAngle() {
-		return angle;
-	}
-
-	/**
-	 * Getter for the layer attribute
-	 * 
-	 * @return the layer
-	 */
-	public int getLayer() {
-		return layer;
-	}
-
-	/**
-	 * Set the layer and update
-	 * 
-	 * @param layer
-	 *            the new layer
-	 * @return the object for chained methods
-	 */
-	public Drawable setLayer(int layer) {
-		this.layer = layer;
-		return this;
-	}
-
-	/**
-	 * Set the new angle
-	 * 
-	 * @param angle2
-	 *            the new angle
-	 * @return the object for chained methods
-	 */
-	public Drawable setAngle(double angle2) {
-		this.angle = angle2;
-		return this;
-	}
-
-	/**
-	 * Draw the object if visible and if on screen
-	 * 
-	 */
-	public void onDraw() {
-		if (isVisible()) {
-			_onDraw();
-		}
-	}
-
-	/**
-	 * Method that must be overrided to draw
-	 */
-	public abstract void _onDraw();
-
-	/**
-	 * Set the associated VUI
-	 * 
-	 * @param vectorialUI
-	 */
-	public void setVUI(VUI vectorialUI) {
-		vui = vectorialUI;
-	}
-
-	/**
-	 * Get the top y coordinate
-	 * 
-	 * @return the top y coordinate
-	 */
-	public double top() {
-		if (isFixed())
-			return y - height / 2;
-		else
-			return vui.worldToScreenY(y - height / 2);
-	}
-
-	/**
-	 * Get the left x coordinate
-	 * 
-	 * @return the left x coordinate
-	 */
-	public double left() {
-		if (isFixed())
-			return x - width / 2;
-		else
-			return vui.worldToScreenX(x - width / 2);
-	}
-
-	/**
-	 * Get the bottom y coordinate
-	 * 
-	 * @return the bottom y coordinate
-	 */
-	public double bottom() {
-		if (isFixed())
-			return y + height / 2;
-		else
-			return vui.worldToScreenY(y + height / 2);
-	}
-
-	/**
-	 * Get the right x coordinate
-	 * 
-	 * @return the right x coordinate
-	 */
-	public double right() {
-		if (isFixed())
-			return x + width / 2;
-		else
-			return vui.worldToScreenX(x + width / 2);
-	}
-
-	/**
-	 * Only draw the border of the object
-	 * 
-	 * @return the object for chained methods
-	 */
-	public Drawable setStrokeOnly() {
-		strokeMode = true;
-		return this;
-	}
-
-	/**
-	 * 
-	 * @param color
-	 * @return the object for chained methods
-	 */
-	public Drawable setColor(Color color) {
-		if (color == this.color)
-			return this;
-		this.color = color;
-		return this;
-	}
-	
-	/**
-	 * The color of the drawable.
-	 * @return
-	 */
-	public Color getColor() {
-		return color;
-	}
-
-	/**
-	 * 
-	 * @param dx
-	 * @param dy
-	 * @return the object for chained methods
-	 */
-	public Drawable move(double dx, double dy) {
-		if (x == dx && y == dy)
-			return this;
-		this.x = dx;
-		this.y = dy;
-		return this;
-	}
-
-	/**
-	 * 
-	 * @return the object for chained methods
-	 */
-	public Drawable setFixed() {
-		this.fixed = true;
-		return this;
-	}
-
-	/**
-	 * 
-	 * @return the object for chained methods
-	 */
-	public Drawable show() {
-		return this.setVisible(true);
-	}
-	
-	protected abstract void _hide();
-
-	/**
-	 * 
-	 * @return
-	 */
-	public Drawable hide() {
-		_hide();
-		return this.setVisible(false);
-	}
-
-	/**
-	 * 
-	 * @return
-	 */
-	public boolean isVisible() {
-		return visible;
-	}
-	
-	public abstract void _show();
-
-	/**
-	 * 
-	 * @param visible
-	 * @return the object for chained methods
-	 */
-	public Drawable setVisible(boolean visible) {
-		this.visible = visible;
-		if (visible)
-			_show();
-		else
-			_hide();
-		return this;
-	}
-	
-	/**
-	 * The graphical element that is displayed
-	 * @return
-	 */
-	public abstract Node getNode();
-	
-	/**
-	 * Remove the drawable from its VUI
-	 */
-	public void delete() {
-		vui.remove(this);
-	}
-
-	/**
-	 * Get the linked drawable or null if it does not exist.
-	 * @param name name of the linked drawable
-	 * @return the linked drawable or null
-	 */
-	public Drawable getLinkedDrawable(String name) {
-		Drawable ret = null;
-		if(linkedDrawable.containsKey(name)) {
-			ret = linkedDrawable.get(name);
-		}
-		return ret;
-	}
-	
-	/**
-	 * Add a drawable to the list of linked drawables.<br/>
-	 * The relation is not symmetrical.
-	 * @param name
-	 * @param drawable
-	 */
-	public void addLinkedDrawable(String name, Drawable drawable) {
-		linkedDrawable.put(name, drawable);
-	}
-	
-	/**
-	 * Return the list of linked drawables. <br/>
-	 * Linked drawables will receive the same event when using dispatchEvent.
-	 */
-	public List<Drawable> getLinkedDrawables(){
-		return new ArrayList<Drawable>(linkedDrawable.values());
-	}
-	
-	/**
-	 * Used by dispatchEvent. Override if you want to register more event with the dispatchEvent
-	 * @param event
-	 */
-	protected void onEvent(Event event) {
-		switch (event.getEventType().getName()) {
-		case "MOUSE_CLICKED":
-			onMouseClick((MouseEvent)event);
-			break;
-		case "MOUSE_ENTERED":
-			onMouseEntered((MouseEvent)event);
-			break;
-		case "MOUSE_EXITED":
-			onMouseExited((MouseEvent)event);
-			break;
-		default:
-			break;
-		}
-	}
-	
-	/**
-	 * Called when onEvent receive a MOUSE_EXITED event.
-	 * @param event
-	 */
-	protected void onMouseExited(MouseEvent event) {
-		getNode().setStyle(defaultStyle);
-	}
-
-	/**
-	 * Called when onEvent receive a MOUSE_ENTERED event.
-	 * @param event
-	 */
-	protected void onMouseEntered(MouseEvent event) {
-		getNode().setStyle("-fx-stroke: black; -fx-stroke-width: 3;");
-	}
-
-	/**
-	 * Called when onEvent receive a MOUSE_CLICKED event.
-	 * @param event
-	 */
-	protected void onMouseClick(MouseEvent event) {
-		if(expanded) {
-			collapse();
-		} else {
-			expand();
-		}
-	}
-
-	/**
-	 * Dispatch an event to all linked drawable, and this drawable.
-	 * @param event
-	 */
-	public void dispatchEvent(Event event) {
-		for(Drawable d : getLinkedDrawables()) {
-			d.onEvent(event);
-		}
-		onEvent(event);
-	}
-	
-	/**
-	 * If this drawable should be shown in the vui explorer.
-	 */
-	public Drawable setShowInExplorer(boolean showInExplorer) {
-		this.showInExplorer = showInExplorer;
-		return this;
-	}
-	
-	/**
-	 * If relevant, the name of the drawable, usually it's the name
-	 * of the agent represented by this drawable.
-	 */
-	public String getName() {
-		return name == null ? toString() : name;
-	}
-	
-	/**
-	 * If relevant, additional info on the drawable, usually it's the
-	 * state of the agent represented by this drawable.
-	 */
-	public String getInfo() {
-		return info == null ? toString() : info;
-	}
-	
-	/**
-	 * If relevant, the name of the drawable, usually it's the name
-	 * of the agent represented by this drawable.
-	 */
-	public Drawable setName(String name) {
-		this.name = name;
-		return this;
-	}
-	
-	/**
-	 * If relevant, additional info on the drawable, usually it's the
-	 * state of the agent represented by this drawable.
-	 */
-	public Drawable setInfo(String info) {
-		this.info = info;
-		return this;
-	}
-	
-	/**
-	 * Action performed if drawable is clicked while collapsed.<br/>
-	 * By default do nothing
-	 * @see Drawable#collapse()
-	 */
-	public void expand() {
-		expanded = true;
-	}
-	
-	/**
-	 * Action performed if drawable is clicked while expanded.
-	 * @see Drawable#expand()
-	 */
-	public void collapse() {
-		expanded = false;
-	}
-	
-	public boolean isExpanded() {
-		return expanded;
-	}
-	
-	/**
-	 * Set the drawable on top of all others
-	 * @return
-	 */
-	public Drawable toFront() {
-		RunLaterHelper.runLater(()-> getNode().toFront());
-		return this;
-	}
-}
+package fr.irit.smac.amak.ui.drawables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import fr.irit.smac.amak.tools.RunLaterHelper;
+import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.VUIMulti;
+import javafx.event.Event;
+import javafx.event.EventHandler;
+import javafx.scene.Node;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.paint.Color;
+
+/**
+ * A drawable is an object that can be drawn by the {@link VUI} system
+ * 
+ * @author of original version (the Swing one) Alexandre Perles
+ *
+ */
+public abstract class Drawable {
+	
+	/**
+	 * If this drawable should be shown in the vui explorer.
+	 */
+	public boolean showInExplorer = true;
+
+	/**
+	 * Default style applied to drawable node.
+	 */
+	protected static String defaultStyle = "-fx-stroke: black; -fx-stroke-width: 1;";
+	
+	/**
+	 * Linked drawables will receive the same event when using dispatchEvent.
+	 */
+	private HashMap<String, Drawable> linkedDrawable = new HashMap<String, Drawable>();
+	
+	/**
+	 * The horizontal position of the object
+	 */
+	protected double x;
+	/**
+	 * The vertical position of the object
+	 */
+	private double y;
+	/**
+	 * The width of the object
+	 */
+	private double width;
+	
+	/**
+	 * The real height
+	 */
+	protected double height;
+
+	/**
+	 * Does only the border must be displayed ?
+	 */
+	protected boolean strokeMode = false;
+
+	/**
+	 * The color of the object
+	 */
+	protected Color color = Color.BLACK;
+	
+	/**
+	 * The VUI on which the object is drawn
+	 */
+	protected VUI vui;
+	
+	protected VUIMulti vuiMulti;
+	
+	/**
+	 * The order of drawing. An higher layer is drawn on top of the other.
+	 */
+	protected int layer = 0;
+	
+	/**
+	 * The angle of rotation of the object
+	 */
+	private double angle;
+	
+	/**
+	 * A fixed object doesn't move with the view. It can be used for HUD
+	 */
+	private boolean fixed = false;
+	
+	/**
+	 * Must the object be drawn ?
+	 */
+	private boolean visible = true;
+	
+	/**
+	 * Is the drawable expanded ?
+	 * @see Drawable#onMouseClick(MouseEvent)
+	 * @see Drawable#expand()
+	 * @see Drawable#collapse()
+	 */
+	private boolean expanded = false;
+	
+	/**
+	 * If relevant, the name of the drawable, usually it's the name
+	 * of the agent represented by this drawable.
+	 */
+	private String name;
+	
+	/**
+	 * If relevant, additional info on the drawable, usually it's the
+	 * state of the agent represented by this drawable.
+	 */
+	private String info;
+
+	/**
+	 * Constructor of the object
+	 * 
+	 * @param vui
+	 *            the VUI on which the object must be drawn
+	 * @param dx
+	 *            the x real position
+	 * @param dy
+	 *            the y real position
+	 * @param width
+	 *            the real width
+	 * @param height
+	 *            the real height
+	 */
+	protected Drawable(double dx, double dy, double width, double height) {
+		move(dx, dy);
+		setWidth(width);
+		setHeight(height);
+	}
+	
+	/**
+	 * If you wish to use some default settings for your drawable.<br/>
+	 * Must be called AFTER the node for your drawable has been created.
+	 */
+	protected void defaultInit() {
+		getNode().setStyle(defaultStyle);
+		
+		getNode().addEventHandler(MouseEvent.ANY, new EventHandler<MouseEvent>() {
+			@Override
+			public void handle(MouseEvent event) {
+				dispatchEvent(event);
+			}
+		});
+	}
+	
+	/**
+	 * Compute the width as it must be displayed on screen. Given the zoom factor,
+	 * the width displayed can be different than the real width.
+	 * 
+	 * @return the width
+	 */
+	public double getRenderedWidth() {
+		if (isFixed())
+			return width;
+		else
+			return vui != null ? vui.worldToScreenDistance(width) : vuiMulti.worldToScreenDistance(width);
+		
+	}
+
+	/**
+	 * Set the real width of the object
+	 * 
+	 * @param width
+	 *            The new width
+	 */
+	public void setWidth(double width) {
+		this.width = width;
+	}
+
+	/**
+	 * Compute the height as it must be displayed on screen. Given the zoom factor,
+	 * the height displayed can be different than the real height.
+	 * 
+	 * @return the width
+	 */
+	public double getRenderedHeight() {
+		if (isFixed())
+			return height;
+		else
+			return vui != null ? vui.worldToScreenDistance(height) : vuiMulti.worldToScreenDistance(height);
+	}
+
+	/**
+	 * Set the real height of the object
+	 * 
+	 * @param height
+	 *            The new height
+	 */
+	public void setHeight(double height) {
+		this.height = height;
+	}
+
+	/**
+	 * Get the real width
+	 * 
+	 * @return the real width
+	 */
+	public double getWidth() {
+		return width;
+	}
+
+	/**
+	 * Get the real height
+	 * 
+	 * @return the real height
+	 */
+	public double getHeight() {
+		return height;
+	}
+
+	
+
+	/**
+	 * Getter for the fixed attribute
+	 * 
+	 * @return if the obejct is fixed
+	 */
+	public boolean isFixed() {
+		return fixed;
+	}
+
+	/**
+	 * Getter for the angle attribute
+	 * 
+	 * @return the angle
+	 */
+	public double getAngle() {
+		return angle;
+	}
+
+	/**
+	 * Getter for the layer attribute
+	 * 
+	 * @return the layer
+	 */
+	public int getLayer() {
+		return layer;
+	}
+
+	/**
+	 * Set the layer and update
+	 * 
+	 * @param layer
+	 *            the new layer
+	 * @return the object for chained methods
+	 */
+	public Drawable setLayer(int layer) {
+		this.layer = layer;
+		return this;
+	}
+
+	/**
+	 * Set the new angle
+	 * 
+	 * @param angle2
+	 *            the new angle
+	 * @return the object for chained methods
+	 */
+	public Drawable setAngle(double angle2) {
+		this.angle = angle2;
+		return this;
+	}
+
+	/**
+	 * Draw the object if visible and if on screen
+	 * 
+	 */
+	public void onDraw() {
+		if (isVisible()) {
+			_onDraw();
+		}
+	}
+
+	/**
+	 * Method that must be overrided to draw
+	 */
+	public abstract void _onDraw();
+
+	/**
+	 * Set the associated VUI
+	 * 
+	 * @param vectorialUI
+	 */
+	public void setVUI(VUI vectorialUI) {
+		vui = vectorialUI;
+	}
+	
+	public void setVUIMulti(VUIMulti vectorialUI) {
+		vuiMulti = vectorialUI;
+	}
+
+	/**
+	 * Get the top y coordinate
+	 * 
+	 * @return the top y coordinate
+	 */
+	public double top() {
+		if (isFixed())
+			return y - height / 2;
+		else
+			return vui != null ? vui.worldToScreenY(y - height / 2) : vuiMulti.worldToScreenY(y - height / 2);
+	}
+
+	/**
+	 * Get the left x coordinate
+	 * 
+	 * @return the left x coordinate
+	 */
+	public double left() {
+		if (isFixed())
+			return x - width / 2;
+		else
+			return vui != null ? vui.worldToScreenX(x - width / 2) : vuiMulti.worldToScreenX(x - width / 2);
+		
+	}
+
+	/**
+	 * Get the bottom y coordinate
+	 * 
+	 * @return the bottom y coordinate
+	 */
+	public double bottom() {
+		if (isFixed())
+			return y + height / 2;
+		else
+			return vui != null ? vui.worldToScreenY(y + height / 2) : vuiMulti.worldToScreenY(y + height / 2);
+	}
+
+	/**
+	 * Get the right x coordinate
+	 * 
+	 * @return the right x coordinate
+	 */
+	public double right() {
+		if (isFixed())
+			return x + width / 2;
+		else
+			return vui != null ? vui.worldToScreenX(x + width / 2) : vuiMulti.worldToScreenX(x + width / 2);
+	}
+
+	/**
+	 * Only draw the border of the object
+	 * 
+	 * @return the object for chained methods
+	 */
+	public Drawable setStrokeOnly() {
+		strokeMode = true;
+		return this;
+	}
+
+	/**
+	 * 
+	 * @param color
+	 * @return the object for chained methods
+	 */
+	public Drawable setColor(Color color) {
+		if (color == this.color)
+			return this;
+		this.color = color;
+		return this;
+	}
+	
+	/**
+	 * The color of the drawable.
+	 * @return
+	 */
+	public Color getColor() {
+		return color;
+	}
+
+	/**
+	 * 
+	 * @param dx
+	 * @param dy
+	 * @return the object for chained methods
+	 */
+	public Drawable move(double dx, double dy) {
+		if (x == dx && y == dy)
+			return this;
+		this.x = dx;
+		this.y = dy;
+		return this;
+	}
+
+	/**
+	 * 
+	 * @return the object for chained methods
+	 */
+	public Drawable setFixed() {
+		this.fixed = true;
+		return this;
+	}
+
+	/**
+	 * 
+	 * @return the object for chained methods
+	 */
+	public Drawable show() {
+		return this.setVisible(true);
+	}
+	
+	protected abstract void _hide();
+
+	/**
+	 * 
+	 * @return
+	 */
+	public Drawable hide() {
+		_hide();
+		return this.setVisible(false);
+	}
+
+	/**
+	 * 
+	 * @return
+	 */
+	public boolean isVisible() {
+		return visible;
+	}
+	
+	public abstract void _show();
+
+	/**
+	 * 
+	 * @param visible
+	 * @return the object for chained methods
+	 */
+	public Drawable setVisible(boolean visible) {
+		this.visible = visible;
+		if (visible)
+			_show();
+		else
+			_hide();
+		return this;
+	}
+	
+	/**
+	 * The graphical element that is displayed
+	 * @return
+	 */
+	public abstract Node getNode();
+	
+	/**
+	 * Remove the drawable from its VUI
+	 */
+	public void delete() {
+		if(vui != null)
+			vui.remove(this);
+		
+		if(vuiMulti != null)
+			vuiMulti.remove(this);
+	}
+
+	/**
+	 * Get the linked drawable or null if it does not exist.
+	 * @param name name of the linked drawable
+	 * @return the linked drawable or null
+	 */
+	public Drawable getLinkedDrawable(String name) {
+		Drawable ret = null;
+		if(linkedDrawable.containsKey(name)) {
+			ret = linkedDrawable.get(name);
+		}
+		return ret;
+	}
+	
+	/**
+	 * Add a drawable to the list of linked drawables.<br/>
+	 * The relation is not symmetrical.
+	 * @param name
+	 * @param drawable
+	 */
+	public void addLinkedDrawable(String name, Drawable drawable) {
+		linkedDrawable.put(name, drawable);
+	}
+	
+	/**
+	 * Return the list of linked drawables. <br/>
+	 * Linked drawables will receive the same event when using dispatchEvent.
+	 */
+	public List<Drawable> getLinkedDrawables(){
+		return new ArrayList<Drawable>(linkedDrawable.values());
+	}
+	
+	/**
+	 * Used by dispatchEvent. Override if you want to register more event with the dispatchEvent
+	 * @param event
+	 */
+	protected void onEvent(Event event) {
+		switch (event.getEventType().getName()) {
+		case "MOUSE_CLICKED":
+			onMouseClick((MouseEvent)event);
+			break;
+		case "MOUSE_ENTERED":
+			onMouseEntered((MouseEvent)event);
+			break;
+		case "MOUSE_EXITED":
+			onMouseExited((MouseEvent)event);
+			break;
+		default:
+			break;
+		}
+	}
+	
+	/**
+	 * Called when onEvent receive a MOUSE_EXITED event.
+	 * @param event
+	 */
+	protected void onMouseExited(MouseEvent event) {
+		getNode().setStyle(defaultStyle);
+	}
+
+	/**
+	 * Called when onEvent receive a MOUSE_ENTERED event.
+	 * @param event
+	 */
+	protected void onMouseEntered(MouseEvent event) {
+		getNode().setStyle("-fx-stroke: black; -fx-stroke-width: 3;");
+	}
+
+	/**
+	 * Called when onEvent receive a MOUSE_CLICKED event.
+	 * @param event
+	 */
+	protected void onMouseClick(MouseEvent event) {
+		if(expanded) {
+			collapse();
+		} else {
+			expand();
+		}
+	}
+
+	/**
+	 * Dispatch an event to all linked drawable, and this drawable.
+	 * @param event
+	 */
+	public void dispatchEvent(Event event) {
+		for(Drawable d : getLinkedDrawables()) {
+			d.onEvent(event);
+		}
+		onEvent(event);
+	}
+	
+	/**
+	 * If this drawable should be shown in the vui explorer.
+	 */
+	public Drawable setShowInExplorer(boolean showInExplorer) {
+		this.showInExplorer = showInExplorer;
+		return this;
+	}
+	
+	/**
+	 * If relevant, the name of the drawable, usually it's the name
+	 * of the agent represented by this drawable.
+	 */
+	public String getName() {
+		return name == null ? toString() : name;
+	}
+	
+	/**
+	 * If relevant, additional info on the drawable, usually it's the
+	 * state of the agent represented by this drawable.
+	 */
+	public String getInfo() {
+		return info == null ? toString() : info;
+	}
+	
+	/**
+	 * If relevant, the name of the drawable, usually it's the name
+	 * of the agent represented by this drawable.
+	 */
+	public Drawable setName(String name) {
+		this.name = name;
+		return this;
+	}
+	
+	/**
+	 * If relevant, additional info on the drawable, usually it's the
+	 * state of the agent represented by this drawable.
+	 */
+	public Drawable setInfo(String info) {
+		this.info = info;
+		return this;
+	}
+	
+	/**
+	 * Action performed if drawable is clicked while collapsed.<br/>
+	 * By default do nothing
+	 * @see Drawable#collapse()
+	 */
+	public void expand() {
+		expanded = true;
+	}
+	
+	/**
+	 * Action performed if drawable is clicked while expanded.
+	 * @see Drawable#expand()
+	 */
+	public void collapse() {
+		expanded = false;
+	}
+	
+	public boolean isExpanded() {
+		return expanded;
+	}
+	
+	/**
+	 * Set the drawable on top of all others
+	 * @return
+	 */
+	public Drawable toFront() {
+		RunLaterHelper.runLater(()-> getNode().toFront());
+		return this;
+	}
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/ui/drawables/DrawablePoint.java b/AMAKFX/src/fr/irit/smac/amak/ui/drawables/DrawablePoint.java
index f17758a89e8b780e74d300884c1e325ea36f70ae..13145dff91cdde22924968cb7d21fe59d8953aae 100644
--- a/AMAKFX/src/fr/irit/smac/amak/ui/drawables/DrawablePoint.java
+++ b/AMAKFX/src/fr/irit/smac/amak/ui/drawables/DrawablePoint.java
@@ -1,65 +1,65 @@
-package fr.irit.smac.amak.ui.drawables;
-
-import javafx.event.EventHandler;
-import javafx.scene.Node;
-import javafx.scene.input.MouseEvent;
-import javafx.scene.shape.SVGPath;
-
-/**
- * Drawable to point things on the VUI, use a '+' icon as graphical representation.
- * @author Hugo
- *
- */
-public class DrawablePoint extends Drawable {
-
-	private SVGPath svg = new SVGPath();
-	
-	public DrawablePoint(double dx, double dy) {
-		super(dx, dy, 0.5, 0.5);
-		svg.setContent("M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z");
-		getNode().addEventHandler(MouseEvent.ANY, new EventHandler<MouseEvent>() {
-			@Override
-			public void handle(MouseEvent event) {
-				dispatchEvent(event);
-			}
-		});
-	}
-
-	@Override
-	public void _onDraw() {
-		svg.setFill(color);
-		svg.setScaleX(getRenderedWidth());
-		svg.setScaleY(getRenderedHeight());
-		// the render has an offset, 10 look like a good value 
-		svg.setTranslateX(left()-10);
-		svg.setTranslateY(top()-10);
-	}
-
-	@Override
-	protected void _hide() {
-		svg.setVisible(false);
-	}
-
-	@Override
-	public void _show() {
-		svg.setVisible(true);
-	}
-
-	@Override
-	public Node getNode() {
-		return svg;
-	}
-	
-	@Override
-	protected void onMouseExited(MouseEvent event) {
-		svg.setScaleX(getRenderedWidth());
-		svg.setScaleY(getRenderedHeight());
-	}
-
-	@Override
-	protected void onMouseEntered(MouseEvent event) {
-		svg.setScaleX(getRenderedWidth()*1.5);
-		svg.setScaleY(getRenderedHeight()*1.5);
-	}
-
-}
+package fr.irit.smac.amak.ui.drawables;
+
+import javafx.event.EventHandler;
+import javafx.scene.Node;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.shape.SVGPath;
+
+/**
+ * Drawable to point things on the VUI, use a '+' icon as graphical representation.
+ * @author Hugo
+ *
+ */
+public class DrawablePoint extends Drawable {
+
+	private SVGPath svg = new SVGPath();
+	
+	public DrawablePoint(double dx, double dy) {
+		super(dx, dy, 0.1, 0.1);
+		svg.setContent("M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z");
+		getNode().addEventHandler(MouseEvent.ANY, new EventHandler<MouseEvent>() {
+			@Override
+			public void handle(MouseEvent event) {
+				dispatchEvent(event);
+			}
+		});
+	}
+
+	@Override
+	public void _onDraw() {
+		svg.setFill(color);
+		svg.setScaleX(getRenderedWidth());
+		svg.setScaleY(getRenderedHeight());
+		// the render has an offset, 10 look like a good value 
+		svg.setTranslateX(left()-10);
+		svg.setTranslateY(top()-10);
+	}
+
+	@Override
+	protected void _hide() {
+		svg.setVisible(false);
+	}
+
+	@Override
+	public void _show() {
+		svg.setVisible(true);
+	}
+
+	@Override
+	public Node getNode() {
+		return svg;
+	}
+	
+	@Override
+	protected void onMouseExited(MouseEvent event) {
+		svg.setScaleX(getRenderedWidth());
+		svg.setScaleY(getRenderedHeight());
+	}
+
+	@Override
+	protected void onMouseEntered(MouseEvent event) {
+		svg.setScaleX(getRenderedWidth()*1.5);
+		svg.setScaleY(getRenderedHeight()*1.5);
+	}
+
+}
diff --git a/AMOEBAonAMAK/README.md b/AMOEBAonAMAK/README.md
deleted file mode 100644
index daac3b82884bc2879d8684289a57e32b486695ba..0000000000000000000000000000000000000000
--- a/AMOEBAonAMAK/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# AMOEBA on AMAK
-An AMOEBA3 port on AMAK.
-
-## Quick start
-```Java
-// Create a world and a studied system for your amoeba
-World world = new World();
-StudiedSystem studiedSystem = new F_XY_System(50.0);
-AMOEBA amoeba = new AMOEBA(world, studiedSystem);
-// Create a backup system for the amoeba
-IBackupSystem backupSystem = new BackupSystem(amoeba);
-// Load a configuration matching the studied system
-File file = new File("resources\\twoDimensionsLauncher.xml");
-backupSystem.loadXML(file);
-// The amoeba is ready to be used
-```
-You can find a more complete exemple at `experiment.Main.java`.
\ No newline at end of file
diff --git a/AMOEBAonAMAK/src/agents/AmoebaAgent.java b/AMOEBAonAMAK/src/agents/AmoebaAgent.java
index 45a2c6c1ba8e1985d2d4a11c0c2075cfeff62fb5..8ae90bc81b63ebea93836dfe2a4d0c0d07bab4fc 100644
--- a/AMOEBAonAMAK/src/agents/AmoebaAgent.java
+++ b/AMOEBAonAMAK/src/agents/AmoebaAgent.java
@@ -1,109 +1,110 @@
-package agents;
-
-import agents.percept.Percept;
-import fr.irit.smac.amak.Agent;
-import fr.irit.smac.amak.tools.Loggable;
-import gui.RenderStrategy;
-import kernel.AMOEBA;
-import kernel.World;
-
-/**
- * The base class for all AMOEBA agents
- */
-public abstract class AmoebaAgent extends Agent<AMOEBA, World> implements Loggable {
-	// Attributes
-	protected String name;
-	private boolean dying;
-	
-	protected RenderStrategy renderStrategy;
-
-	/**
-	 * Instantiate a new agent attached to an amoeba
-	 * @param the amoeba
-	 */
-	public AmoebaAgent(AMOEBA amas, Object... params) {
-		super(amas, params);
-		this.dying = false;
-	}
-	
-	@Override
-	protected void onReady() {
-		super.onReady();
-		logger().debug("CYCLE "+getAmas().getCycle(), "Agent %s ready.", toString());
-	}
-
-	@Override
-	protected void onDecide() {
-	}
-
-	@Override
-	protected void onRenderingInitialization() {
-		if(renderStrategy != null) {
-			renderStrategy.initialize();
-		}
-	}
-	
-	@Override
-	public void onUpdateRender() {
-		amas.getEnvironment().incrementNbActivatedAgent();
-		if(renderStrategy != null && !isDying()) {
-			if (amas.isRenderUpdate()) {
-				renderStrategy.render();
-			}
-		}
-	}
-
-	/**
-	 * Set the name of the agent. Useful for visualization, and essential for {@link Percept}.
-	 * @param name
-	 */
-	public void setName(String name) {
-		this.name = name;
-	}
-	
-	@Override
-	public void destroy() {
-		dying = true;
-		if(renderStrategy != null) {
-			renderStrategy.delete();
-		}
-		super.destroy();
-		logger().debug("CYCLE "+getAmas().getCycle(), "Agent %s destroyed.", toString());
-	}
-
-	/**
-	 * Get the name of the agent. Useful for visualization, and essential for {@link Percept}.
-	 * @param name
-	 */
-	public String getName() {
-		return name;
-	}
-
-	/**
-	 * Tell if the agent is dying. A dying agent no longer perform any useful action, but is not yet removed from its system.
-	 * @return
-	 */
-	public boolean isDying() {
-		return dying;
-	}
-	
-	/**
-	 * Set the render strategy of an agent.<br/>
-	 * {@link RenderStrategy#delete()} the old one, and {@link RenderStrategy#initialize()} the new one.
-	 * @param renderStrategy
-	 * @see RenderStrategy
-	 */
-	public void setRenderStrategy(RenderStrategy renderStrategy) {
-		if(this.renderStrategy != null) this.renderStrategy.delete();
-		this.renderStrategy = renderStrategy;
-		if(this.renderStrategy != null) this.renderStrategy.initialize();
-	}
-	
-	/**
-	 * Get the render strategy of an agent.
-	 * @return
-	 */
-	public RenderStrategy getRenderStrategy() {
-		return renderStrategy;
-	}
-}
+package agents;
+
+import agents.percept.Percept;
+import fr.irit.smac.amak.Agent;
+import fr.irit.smac.amak.tools.Loggable;
+import gui.RenderStrategy;
+import kernel.AMOEBA;
+import kernel.World;
+
+/**
+ * The base class for all AMOEBA agents
+ */
+public abstract class AmoebaAgent extends Agent<AMOEBA, World> implements Loggable {
+	// Attributes
+	protected String name;
+	private boolean dying;
+	
+	protected RenderStrategy renderStrategy;
+
+	/**
+	 * Instantiate a new agent attached to an amoeba
+	 * @param the amoeba
+	 */
+	public AmoebaAgent(AMOEBA amas, Object... params) {
+		super(amas, params);
+		this.dying = false;
+	}
+	
+	@Override
+	protected void onReady() {
+		super.onReady();
+		logger().debug("CYCLE "+getAmas().getCycle(), "Agent %s ready.", toString());
+	}
+
+	@Override
+	protected void onDecide() {
+	}
+
+	@Override
+	protected void onRenderingInitialization() {
+		if(renderStrategy != null) {
+			renderStrategy.initialize(getAmas().getVUIMulti());
+			
+		}
+	}
+	
+	@Override
+	public void onUpdateRender() {
+		amas.getEnvironment().incrementNbActivatedAgent();
+		if(renderStrategy != null && !isDying()) {
+			if (amas.isRenderUpdate()) {
+				renderStrategy.render();
+			}
+		}
+	}
+
+	/**
+	 * Set the name of the agent. Useful for visualization, and essential for {@link Percept}.
+	 * @param name
+	 */
+	public void setName(String name) {
+		this.name = name;
+	}
+	
+	@Override
+	public void destroy() {
+		dying = true;
+		if(renderStrategy != null) {
+			renderStrategy.delete();
+		}
+		super.destroy();
+		logger().debug("CYCLE "+getAmas().getCycle(), "Agent %s destroyed.", toString());
+	}
+
+	/**
+	 * Get the name of the agent. Useful for visualization, and essential for {@link Percept}.
+	 * @param name
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * Tell if the agent is dying. A dying agent no longer perform any useful action, but is not yet removed from its system.
+	 * @return
+	 */
+	public boolean isDying() {
+		return dying;
+	}
+	
+	/**
+	 * Set the render strategy of an agent.<br/>
+	 * {@link RenderStrategy#delete()} the old one, and {@link RenderStrategy#initialize()} the new one.
+	 * @param renderStrategy
+	 * @see RenderStrategy
+	 */
+	public void setRenderStrategy(RenderStrategy renderStrategy) {
+		if(this.renderStrategy != null) this.renderStrategy.delete();
+		this.renderStrategy = renderStrategy;
+		if(this.renderStrategy != null) this.renderStrategy.initialize(getAmas().getVUIMulti());
+	}
+	
+	/**
+	 * Get the render strategy of an agent.
+	 * @return
+	 */
+	public RenderStrategy getRenderStrategy() {
+		return renderStrategy;
+	}
+}
diff --git a/AMOEBAonAMAK/src/agents/context/Context.java b/AMOEBAonAMAK/src/agents/context/Context.java
index 981e0fb4a6fb88c61dc44f585aa0f1ff98508208..d97077c06cddfafaca7b5f598f9a1c810bdead93 100644
--- a/AMOEBAonAMAK/src/agents/context/Context.java
+++ b/AMOEBAonAMAK/src/agents/context/Context.java
@@ -588,7 +588,24 @@ public class Context extends AmoebaAgent {
 			
 			HashMap<Percept, Double> request = boundsToRequest(bounds);
 			if(request != null) {
-				return new EndogenousRequest(request, bounds, 7, new ArrayList<Context>(Arrays.asList(this,ctxt)), REQUEST.OVERLAP);
+				
+				double currentDistanceToOraclePrediction = this.getLocalModel().distance(this.getCurrentExperiment());
+				double otherContextDistanceToOraclePrediction = ctxt.getLocalModel().distance(ctxt.getCurrentExperiment());
+				
+				Double averageDistanceToOraclePrediction = getAmas().getHeadAgent().getAverageRegressionPerformanceIndicator();
+				Double distanceDifference = Math.abs(currentDistanceToOraclePrediction-otherContextDistanceToOraclePrediction);
+					
+				getEnvironment().trace(TRACE_LEVEL.DEBUG, new ArrayList<String>( Arrays.asList(this.getName(),"currentDistanceToOraclePrediction",""+ currentDistanceToOraclePrediction,"otherContextDistanceToOraclePrediction",""+ otherContextDistanceToOraclePrediction, "distanceDifference", ""+distanceDifference)));
+				
+				if(distanceDifference<averageDistanceToOraclePrediction) {
+					return new EndogenousRequest(request, bounds, 6, new ArrayList<Context>(Arrays.asList(this,ctxt)), REQUEST.CONCURRENCE);
+				}
+				else {
+					return new EndogenousRequest(request, bounds, 7, new ArrayList<Context>(Arrays.asList(this,ctxt)), REQUEST.CONFLICT);
+				}
+				
+				
+				
 			}		
 		}
 		else if(overlapCounts == getAmas().getPercepts().size()-1 && voidDistances.size() == 1 && getAmas().getCycle() > 750) {
@@ -695,10 +712,13 @@ public class Context extends AmoebaAgent {
 				
 				//double minDistanceToOraclePrediction = Math.min(getAmas().getHeadAgent().getDistanceToRegressionAllowed(), getAmas().getHeadAgent().getDistanceToRegressionAllowed());
 				Double averageDistanceToOraclePrediction = getAmas().getHeadAgent().getAverageRegressionPerformanceIndicator();
-						
-				if((currentDistanceToOraclePrediction<averageDistanceToOraclePrediction) && (otherContextDistanceToOraclePrediction<averageDistanceToOraclePrediction)) {
+				Double distanceDifference = Math.abs(currentDistanceToOraclePrediction-otherContextDistanceToOraclePrediction);
+					
+				getEnvironment().trace(TRACE_LEVEL.DEBUG, new ArrayList<String>( Arrays.asList(this.getName(),"currentDistanceToOraclePrediction",""+ currentDistanceToOraclePrediction,"otherContextDistanceToOraclePrediction",""+ otherContextDistanceToOraclePrediction, "distanceDifference", ""+distanceDifference)));
+				
+				if(distanceDifference<averageDistanceToOraclePrediction) {
 					
-					getEnvironment().trace(TRACE_LEVEL.DEBUG, new ArrayList<String>( Arrays.asList(this.getName(),"currentDistanceToOraclePrediction",""+ currentDistanceToOraclePrediction,"otherContextDistanceToOraclePrediction",""+ otherContextDistanceToOraclePrediction))); 
+					 
 					
 					
 					for(Percept pct : ranges.keySet()) {
diff --git a/AMOEBAonAMAK/src/agents/head/Head.java b/AMOEBAonAMAK/src/agents/head/Head.java
index ce3ab15712a6ebac55b3bedddb3823ef348903b1..eb6087a322cdecd06c7d1b12e2ec5ddf13724010 100644
--- a/AMOEBAonAMAK/src/agents/head/Head.java
+++ b/AMOEBAonAMAK/src/agents/head/Head.java
@@ -90,6 +90,7 @@ public class Head extends AmoebaAgent {
 		setContextFromPropositionWasSelected(false);
 		getAmas().data.oldOracleValue = getAmas().data.oracleValue;
 		getAmas().data.oracleValue = getAmas().getPerceptions("oracle");
+		setAverageRegressionPerformanceIndicator();
 
 		/* The head memorize last used context agent */
 		lastUsedContext = bestContext;
@@ -232,13 +233,18 @@ public class Head extends AmoebaAgent {
 		getAmas().data.executionTimes[6]=System.currentTimeMillis()- getAmas().data.executionTimes[6];
 
 
-		getAmas().data.executionTimes[7]=System.currentTimeMillis();
 		
+		getAmas().data.executionTimes[11]=System.currentTimeMillis();
 		NCSDetection_ChildContext();
+		getAmas().data.executionTimes[11]=System.currentTimeMillis()- getAmas().data.executionTimes[11];
+		
+		getAmas().data.executionTimes[12]=System.currentTimeMillis();
+		NCSDetection_PotentialRequest();
+		getAmas().data.executionTimes[12]=System.currentTimeMillis()- getAmas().data.executionTimes[12];
 		
-		//NCSDetection_PotentialRequest();
 		
-		//predictionPropagation();
+		
+		getAmas().data.executionTimes[7]=System.currentTimeMillis();
 		
 		criticalities.addCriticality("spatialCriticality",
 				(getMinMaxVolume() - getVolumeOfAllContexts()) / getMinMaxVolume());
@@ -316,12 +322,13 @@ public class Head extends AmoebaAgent {
 
 		
 		getAmas().data.executionTimes[7]=System.currentTimeMillis()- getAmas().data.executionTimes[7];
-
 		
 		for(int i = 0 ; i<20;i++) {
 			getAmas().data.executionTimesSums[i] += getAmas().data.executionTimes[i];
 		}			
 
+		
+		
 	}
 
 	public double getMinMaxVolume() {
@@ -1093,32 +1100,21 @@ public class Head extends AmoebaAgent {
 		double minDistanceToOraclePrediction = Double.POSITIVE_INFINITY;
 
 		for (Context activatedContext : activatedContexts) {
+			currentDistanceToOraclePrediction = activatedContext.getLocalModel().distance(activatedContext.getCurrentExperiment());
 			
-			
-			
-			
-			
-			currentDistanceToOraclePrediction = activatedContext.getLocalModel()
-					.distance(activatedContext.getCurrentExperiment());
-			getAmas().data.distanceToRegression = currentDistanceToOraclePrediction;
 
 			getAmas().data.contextNotFinished = false;
 			getEnvironment().trace(TRACE_LEVEL.DEBUG, new ArrayList<String>(Arrays.asList("MODEL DISTANCE", activatedContext.getName(),
 					"" + activatedContext.getLocalModel().distance(activatedContext.getCurrentExperiment()))));
 			if (!activatedContext.getLocalModel().finishedFirstExperiments()) {
+				
 				activatedContext.getLocalModel().updateModel(activatedContext.getCurrentExperiment(), getAmas().data.learningSpeed);
 				getAmas().data.contextNotFinished = true;
 				
-				if(getAmas().data.oracleValue>0) {
-					
-					System.out.println(activatedContext.getName());
-					
-					
-				}
 			}
-
 			else if (currentDistanceToOraclePrediction < getAverageRegressionPerformanceIndicator()) {
 			//else if (currentDistanceToOraclePrediction < regressionPerformance.getPerformanceIndicator()) {
+				
 				activatedContext.getLocalModel().updateModel(activatedContext.getCurrentExperiment(), getAmas().data.learningSpeed);
 
 				if(getAmas().data.oracleValue>0) {
@@ -1131,6 +1127,7 @@ public class Head extends AmoebaAgent {
 
 			if (currentDistanceToOraclePrediction < minDistanceToOraclePrediction) {
 				minDistanceToOraclePrediction = currentDistanceToOraclePrediction;
+				getAmas().data.distanceToRegression = minDistanceToOraclePrediction;
 			}
 
 			if (!getAmas().data.contextNotFinished) {
@@ -1152,6 +1149,7 @@ public class Head extends AmoebaAgent {
 			activatedContexts.get(i).criticalities.updateMeans();
 			
 			if (activatedContexts.get(i).criticalities.getCriticalityMean("distanceToRegression") != null) {
+				
 				activatedContexts.get(i).regressionPerformance.update(activatedContexts.get(i).criticalities.getCriticalityMean("distanceToRegression"));
 				getEnvironment().trace(TRACE_LEVEL.INFORM, new ArrayList<String>(Arrays.asList("UPDATE REGRESSION PERFORMANCE", activatedContexts.get(i).getName(), ""+activatedContexts.get(i).regressionPerformance.getPerformanceIndicator())));
 			}
@@ -2061,7 +2059,7 @@ public class Head extends AmoebaAgent {
 				
 				EndogenousRequest currentRequest = itr.next();
 				
-				if(currentRequest.getType() == REQUEST.OVERLAP) {
+				if(currentRequest.getType() == REQUEST.CONFLICT || currentRequest.getType() == REQUEST.CONCURRENCE) {
 						
 					existingRequestTest = existingRequestTest || currentRequest.testIfContextsAlreadyAsked(request.getAskingContexts()); 
 				}
@@ -2113,6 +2111,12 @@ public class Head extends AmoebaAgent {
 	
 	public Double getAverageRegressionPerformanceIndicator() {
 		
+		return getAmas().data.averageRegressionPerformanceIndicator;
+		
+	}
+	
+	public void setAverageRegressionPerformanceIndicator() {
+		
 		int numberOfRegressions = 0;
 		if(activatedNeighborsContexts.size()>0) {
 			double meanRegressionPerformanceIndicator = 0.0;
@@ -2121,13 +2125,14 @@ public class Head extends AmoebaAgent {
 					numberOfRegressions+=1;
 			}
 			assert numberOfRegressions != 0;
-			return meanRegressionPerformanceIndicator/numberOfRegressions;
+			getAmas().data.averageRegressionPerformanceIndicator =  (meanRegressionPerformanceIndicator/numberOfRegressions > getAmas().data.initRegressionPerformance) ? meanRegressionPerformanceIndicator/numberOfRegressions :  getAmas().data.initRegressionPerformance;
 		}
 		else{
-			return getAmas().data.initRegressionPerformance;
+			getAmas().data.averageRegressionPerformanceIndicator = getAmas().data.initRegressionPerformance;
 		}
 		
 	}
+	
 
 	public void proposition(Context c) {
 		activatedContexts.add(c);
diff --git a/AMOEBAonAMAK/src/agents/head/REQUEST.java b/AMOEBAonAMAK/src/agents/head/REQUEST.java
index 2cb7d78652523f7ce9f3d3fe3b9ed10f03cf4efe..64ec5f2d25528b862f3fb52151d431d68066335c 100644
--- a/AMOEBAonAMAK/src/agents/head/REQUEST.java
+++ b/AMOEBAonAMAK/src/agents/head/REQUEST.java
@@ -5,6 +5,8 @@ import java.io.Serializable;
 public enum REQUEST implements Serializable {
 	
 	OVERLAP,
+	CONFLICT,
+	CONCURRENCE,
 	VOID,
 	SELF
 	
diff --git a/AMOEBAonAMAK/src/experiments/AdvancedMain.java b/AMOEBAonAMAK/src/experiments/AdvancedMain.java
index d0267bb169fd4cb920fb0d8ae0dd01ab69ffec4c..39be228a80022f9765d872c9d0e3e7d2362a76c7 100644
--- a/AMOEBAonAMAK/src/experiments/AdvancedMain.java
+++ b/AMOEBAonAMAK/src/experiments/AdvancedMain.java
@@ -1,122 +1,152 @@
-package experiments;
-
-import java.io.File;
-import java.io.IOException;
-
-import fr.irit.smac.amak.Configuration;
-import fr.irit.smac.amak.tools.Log;
-import gui.AmoebaWindow;
-import javafx.beans.value.ChangeListener;
-import javafx.beans.value.ObservableValue;
-import javafx.scene.control.Slider;
-import kernel.AMOEBA;
-import kernel.StudiedSystem;
-import kernel.backup.BackupSystem;
-import kernel.backup.IBackupSystem;
-import kernel.backup.SaveHelperImpl;
-
-/**
- * A more advanced and complete main.
- * @author Hugo
- *
- */
-public class AdvancedMain {
-
-	public static void main(String[] args) throws IOException {
-		// Instantiating the MainWindow before usage.
-		// It also allows you to change some of its behavior before creating an AMOEBA.
-		// If you use Configuration.commandLineMode = True , then you should skip it. 
-		AmoebaWindow.instance();
-		example();
-	}
-
-	private static void example() throws IOException {
-
-		// Set AMAK configuration before creating an AMOEBA
-		Configuration.commandLineMode = false;
-		Configuration.allowedSimultaneousAgentsExecution = 1;
-		Configuration.waitForGUI = true;
-
-		// Create an AMOEBA
-		AMOEBA amoeba = new AMOEBA();
-		// Create a studied system and add it to the amoeba.
-		// Adding a studied system to an amoeba allow you to control the learning speed (the simulation : how many cycles per second)
-		// with amoeba's scheduler, graphically or programmatically.
-		StudiedSystem studiedSystem = new F_XY_System(50.0);
-		amoeba.setStudiedSystem(studiedSystem);
-		// A window appeared, allowing to control the simulation, but if you try to run it
-		// it will crash (there's no percepts !). We need to load a configuration :
-		
-		// Change how new Context are rendered.
-		//Context.defaultRenderStrategy = NoneRenderer.class;
-		
-		// Create a backup system for the AMOEBA
-		IBackupSystem backupSystem = new BackupSystem(amoeba);
-		// Load a configuration matching the studied system
-		File file = new File("resources/twoDimensionsLauncher.xml");
-		backupSystem.load(file);
-		// Note : if you intend to use a SaveHelper, you can use SaveHelper.load instead of a BackupSystem
-		
-		// We add an optional saver, allowing us to autosave the amoeba at each cycle.
-		// The SaveHelper also add graphical tools to save and load AMOEBA's state.
-		amoeba.saver = new SaveHelperImpl(amoeba);
-		// Autosave slow execution, if you want fast training, set saver to null,
-		// or saver.autoSave = false.
-
-		// The amoeba is ready to be used.
-		// Next we show how to control it with code :
-
-		// We deny the possibility to change simulation speed with the UI
-		amoeba.allowGraphicalScheduler(false);
-		// We allow rendering
-		amoeba.setRenderUpdate(true);
-		long start = System.currentTimeMillis();
-		// We run some learning cycles
-		int nbCycle = 1000;
-		for (int i = 0; i < nbCycle; ++i) {
-			studiedSystem.playOneStep();
-			amoeba.learn(studiedSystem.getOutput());
-		}
-		long end = System.currentTimeMillis();
-		System.out.println("Done in : " + (end - start) / 1000.0);
-		
-		// We create a manual save point
-		amoeba.saver.newManualSave("TestManualSave");
-		
-		// We set the log level to INFORM, to avoid debug logs that slow down simulation
-		Log.defaultMinLevel = Log.Level.INFORM;
-		
-		// We deactivate rendering
-		amoeba.setRenderUpdate(false);
-		// Do some more learning
-		start = System.currentTimeMillis();
-		for (int i = 0; i < nbCycle; ++i) {
-			studiedSystem.playOneStep();
-			amoeba.learn(studiedSystem.getOutput());
-		}
-		end = System.currentTimeMillis();
-		System.out.println("Done in : " + (end - start) / 1000.0);
-		
-		
-		// Activate rendering back
-		amoeba.setRenderUpdate(true);
-		// After activating rendering we need to update agent's visualization
-		amoeba.updateAgentsVisualisation();
-		// We allow simulation control with the UI
-		amoeba.allowGraphicalScheduler(true);
-		
-		// Exemple for adding a tool in the toolbar
-		Slider slider = new Slider(0, 10, 0);
-		slider.setShowTickLabels(true);
-		slider.setShowTickMarks(true);
-		slider.valueProperty().addListener(new ChangeListener<Number>() {
-			@Override
-			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
-				System.out.println("new Value "+newValue);
-			}
-		});
-		AmoebaWindow.addToolbar(slider);
-
-		System.out.println("End main");
-	}
-}
+package experiments;
+
+import java.io.File;
+import java.io.IOException;
+
+import fr.irit.smac.amak.Configuration;
+import fr.irit.smac.amak.tools.Log;
+import fr.irit.smac.amak.ui.VUIMulti;
+import gui.AmoebaMultiUIWindow;
+import gui.AmoebaWindow;
+import javafx.application.Application;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.control.Slider;
+import javafx.stage.Stage;
+import kernel.AMOEBA;
+import kernel.StudiedSystem;
+import kernel.backup.BackupSystem;
+import kernel.backup.IBackupSystem;
+import kernel.backup.SaveHelperImpl;
+
+/**
+ * A more advanced and complete main.
+ * @author Hugo
+ *
+ */
+public class AdvancedMain extends Application{
+	
+	
+	public static void main(String[] args) throws IOException {
+		
+		// Application.launch(args) launches JavaFX process 
+		// It also allows you to change some of its behavior before creating an AMOEBA.
+		// If you use Configuration.commandLineMode = True , then you should skip it. 
+		Application.launch(args);
+
+
+	}
+	
+	@Override
+	public void start(Stage primaryStage) throws Exception, IOException {
+
+		example();
+		
+	}
+
+	
+
+	private static void example() throws IOException {
+
+		// Set AMAK configuration before creating an AMOEBA
+		Configuration.commandLineMode = false;
+		Configuration.allowedSimultaneousAgentsExecution = 1;
+		Configuration.waitForGUI = true;
+		Configuration.multiUI = true;
+
+		
+		VUIMulti amoebaVUI = new VUIMulti("2D");
+		AmoebaMultiUIWindow amoebaUI = new AmoebaMultiUIWindow("ELLSA", amoebaVUI);
+		
+		// Create an AMOEBA
+		AMOEBA amoeba = new AMOEBA(amoebaUI, amoebaVUI);
+		// Create a studied system and add it to the amoeba.
+		// Adding a studied system to an amoeba allow you to control the learning speed (the simulation : how many cycles per second)
+		// with amoeba's scheduler, graphically or programmatically.
+		StudiedSystem studiedSystem = new F_XY_System(50.0);
+		amoeba.setStudiedSystem(studiedSystem);
+		// A window appeared, allowing to control the simulation, but if you try to run it
+		// it will crash (there's no percepts !). We need to load a configuration :
+		
+		// Change how new Context are rendered.
+		//Context.defaultRenderStrategy = NoneRenderer.class;
+		
+		// Create a backup system for the AMOEBA
+		IBackupSystem backupSystem = new BackupSystem(amoeba);
+		// Load a configuration matching the studied system
+		File file = new File("resources/twoDimensionsLauncher.xml");
+		backupSystem.load(file);
+		// Note : if you intend to use a SaveHelper, you can use SaveHelper.load instead of a BackupSystem
+		
+		// We add an optional saver, allowing us to autosave the amoeba at each cycle.
+		// The SaveHelper also add graphical tools to save and load AMOEBA's state.
+		amoeba.saver = new SaveHelperImpl(amoeba);
+		// Autosave slow execution, if you want fast training, set saver to null,
+		// or saver.autoSave = false.
+
+		// The amoeba is ready to be used.
+		// Next we show how to control it with code :
+
+		// We deny the possibility to change simulation speed with the UI
+		amoeba.allowGraphicalScheduler(false);
+		// We allow rendering
+		amoeba.setRenderUpdate(true);
+		long start = System.currentTimeMillis();
+		// We run some learning cycles
+		int nbCycle = 100;
+		for (int i = 0; i < nbCycle; ++i) {
+			System.out.println(i);
+			studiedSystem.playOneStep();
+			amoeba.learn(studiedSystem.getOutput());
+		}
+		long end = System.currentTimeMillis();
+		System.out.println("Done in : " + (end - start) / 1000.0);
+		
+		// We create a manual save point
+		amoeba.saver.newManualSave("TestManualSave");
+		
+		// We set the log level to INFORM, to avoid debug logs that slow down simulation
+		Log.defaultMinLevel = Log.Level.INFORM;
+		
+		// We deactivate rendering
+		amoeba.setRenderUpdate(false);
+		// Do some more learning
+		start = System.currentTimeMillis();
+		for (int i = 0; i < nbCycle; ++i) {
+			studiedSystem.playOneStep();
+			amoeba.learn(studiedSystem.getOutput());
+		}
+		end = System.currentTimeMillis();
+		System.out.println("Done in : " + (end - start) / 1000.0);
+		
+		
+		// Activate rendering back
+		amoeba.setRenderUpdate(true);
+		// After activating rendering we need to update agent's visualization
+		amoeba.updateAgentsVisualisation();
+		// We allow simulation control with the UI
+		amoeba.allowGraphicalScheduler(true);
+		
+		// Exemple for adding a tool in the toolbar
+		Slider slider = new Slider(0, 10, 0);
+		slider.setShowTickLabels(true);
+		slider.setShowTickMarks(true);
+		slider.valueProperty().addListener(new ChangeListener<Number>() {
+			@Override
+			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+				System.out.println("new Value "+newValue);
+			}
+		});
+		amoebaUI.addToolbar(slider);
+
+		System.out.println("End main");
+	}
+	
+	@Override
+	public void stop() throws Exception {
+		super.stop();
+		System.exit(0);
+	}
+
+	
+}
diff --git a/AMOEBAonAMAK/src/experiments/F_XY_System.java b/AMOEBAonAMAK/src/experiments/F_XY_System.java
index 8aba6d446a4bb86d6aea058b234028ff40b8adac..b31bceff9afb18fdc815757fce0de6b26ac86f5f 100644
--- a/AMOEBAonAMAK/src/experiments/F_XY_System.java
+++ b/AMOEBAonAMAK/src/experiments/F_XY_System.java
@@ -48,7 +48,7 @@ public class F_XY_System implements StudiedSystem {
 
 		out.put("px0", x);
 		out.put("px1", y);
-		out.put("oracle", 0.0);
+		out.put("oracle", result);
 		return out;
 	}
 
diff --git a/AMOEBAonAMAK/src/experiments/Main.java b/AMOEBAonAMAK/src/experiments/Main.java
index 4cd5643ea4f16c72567fe716d54b72cd9223787b..721e209ebdd996c13a773954808eb66aa9d84c38 100644
--- a/AMOEBAonAMAK/src/experiments/Main.java
+++ b/AMOEBAonAMAK/src/experiments/Main.java
@@ -39,7 +39,7 @@ public class Main {
 		}
 		
 		System.out.println("Creating the amoeba");
-		AMOEBA amoeba = new AMOEBA(configFile, ss);
+		AMOEBA amoeba = new AMOEBA(null,null,configFile, ss);
 		
 		synchronized (Thread.currentThread()){
 			try {
diff --git a/AMOEBAonAMAK/src/experiments/MinimalMain.java b/AMOEBAonAMAK/src/experiments/MinimalMain.java
deleted file mode 100644
index b9b0bde35821831dfe658b762a50be32ceb0540a..0000000000000000000000000000000000000000
--- a/AMOEBAonAMAK/src/experiments/MinimalMain.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package experiments;
-
-import kernel.AMOEBA;
-import kernel.StudiedSystem;
-
-/**
- * The most minimal main possible producing a functioning amoeba.
- * @author Hugo
- *
- */
-public class MinimalMain {
-
-	public static void main(String[] args) throws InterruptedException {
-		// create a system to be studied
-		StudiedSystem studiedSystem = new F_XY_System(50.0);
-		// create the amoeba
-		// Make sure the path to the config file is correct.
-		AMOEBA amoeba = new AMOEBA("resources/twoDimensionsLauncher.xml", studiedSystem);
-		// a window should have appeared, allowing you to control and visualize the amoeba.
-	}
-
-}
diff --git a/AMOEBAonAMAK/src/experiments/MinimalMainCommandLineMode.java b/AMOEBAonAMAK/src/experiments/MinimalMainCommandLineMode.java
new file mode 100644
index 0000000000000000000000000000000000000000..d57dfb6fab68c4228f1c088e2d2c2cc19b06b0bf
--- /dev/null
+++ b/AMOEBAonAMAK/src/experiments/MinimalMainCommandLineMode.java
@@ -0,0 +1,40 @@
+package experiments;
+
+import fr.irit.smac.amak.Configuration;
+import kernel.AMOEBA;
+import kernel.StudiedSystem;
+
+/**
+ * The most minimal main possible producing a functioning amoeba.
+ * @author Hugo
+ *
+ */
+public class MinimalMainCommandLineMode {
+
+	public static void main(String[] args) throws InterruptedException {
+		
+		Configuration.commandLineMode = true;
+		
+		// create a system to be studied
+		StudiedSystem studiedSystem = new F_XY_System(50.0);
+		// create the amoeba
+		// Make sure the path to the config file is correct.
+		AMOEBA amoeba = new AMOEBA(null,null,"resources/twoDimensionsLauncher.xml", studiedSystem);
+		// a window should have appeared, allowing you to control and visualize the amoeba.
+		
+		// Learning and Request example
+		long start = System.currentTimeMillis();
+		for (int i = 0; i < 1001; ++i) {
+			studiedSystem.playOneStep();
+			amoeba.learn(studiedSystem.getOutput());
+		}
+		long end = System.currentTimeMillis();
+		System.out.println("Done in : " + (end - start)  + " ms");
+		
+		for (int i = 0; i < 10; ++i) {
+			studiedSystem.playOneStep();
+			System.out.println(amoeba.request(studiedSystem.getOutput()));
+		}
+	}
+
+}
diff --git a/AMOEBAonAMAK/src/experiments/MinimalMainUI.java b/AMOEBAonAMAK/src/experiments/MinimalMainUI.java
new file mode 100644
index 0000000000000000000000000000000000000000..074596944882d8c54c4c104060d81602d3365698
--- /dev/null
+++ b/AMOEBAonAMAK/src/experiments/MinimalMainUI.java
@@ -0,0 +1,40 @@
+package experiments;
+
+import fr.irit.smac.amak.Configuration;
+import kernel.AMOEBA;
+import kernel.StudiedSystem;
+
+/**
+ * The most minimal main possible producing a functioning amoeba.
+ * @author Hugo
+ *
+ */
+public class MinimalMainUI {//TODO
+
+	public static void main(String[] args) throws InterruptedException {
+		
+		Configuration.commandLineMode = false;
+		
+		// create a system to be studied
+		StudiedSystem studiedSystem = new F_XY_System(50.0);
+		// create the amoeba
+		// Make sure the path to the config file is correct.
+		AMOEBA amoeba = new AMOEBA(null,null,"resources/twoDimensionsLauncher.xml", studiedSystem);
+		// a window should have appeared, allowing you to control and visualize the amoeba.
+		
+		// Learning and Request example
+		long start = System.currentTimeMillis();
+		for (int i = 0; i < 1001; ++i) {
+			studiedSystem.playOneStep();
+			amoeba.learn(studiedSystem.getOutput());
+		}
+		long end = System.currentTimeMillis();
+		System.out.println("Done in : " + (end - start)  + " ms");
+		
+		for (int i = 0; i < 10; ++i) {
+			studiedSystem.playOneStep();
+			System.out.println(amoeba.request(studiedSystem.getOutput()));
+		}
+	}
+
+}
diff --git a/AMOEBAonAMAK/src/experiments/SimpleReinforcement.java b/AMOEBAonAMAK/src/experiments/SimpleReinforcement.java
new file mode 100644
index 0000000000000000000000000000000000000000..db4a991c79b90f5c0afad2ed25f4037653406224
--- /dev/null
+++ b/AMOEBAonAMAK/src/experiments/SimpleReinforcement.java
@@ -0,0 +1,169 @@
+package experiments;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Random;
+
+import fr.irit.smac.amak.Configuration;
+import fr.irit.smac.amak.ui.drawables.Drawable;
+import fr.irit.smac.amak.ui.drawables.DrawableOval;
+import gui.AmoebaWindow;
+import javafx.scene.paint.Color;
+import kernel.AMOEBA;
+import kernel.backup.SaveHelperDummy;
+import utils.Pair;
+import utils.RandomUtils;
+import utils.XmlConfigGenerator;
+
+/**
+ * Train an amoeba on a simple reinforcement task.
+ * The goal of the task is to get to the center. When the position of the agent cross 0, it gets a reward of 100.
+ * The agent can only moves in 2 directions, of a distance of 1. Moving give a reward of -1.
+ * If the agent moves outside of the allowed range, it gets a reward of -100. 
+ * @author Hugo
+ *
+ */
+public class SimpleReinforcement {
+	
+	private Random rand = new Random();
+	private double x = 0;
+	private double reward = 0;
+	private Drawable pos;
+
+	public static void main(String[] args) {
+		ArrayList<Pair<String, Boolean>> sensors = new ArrayList<>();
+		sensors.add(new Pair<String, Boolean>("p1", false));
+		sensors.add(new Pair<String, Boolean>("a1", true));
+		File config;
+		try {
+			config = File.createTempFile("config", "xml");
+			XmlConfigGenerator.makeXML(config, sensors);
+		} catch (IOException e) {
+			e.printStackTrace();
+			System.exit(1);
+			return; // now compilator know config is initialized
+		}
+		
+		Configuration.commandLineMode = true;
+		AMOEBA amoeba = new AMOEBA(null,null,config.getAbsolutePath(), null);
+		amoeba.saver = new SaveHelperDummy();
+		SimpleReinforcement env = new SimpleReinforcement();
+		
+		Random r = new Random();
+		HashMap<String, Double> state = env.reset();
+		HashMap<String, Double> state2;
+		double explo = 0.5;
+		for(int i = 0; i < 100; i++) {
+			boolean done = false;
+			Deque<HashMap<String, Double>> actions = new ArrayDeque<>();
+			//System.out.println("Explore "+i);
+			int nbStep = 0;
+			state = env.reset();
+			while(!done) {
+				nbStep++;
+				if(nbStep > 500) {
+					done = true;
+				}
+				state.remove("oracle");
+				state.remove("a1");
+				HashMap<String, Double> action = amoeba.maximize(state);
+				if(r.nextDouble() < 0.5 || action.get("oracle").equals(Double.NEGATIVE_INFINITY) ) {
+					//System.out.println("Random action");
+					action.put("a1", (r.nextBoolean() ? 10.0 : -10.0));
+				}
+				state2 = env.step(action.get("a1"));
+				
+				if(state2.get("oracle") != -1.0) {
+					done = true;
+				}
+				
+				action.put("p1", state.get("p1"));
+				action.put("oracle", state2.get("oracle"));
+				//System.out.println(action);
+				actions.add(action);
+				
+				state = state2;
+			}
+			
+			//System.out.println("Learn "+i);
+			HashMap<String, Double> action = actions.pop();
+			double reward = action.get("oracle");
+			amoeba.learn(action);
+			
+			while(!actions.isEmpty()) {
+				action = actions.pop();
+				reward += action.get("oracle");
+				action.put("oracle", reward);
+				amoeba.learn(action);
+			}
+			
+			if(explo > 0.1) {
+				explo -= 0.01;
+				if(explo < 0.1)
+					explo = 0.1;
+			}
+			
+			System.out.println("Episode "+i+"  reward : "+reward+"  explo : "+explo);
+		}
+	}
+	
+	/**
+	 * Must be called AFTER an AMOEBA with GUI
+	 */
+	public SimpleReinforcement() {
+		//Configuration.commandLineMode = false;
+		//AmoebaWindow instance = AmoebaWindow.instance();
+		//pos = new DrawableOval(0.5, 0.5, 1, 1);
+		//pos.setColor(new Color(0.5, 0.0, 0.0, 0.5));
+		//instance.mainVUI.add(pos);
+		//instance.mainVUI.createAndAddRectangle(-50, -0.25, 100, 0.5);
+		//instance.mainVUI.createAndAddRectangle(-0.25, -1, 0.5, 2);
+		
+		
+		
+	}
+	
+	public HashMap<String, Double> step(double action){
+		if(action == 0.0) action = rand.nextDouble();
+		if(action > 0.0) action = Math.ceil(action);
+		if(action < 0.0 ) action = Math.floor(action);
+		if(action > 1.0) action = 1.0;
+		if(action < -1.0) action = -1.0;
+		double oldX = x;
+		x = x + action;
+		if(x < -50.0 || x > 50.0) {
+			x = RandomUtils.nextDouble(rand, -50.0, Math.nextUp(50.0));
+			reward = -100.0;
+		} else if(x == 0.0 || sign(oldX) != sign(x)) {
+			// win !
+			reward = 1000.0;
+			x = RandomUtils.nextDouble(rand, -50.0, Math.nextUp(50.0));
+		} else {
+			reward = -1.0;
+		}
+		HashMap<String, Double> ret = new HashMap<>();
+		ret.put("p1", x);
+		ret.put("oracle", reward);
+		//pos.move(x+0.5, 0.5);
+		return ret;
+	}
+	
+	public HashMap<String, Double> reset(){
+		x = RandomUtils.nextDouble(rand, -50.0, Math.nextUp(50.0));
+		reward = 0.0;
+		
+		HashMap<String, Double> ret = new HashMap<>();
+		ret.put("p1", x);
+		ret.put("oracle", reward);
+		return ret;
+	}
+	
+	private int sign(double x) {
+		return x < 0 ? -1 : 1;
+	}
+
+}
diff --git a/AMOEBAonAMAK/src/experiments/TestingMain.java b/AMOEBAonAMAK/src/experiments/TestingMain.java
index 55bfd61be6a785e91185c2cf2eff2c396e810a15..781cfa3e58b079ee3a6820837d44e3ef4451a893 100644
--- a/AMOEBAonAMAK/src/experiments/TestingMain.java
+++ b/AMOEBAonAMAK/src/experiments/TestingMain.java
@@ -22,7 +22,7 @@ public class TestingMain {
 		StudiedSystem studiedSystem = new NDimCube(50.0, 3);
 		// create the amoeba
 		// Make sure the path to the config file is correct.
-		AMOEBA amoeba = new AMOEBA("resources/threeDimensionsLauncher.xml", studiedSystem);
+		AMOEBA amoeba = new AMOEBA(null,null,"resources/threeDimensionsLauncher.xml", studiedSystem);
 		amoeba.saver = new SaveHelperDummy();
 		// a window should have appeared, allowing you to control and visualize the amoeba.
 		
diff --git a/AMOEBAonAMAK/src/experiments/UnityLauncher/Main.java b/AMOEBAonAMAK/src/experiments/UnityLauncher/Main.java
index 099acfb8fc051ddd4d8bdc9e921b199dc79844f7..80756bc9c9159796e795102ae6fd2530802cef93 100644
--- a/AMOEBAonAMAK/src/experiments/UnityLauncher/Main.java
+++ b/AMOEBAonAMAK/src/experiments/UnityLauncher/Main.java
@@ -76,7 +76,7 @@ public class Main implements Runnable {
 		Configuration.waitForGUI = true;
 		Configuration.plotMilliSecondsUpdate = 20000;
 		
-		AMOEBA amoeba = new AMOEBA();
+		AMOEBA amoeba = new AMOEBA(null,null);
 		StudiedSystem studiedSystem = new F_N_Manager(spaceSize, dimension, nbOfModels, normType, randomExploration, explorationIncrement,explorationWidht,limitedToSpaceZone, oracleNoiseRange);
 		amoeba.setStudiedSystem(studiedSystem);
 		IBackupSystem backupSystem = new BackupSystem(amoeba);
diff --git a/AMOEBAonAMAK/src/experiments/benchmark/Benchmark.java b/AMOEBAonAMAK/src/experiments/benchmark/Benchmark.java
index aae072a3e57df5830cb40fbde7e2ee357f0adfd0..7eb0eeb7e16366c07be8ebff5cd9af53d2d14a2b 100644
--- a/AMOEBAonAMAK/src/experiments/benchmark/Benchmark.java
+++ b/AMOEBAonAMAK/src/experiments/benchmark/Benchmark.java
@@ -43,7 +43,7 @@ public class Benchmark {
 	private static void execLearn(int nbCycle, String configFile) {
 		System.out.println("Start "+nbCycle+" learning cycles.");
 		
-		AMOEBA amoeba = new AMOEBA();
+		AMOEBA amoeba = new AMOEBA(null,null);
 		BackupSystem bs = new BackupSystem(amoeba);
 		bs.load(new File(configFile));
 		StudiedSystem ss = new NDimCube(50, amoeba.getPercepts().size());
@@ -67,7 +67,7 @@ public class Benchmark {
 	private static void execRequest(int nbCycle, String configFile) {
 		System.out.println("Start "+nbCycle+" request cycles.");
 		
-		AMOEBA amoeba = new AMOEBA();
+		AMOEBA amoeba = new AMOEBA(null,null);
 		BackupSystem bs = new BackupSystem(amoeba);
 		bs.load(new File(configFile));
 		StudiedSystem ss = new NDimCube(50, amoeba.getPercepts().size());
diff --git a/AMOEBAonAMAK/src/experiments/benchmark/BenchmarkThreading.java b/AMOEBAonAMAK/src/experiments/benchmark/BenchmarkThreading.java
index 37233bbe46470ba06d5d39dbf52cdb5fd8a40b0c..23bce11e9f8b3c719377ad461f8364d73ee74cf7 100644
--- a/AMOEBAonAMAK/src/experiments/benchmark/BenchmarkThreading.java
+++ b/AMOEBAonAMAK/src/experiments/benchmark/BenchmarkThreading.java
@@ -116,7 +116,7 @@ public class BenchmarkThreading {
 		// setup cache --- (very important to reduce impact of the 1st measure)
 		Configuration.allowedSimultaneousAgentsExecution = 1;
 		StudiedSystem learnSystem = new NDimCube(50.0, 100);
-		AMOEBA amoeba = new AMOEBA();
+		AMOEBA amoeba = new AMOEBA(null,null);
 		amoeba.setStudiedSystem(learnSystem);
 		IBackupSystem backupSystem = new BackupSystem(amoeba);
 		backupSystem.load(file);
@@ -127,7 +127,7 @@ public class BenchmarkThreading {
 		for(int thd = 1; thd <= 8; thd *= 2) {
 			Configuration.allowedSimultaneousAgentsExecution = thd;
 			learnSystem = new NDimCube(50.0, 100);
-			amoeba = new AMOEBA();
+			amoeba = new AMOEBA(null,null);
 			backupSystem = new BackupSystem(amoeba);
 			backupSystem.load(file);
 			List<List<Double>> bench = benchmark(amoeba, learnSystem, learnSystem, 0, 10000, 1000, null);
diff --git a/AMOEBAonAMAK/src/experiments/nDimensionsLaunchers/F_N_Launcher.java b/AMOEBAonAMAK/src/experiments/nDimensionsLaunchers/F_N_Launcher.java
index 300f91f974a5129d9488e6b97388d84df20173ea..f984ebe3760c85448c5f8e3cdfbd7817b1892753 100644
--- a/AMOEBAonAMAK/src/experiments/nDimensionsLaunchers/F_N_Launcher.java
+++ b/AMOEBAonAMAK/src/experiments/nDimensionsLaunchers/F_N_Launcher.java
@@ -7,10 +7,14 @@ import java.util.ArrayList;
 
 import experiments.FILE;
 import fr.irit.smac.amak.Configuration;
+import fr.irit.smac.amak.ui.VUIMulti;
+import gui.AmoebaMultiUIWindow;
 import gui.AmoebaWindow;
+import javafx.application.Application;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
 import javafx.scene.control.Slider;
+import javafx.stage.Stage;
 import kernel.AMOEBA;
 import kernel.StudiedSystem;
 import kernel.backup.BackupSystem;
@@ -21,7 +25,7 @@ import kernel.backup.SaveHelperImpl;
 /**
  * The Class BadContextLauncherEasy.
  */
-public class F_N_Launcher implements Serializable {
+public class F_N_Launcher  extends Application implements Serializable {
 
 
 	public static final double oracleNoiseRange = 0.5;
@@ -31,7 +35,7 @@ public class F_N_Launcher implements Serializable {
 	public static final double spaceSize = 50.0	;
 	public static final int nbOfModels = 3	;
 	public static final int normType = 2	;
-	public static final boolean randomExploration = false;
+	public static final boolean randomExploration = true;
 	public static final boolean limitedToSpaceZone = true;
 	//public static final double mappingErrorAllowed = 0.07; // BIG SQUARE
 	public static double mappingErrorAllowed = 0.03; // MULTI
@@ -43,27 +47,28 @@ public class F_N_Launcher implements Serializable {
 
 	
 	public static void main(String[] args) throws IOException {
-		// Instantiating the MainWindow before usage.
-		// It also allows you to change some of its behavior before creating an AMOEBA.
-		// If you use Configuration.commandLineMode = True , then you should skip it. 
-		AmoebaWindow.instance();
-		launch();
+		
+		
+		Application.launch(args);
+
+
 	}
 	
 
+	@Override
+	public void start(Stage arg0) throws Exception {
+
 
-	public static void launch() throws IOException{
-		
-		
-		
-		
 		// Set AMAK configuration before creating an AMOEBA
+		Configuration.multiUI=true;
 		Configuration.commandLineMode = false;
 		Configuration.allowedSimultaneousAgentsExecution = 1;
 		Configuration.waitForGUI = true;
 		Configuration.plotMilliSecondsUpdate = 20000;
 		
-		AMOEBA amoeba = new AMOEBA();
+		VUIMulti amoebaVUI = new VUIMulti("2D");
+		AmoebaMultiUIWindow amoebaUI = new AmoebaMultiUIWindow("ELLSA", amoebaVUI);
+		AMOEBA amoeba = new AMOEBA(amoebaUI,  amoebaVUI);
 		StudiedSystem studiedSystem = new F_N_Manager(spaceSize, dimension, nbOfModels, normType, randomExploration, explorationIncrement,explorationWidht,limitedToSpaceZone, oracleNoiseRange);
 		amoeba.setStudiedSystem(studiedSystem);
 		IBackupSystem backupSystem = new BackupSystem(amoeba);
@@ -90,65 +95,66 @@ public class F_N_Launcher implements Serializable {
 				amoeba.getEnvironment().setMappingErrorAllowed(mappingErrorAllowed);
 			}
 		});
-		AmoebaWindow.addToolbar(slider);
+		amoebaUI.addToolbar(slider);
 		
 		studiedSystem.playOneStep();
 		amoeba.learn(studiedSystem.getOutput());
 		
 		/* AUTOMATIC */
-//		long start = System.currentTimeMillis();
-//		for (int i = 0; i < nbCycle; ++i) {
-//			studiedSystem.playOneStep();
-//			amoeba.learn(studiedSystem.getOutput());
-//		}
-//		long end = System.currentTimeMillis();
-//		System.out.println("Done in : " + (end - start) );
-//		
-//		start = System.currentTimeMillis();
-//		for (int i = 0; i < nbCycle; ++i) {
-//			studiedSystem.playOneStep();
-//			amoeba.request(studiedSystem.getOutput());
-//		}
-//		end = System.currentTimeMillis();
-//		System.out.println("Done in : " + (end - start) );
-		
-		
-//		/* XP PIERRE */
-//		
-//		String fileName = fileName(new ArrayList<String>(Arrays.asList("GaussiennePierre")));
-//		
-//		FILE Pierrefile = new FILE("Pierre",fileName);
-//		for (int i = 0; i < nbCycle; ++i) {
-//			studiedSystem.playOneStep();
-//			amoeba.learn(studiedSystem.getOutput());
-//			if(amoeba.getHeadAgent().isActiveLearning()) {
-//				studiedSystem.setActiveLearning(true);
-//				studiedSystem.setSelfRequest(amoeba.getHeadAgent().getSelfRequest());
-//				 
-//			}
-//		}
-//		
-//		for (int i = 0; i < 10; ++i) {
-//			studiedSystem.playOneStep();
-//			System.out.println(studiedSystem.getOutput());
-//			System.out.println(amoeba.request(studiedSystem.getOutput()));
-//			
-//			
-//		}
-//		
-//		Pierrefile.write(new ArrayList<String>(Arrays.asList("ID contexte","Coeff Cte","Coeff X0","Coeff X1","Min Value","Max Value")));
-//		
-//		for(Context ctxt : amoeba.getContexts()) {
-//			
-//			writeMessage(Pierrefile, ctxt.toStringArrayPierre());
+//				long start = System.currentTimeMillis();
+//				for (int i = 0; i < nbCycle; ++i) {
+//					studiedSystem.playOneStep();
+//					amoeba.learn(studiedSystem.getOutput());
+//				}
+//				long end = System.currentTimeMillis();
+//				System.out.println("Done in : " + (end - start) );
+//				
+//				start = System.currentTimeMillis();
+//				for (int i = 0; i < nbCycle; ++i) {
+//					studiedSystem.playOneStep();
+//					amoeba.request(studiedSystem.getOutput());
+//				}
+//				end = System.currentTimeMillis();
+//				System.out.println("Done in : " + (end - start) );
+		
+		
+//				/* XP PIERRE */
+//				
+//				String fileName = fileName(new ArrayList<String>(Arrays.asList("GaussiennePierre")));
+//				
+//				FILE Pierrefile = new FILE("Pierre",fileName);
+//				for (int i = 0; i < nbCycle; ++i) {
+//					studiedSystem.playOneStep();
+//					amoeba.learn(studiedSystem.getOutput());
+//					if(amoeba.getHeadAgent().isActiveLearning()) {
+//						studiedSystem.setActiveLearning(true);
+//						studiedSystem.setSelfRequest(amoeba.getHeadAgent().getSelfRequest());
+//						 
+//					}
+//				}
+//				
+//				for (int i = 0; i < 10; ++i) {
+//					studiedSystem.playOneStep();
+//					System.out.println(studiedSystem.getOutput());
+//					System.out.println(amoeba.request(studiedSystem.getOutput()));
+//					
+//					
+//				}
+//				
+//				Pierrefile.write(new ArrayList<String>(Arrays.asList("ID contexte","Coeff Cte","Coeff X0","Coeff X1","Min Value","Max Value")));
+//				
+//				for(Context ctxt : amoeba.getContexts()) {
+//					
+//					writeMessage(Pierrefile, ctxt.toStringArrayPierre());
 //
-//		}
-//		
-//		
-//		Pierrefile.close();
+//				}
+//				
+//				
+//				Pierrefile.close();
 		
-	
 	}
+
+	
 	
 	public static String fileName(ArrayList<String> infos) {
 		String fileName = "";
@@ -171,4 +177,8 @@ public class F_N_Launcher implements Serializable {
 		file.sendManualMessage();
 		
 	}
+
+
+
+	
 }
diff --git a/AMOEBAonAMAK/src/experiments/nDimensionsLaunchers/F_N_LauncherMultiUI.java b/AMOEBAonAMAK/src/experiments/nDimensionsLaunchers/F_N_LauncherMultiUI.java
new file mode 100644
index 0000000000000000000000000000000000000000..0256e545391f70cdc2c15000e5f924fc42b6b67c
--- /dev/null
+++ b/AMOEBAonAMAK/src/experiments/nDimensionsLaunchers/F_N_LauncherMultiUI.java
@@ -0,0 +1,458 @@
+package experiments.nDimensionsLaunchers;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import experiments.FILE;
+import fr.irit.smac.amak.Configuration;
+import fr.irit.smac.amak.examples.randomantsMultiUi.AntHillExampleMultiUI;
+import fr.irit.smac.amak.examples.randomantsMultiUi.WorldExampleMultiUI;
+import fr.irit.smac.amak.ui.AmasMultiUIWindow;
+import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.VUIMulti;
+import gui.AmoebaMultiUIWindow;
+import gui.AmoebaWindow;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.control.Slider;
+import javafx.stage.Stage;
+import kernel.AMOEBA;
+import kernel.StudiedSystem;
+import kernel.backup.BackupSystem;
+import kernel.backup.IBackupSystem;
+import kernel.backup.SaveHelperImpl;
+import utils.TRACE_LEVEL;
+
+
+/**
+ * The Class BadContextLauncherEasy.
+ */
+public class F_N_LauncherMultiUI extends Application implements Serializable {
+
+
+	public static final double oracleNoiseRange = 0.5;
+	public static final double learningSpeed = 0.01;
+	public static final int regressionPoints = 100;
+	public static final int dimension = 2;
+	public static final double spaceSize = 50.0	;
+	public static final int nbOfModels = 3	;
+	public static final int normType = 2	;
+	public static final boolean randomExploration = true;
+	public static final boolean limitedToSpaceZone = true;
+	//public static final double mappingErrorAllowed = 0.07; // BIG SQUARE
+	public static double mappingErrorAllowed = 0.03; // MULTI
+	public static final double explorationIncrement = 1.0	;
+	public static final double explorationWidht = 0.5	;
+	
+	public static final int nbCycle = 1000;
+	
+	AMOEBA amoeba;
+	StudiedSystem studiedSystem;
+	VUIMulti amoebaVUI;
+	AmoebaMultiUIWindow amoebaUI;
+	
+	AMOEBA amoeba2;
+	StudiedSystem studiedSystem2;
+	VUIMulti amoebaVUI2;
+	AmoebaMultiUIWindow amoebaUI2;
+	
+	public static void main(String[] args) throws IOException {
+		
+		
+		Application.launch(args);
+
+
+	}
+	
+	@Override
+	public void start(Stage arg0) throws Exception, IOException {
+
+		Configuration.multiUI=true;
+		Configuration.commandLineMode = false;
+		Configuration.allowedSimultaneousAgentsExecution = 1;
+		Configuration.waitForGUI = true;
+		Configuration.plotMilliSecondsUpdate = 20000;
+		
+		amoebaVUI = new VUIMulti("2D");
+		amoebaUI = new AmoebaMultiUIWindow("ELLSA", amoebaVUI);
+		
+		
+		// Exemple for adding a tool in the toolbar
+		Slider slider = new Slider(0.01, 0.1, mappingErrorAllowed);
+		slider.setShowTickLabels(true);
+		slider.setShowTickMarks(true);
+		
+		slider.valueProperty().addListener(new ChangeListener<Number>() {
+			@Override
+			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+				System.out.println("new Value "+newValue);
+				mappingErrorAllowed = (double)newValue;
+				amoeba.getEnvironment().setMappingErrorAllowed(mappingErrorAllowed);
+			}
+		});
+		amoebaUI.addToolbar(slider);
+		
+		
+		amoebaVUI2 = new VUIMulti("2D");
+		amoebaUI2 = new AmoebaMultiUIWindow("ELLSA", amoebaVUI2);
+		
+		
+		// Exemple for adding a tool in the toolbar
+		Slider slider2 = new Slider(0.01, 0.1, mappingErrorAllowed);
+		slider2.setShowTickLabels(true);
+		slider2.setShowTickMarks(true);
+		
+		slider2.valueProperty().addListener(new ChangeListener<Number>() {
+			@Override
+			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+				System.out.println("new Value "+newValue);
+				mappingErrorAllowed = (double)newValue;
+				amoeba2.getEnvironment().setMappingErrorAllowed(mappingErrorAllowed);
+			}
+		});
+		amoebaUI2.addToolbar(slider2);
+		
+		
+		
+		startTask(100, 1000);
+		startTask2(500, 100);
+
+
+		
+//		VUIMulti amoebaVUI2 = VUIMulti.get("2D");
+//		AmoebaMultiUIWindow amoebaUI2 = new AmoebaMultiUIWindow("ELLSA", amoebaVUI2);
+//		AMOEBA amoeba2 = new AMOEBA(amoebaUI2,  amoebaVUI2);
+//		
+//		StudiedSystem studiedSystem2 = new F_N_Manager(spaceSize, dimension, nbOfModels, normType, randomExploration, explorationIncrement,explorationWidht,limitedToSpaceZone, oracleNoiseRange);
+//		amoeba2.setStudiedSystem(studiedSystem2);
+//		IBackupSystem backupSystem2 = new BackupSystem(amoeba2);
+//		File file2 = new File("resources/twoDimensionsLauncher.xml");
+//		backupSystem2.load(file2);
+//		
+//		amoeba2.saver = new SaveHelperImpl(amoeba2);
+//		amoeba2.allowGraphicalScheduler(true);
+//		amoeba2.setRenderUpdate(true);		
+//		amoeba2.data.learningSpeed = learningSpeed;
+//		amoeba2.data.numberOfPointsForRegression = regressionPoints;
+//		amoeba2.getEnvironment().setMappingErrorAllowed(mappingErrorAllowed);
+//		
+//		// Exemple for adding a tool in the toolbar
+//		Slider slider2 = new Slider(0.01, 0.1, mappingErrorAllowed);
+//		slider2.setShowTickLabels(true);
+//		slider2.setShowTickMarks(true);
+//		
+//		slider2.valueProperty().addListener(new ChangeListener<Number>() {
+//			@Override
+//			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+//				System.out.println("new Value "+newValue);
+//				mappingErrorAllowed = (double)newValue;
+//				amoeba2.getEnvironment().setMappingErrorAllowed(mappingErrorAllowed);
+//			}
+//		});
+//		amoebaUI2.addToolbar(slider2);
+//		
+//		studiedSystem2.playOneStep();
+//		amoeba2.learn(studiedSystem2.getOutput());
+		
+//		try {
+//			   Thread.sleep(2000) ;
+//			}  catch (InterruptedException e) {
+//			    // gestion de l'erreur
+//			}
+//		
+//		long start = System.currentTimeMillis();
+//		for (int i = 0; i < nbCycle; ++i) {
+//			studiedSystem.playOneStep();
+//			amoeba.learn(studiedSystem.getOutput());
+//		}
+//		long end = System.currentTimeMillis();
+//		System.out.println("Done in : " + (end - start) );
+		
+
+		
+	}
+	
+	public void startTask(long wait, int cycles) 
+    {
+        // Create a Runnable
+        Runnable task = new Runnable()
+        {
+            public void run()
+            {
+                runTask(wait, cycles);
+            }
+        };
+ 
+        // Run the task in a background thread
+        Thread backgroundThread = new Thread(task);
+        // Terminate the running thread if the application exits
+        backgroundThread.setDaemon(true);
+        // Start the thread
+        backgroundThread.start();
+        
+     
+    }
+	
+	public void startTask2(long wait, int cycles) 
+    {
+        // Create a Runnable
+        Runnable task = new Runnable()
+        {
+            public void run()
+            {
+                runTask2(wait, cycles);
+            }
+        };
+ 
+        // Run the task in a background thread
+        Thread backgroundThread = new Thread(task);
+        // Terminate the running thread if the application exits
+        backgroundThread.setDaemon(true);
+        // Start the thread
+        backgroundThread.start();
+        
+     
+    }
+	
+	public void runTask(long wait, int cycles) 
+    {
+		
+		try
+        {
+             
+            // Update the Label on the JavaFx Application Thread        
+            Platform.runLater(new Runnable() 
+            {
+                @Override
+                public void run() 
+                {
+                	amoeba = new AMOEBA(amoebaUI,  amoebaVUI);
+            		studiedSystem = new F_N_Manager(spaceSize, dimension, nbOfModels, normType, randomExploration, explorationIncrement,explorationWidht,limitedToSpaceZone, oracleNoiseRange);
+            		amoeba.setStudiedSystem(studiedSystem);
+            		IBackupSystem backupSystem = new BackupSystem(amoeba);
+            		File file = new File("resources/twoDimensionsLauncher.xml");
+            		backupSystem.load(file);
+            		
+            		amoeba.saver = new SaveHelperImpl(amoeba);
+            		amoeba.allowGraphicalScheduler(true);
+            		amoeba.setRenderUpdate(true);		
+            		amoeba.data.learningSpeed = learningSpeed;
+            		amoeba.data.numberOfPointsForRegression = regressionPoints;
+            		amoeba.getEnvironment().setMappingErrorAllowed(mappingErrorAllowed);
+            		
+                }
+            });
+     
+            Thread.sleep(wait);
+        }
+        catch (InterruptedException e) 
+        {
+            e.printStackTrace();
+        }
+		
+		
+		
+        for(int i = 0; i < cycles; i++) 
+        {
+            try
+            {
+                // Get the Status
+                final String status = "Processing " + i + " of " + cycles;
+                 
+                // Update the Label on the JavaFx Application Thread        
+                Platform.runLater(new Runnable() 
+                {
+                    @Override
+                    public void run() 
+                    {
+                    	studiedSystem.playOneStep();
+                    	amoeba.learn(studiedSystem.getOutput());
+                    	if(amoeba.getHeadAgent().isActiveLearning()) {
+                    		studiedSystem.setActiveLearning(true);
+                    		studiedSystem.setSelfRequest(amoeba.getHeadAgent().getSelfRequest());
+    						 
+    					}
+                    	System.out.println(status);
+                    }
+                });
+         
+                Thread.sleep(wait);
+            }
+            catch (InterruptedException e) 
+            {
+                e.printStackTrace();
+            }
+        }
+    }   
+	
+	public void runTask2(long wait, int cycles) 
+    {
+		
+		try
+        {
+             
+            // Update the Label on the JavaFx Application Thread        
+            Platform.runLater(new Runnable() 
+            {
+                @Override
+                public void run() 
+                {
+                	amoeba2 = new AMOEBA(amoebaUI2,  amoebaVUI2);
+            		studiedSystem2 = new F_N_Manager(spaceSize, dimension, nbOfModels, normType, randomExploration, explorationIncrement,explorationWidht,limitedToSpaceZone, oracleNoiseRange);
+            		amoeba2.setStudiedSystem(studiedSystem2);
+            		IBackupSystem backupSystem2 = new BackupSystem(amoeba2);
+            		File file2 = new File("resources/twoDimensionsLauncher.xml");
+            		backupSystem2.load(file2);
+            		
+            		amoeba2.saver = new SaveHelperImpl(amoeba2);
+            		amoeba2.allowGraphicalScheduler(true);
+            		amoeba2.setRenderUpdate(true);		
+            		amoeba2.data.learningSpeed = learningSpeed;
+            		amoeba2.data.numberOfPointsForRegression = regressionPoints;
+            		amoeba2.getEnvironment().setMappingErrorAllowed(mappingErrorAllowed);
+            		
+                }
+            });
+     
+            Thread.sleep(wait);
+        }
+        catch (InterruptedException e) 
+        {
+            e.printStackTrace();
+        }
+		
+		
+		
+        for(int i = 0; i < cycles; i++) 
+        {
+            try
+            {
+                // Get the Status
+                final String status = "Processing " + i + " of " + cycles;
+                 
+                // Update the Label on the JavaFx Application Thread        
+                Platform.runLater(new Runnable() 
+                {
+                    @Override
+                    public void run() 
+                    {
+                    	studiedSystem2.playOneStep();
+                    	amoeba2.learn(studiedSystem2.getOutput());
+                    	if(amoeba2.getHeadAgent().isActiveLearning()) {
+                    		studiedSystem2.setActiveLearning(true);
+                    		studiedSystem2.setSelfRequest(amoeba2.getHeadAgent().getSelfRequest());
+    						 
+    					}
+                    	System.out.println(status);
+                    }
+                });
+         
+                Thread.sleep(wait);
+            }
+            catch (InterruptedException e) 
+            {
+                e.printStackTrace();
+            }
+        }
+    }   
+	
+	@Override
+	public void stop() throws Exception {
+		super.stop();
+		System.exit(0);
+	}
+
+	public static void launch() throws IOException{
+		
+		
+	
+		
+		
+		
+
+		
+		/* AUTOMATIC */
+//		long start = System.currentTimeMillis();
+//		for (int i = 0; i < nbCycle; ++i) {
+//			studiedSystem.playOneStep();
+//			amoeba.learn(studiedSystem.getOutput());
+//		}
+//		long end = System.currentTimeMillis();
+//		System.out.println("Done in : " + (end - start) );
+//		
+//		start = System.currentTimeMillis();
+//		for (int i = 0; i < nbCycle; ++i) {
+//			studiedSystem.playOneStep();
+//			amoeba.request(studiedSystem.getOutput());
+//		}
+//		end = System.currentTimeMillis();
+//		System.out.println("Done in : " + (end - start) );
+		
+		
+//		/* XP PIERRE */
+//		
+//		String fileName = fileName(new ArrayList<String>(Arrays.asList("GaussiennePierre")));
+//		
+//		FILE Pierrefile = new FILE("Pierre",fileName);
+//		for (int i = 0; i < nbCycle; ++i) {
+//			studiedSystem.playOneStep();
+//			amoeba.learn(studiedSystem.getOutput());
+//			if(amoeba.getHeadAgent().isActiveLearning()) {
+//				studiedSystem.setActiveLearning(true);
+//				studiedSystem.setSelfRequest(amoeba.getHeadAgent().getSelfRequest());
+//				 
+//			}
+//		}
+//		
+//		for (int i = 0; i < 10; ++i) {
+//			studiedSystem.playOneStep();
+//			System.out.println(studiedSystem.getOutput());
+//			System.out.println(amoeba.request(studiedSystem.getOutput()));
+//			
+//			
+//		}
+//		
+//		Pierrefile.write(new ArrayList<String>(Arrays.asList("ID contexte","Coeff Cte","Coeff X0","Coeff X1","Min Value","Max Value")));
+//		
+//		for(Context ctxt : amoeba.getContexts()) {
+//			
+//			writeMessage(Pierrefile, ctxt.toStringArrayPierre());
+//
+//		}
+//		
+//		
+//		Pierrefile.close();
+		
+	
+	}
+	
+	public static String fileName(ArrayList<String> infos) {
+		String fileName = "";
+		
+		for(String info : infos) {
+			fileName += info + "_";
+		}
+		
+		return fileName;
+	}
+	
+	public static void writeMessage(FILE file, ArrayList<String> message) {
+		
+		file.initManualMessage();
+		
+		for(String m : message) {
+			file.addManualMessage(m);
+		}
+		
+		file.sendManualMessage();
+		
+	}
+
+
+
+	
+}
diff --git a/AMOEBAonAMAK/src/experiments/reinforcement/ReinforcementMultiUI.java b/AMOEBAonAMAK/src/experiments/reinforcement/ReinforcementMultiUI.java
new file mode 100644
index 0000000000000000000000000000000000000000..36caa486dc9b19ef94a5d40b915f85c985227a83
--- /dev/null
+++ b/AMOEBAonAMAK/src/experiments/reinforcement/ReinforcementMultiUI.java
@@ -0,0 +1,617 @@
+package experiments.reinforcement;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Random;
+
+import agents.context.localModel.TypeLocalModel;
+import experiments.FILE;
+import experiments.reinforcement.SimpleReinforcement1DSpatialRewardAndActionMltiUI.Environment;
+import experiments.reinforcement.SimpleReinforcement1DSpatialRewardAndActionMltiUI.LearningAgent;
+import fr.irit.smac.amak.Configuration;
+import fr.irit.smac.amak.tools.Log;
+import fr.irit.smac.amak.ui.AmasMultiUIWindow;
+import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.VUIMulti;
+import fr.irit.smac.amak.ui.drawables.Drawable;
+import gui.AmoebaMultiUIWindow;
+import gui.AmoebaWindow;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.control.Slider;
+import javafx.stage.Stage;
+import kernel.AMOEBA;
+import kernel.StudiedSystem;
+import kernel.World;
+import kernel.backup.BackupSystem;
+import kernel.backup.IBackupSystem;
+import kernel.backup.SaveHelperDummy;
+import kernel.backup.SaveHelperImpl;
+import utils.Pair;
+import utils.RandomUtils;
+import utils.TRACE_LEVEL;
+import utils.XmlConfigGenerator;
+
+
+/**
+ * The Class BadContextLauncherEasy.
+ */
+public class ReinforcementMultiUI extends Application implements Serializable {
+
+
+	public static final double oracleNoiseRange = 0.5;
+	public static final double learningSpeed = 0.01;
+	public static final int regressionPoints = 100;
+	public static final int dimension = 2;
+	public static final double spaceSize = 50.0	;
+	public static final int nbOfModels = 3	;
+	public static final int normType = 2	;
+	public static final boolean randomExploration = true;
+	public static final boolean limitedToSpaceZone = true;
+	//public static final double mappingErrorAllowed = 0.07; // BIG SQUARE
+	public static double mappingErrorAllowed = 0.03; // MULTI
+	public static final double explorationIncrement = 1.0	;
+	public static final double explorationWidht = 0.5	;
+	
+	public static final int nbCycle = 1000;
+	
+	/* Learn and Test */
+	public static final int MAX_STEP_PER_EPISODE = 200;
+	public static final int N_LEARN = 1000;//400
+	public static final int N_TEST = 100;
+	
+	/* Exploration */
+	public static final double MIN_EXPLO_RATE = 0.02;
+	public static final double EXPLO_RATE_DIMINUTION_FACTOR = 0.01;
+	public static final double EXPLO_RATE_BASE = 1;
+	
+	AMOEBA amoebaSpatialReward;
+	VUIMulti amoebaSpatialRewardVUI;
+	AmoebaMultiUIWindow amoebaSpatialRewardUI;
+	
+	AMOEBA amoebaControlModel;
+	VUIMulti amoebaControlModelVUI;
+	AmoebaMultiUIWindow amoebaControlModelUI;
+	
+	LearningAgent agent;
+	Environment env;
+	
+	int nbStep;
+	boolean done;
+	boolean invalid;
+	HashMap<String, Double> action;
+	HashMap<String, Double> state ;
+	HashMap<String, Double> state2;
+	double totReward;
+	
+	public static void main(String[] args) throws IOException {
+		
+		
+		Application.launch(args);
+
+
+	}
+	
+	@Override
+	public void start(Stage arg0) throws Exception, IOException {
+
+		Configuration.multiUI=true;
+		Configuration.commandLineMode = false;
+		Configuration.allowedSimultaneousAgentsExecution = 1;
+		Configuration.waitForGUI = true;
+		Configuration.plotMilliSecondsUpdate = 20000;
+		
+		amoebaSpatialRewardVUI = new VUIMulti("2D");
+		amoebaSpatialRewardUI = new AmoebaMultiUIWindow("SPATIAL REWARD", amoebaSpatialRewardVUI);
+		
+		
+		amoebaControlModelVUI = new VUIMulti("2D");
+		amoebaControlModelUI = new AmoebaMultiUIWindow("CONTROL MODEL", amoebaControlModelVUI);
+		
+		startTask(100, 0);		
+	}
+	
+	public void startTask(long wait, int cycles) 
+    {
+        // Create a Runnable
+        Runnable task = new Runnable()
+        {
+            public void run()
+            {
+                runTask(wait, cycles);
+            }
+        };
+ 
+        // Run the task in a background thread
+        Thread backgroundThread = new Thread(task);
+        // Terminate the running thread if the application exits
+        backgroundThread.setDaemon(true);
+        // Start the thread
+        backgroundThread.start();
+        
+     
+    }
+	
+	
+	public void runTask(long wait, int cycles) 
+    {
+		
+//		try
+//        {
+//             
+//            // Update the Label on the JavaFx Application Thread        
+//            Platform.runLater(new Runnable() 
+//            {
+//                @Override
+//                public void run() 
+//                {
+//                	agent = new AmoebaRewardAndControl();
+//                	env = new OneDimensionEnv(10);
+//                }
+//            });
+//     
+//            Thread.sleep(wait);
+//        }
+//        catch (InterruptedException e) 
+//        {
+//            e.printStackTrace();
+//        }
+		
+		
+		
+		agent = new AmoebaRewardAndControl();
+    	env = new OneDimensionEnv(10);
+		
+		state = env.reset(); // BUG LAAAAAAAAAAAAAAAA
+		double explo = EXPLO_RATE_BASE;
+		for(int i = 0; i < N_LEARN; i++) {
+			nbStep = 0;
+			state = env.reset();
+			action = new HashMap<String, Double>();
+			totReward = 0.0;
+			
+			// execute simulation cycles
+			done = false;
+			invalid = false;
+			
+			
+			while(!done && !invalid) {
+				
+				 try
+		            {
+		                 
+		                // Update the Label on the JavaFx Application Thread        
+		                Platform.runLater(new Runnable() 
+		                {
+		                    @Override
+		                    public void run() 
+		                    {
+		                    	nbStep++;
+		        				if(nbStep > MAX_STEP_PER_EPISODE) {
+		        					invalid = true;
+		        				}
+		        				state.remove("oracle");
+		        				
+		        				action = new HashMap<String, Double>();
+		        				
+		        				action = agent.explore(state, env);
+//		        				if(rand.nextDouble() < explo) {
+//		        					action = agent.explore(state, env);
+//		        				} else {
+//		        					action = agent.choose(state, env);
+//		        				}
+		        				
+		        				
+		        				state2 = env.step(action);  // new position with associated reward
+		        				
+		        				if(state2.get("oracle") != -1.0) { //if goal or end of world
+		        					done = true;
+		        				}
+		        				action.put("p1", state.get("p1")); //add previous state to action
+		        				
+		        				action.put("oracle", state2.get("oracle")); //add current reward to action
+		        				
+		        				// state : previous position and associated reward
+		        				// state2 : new position with current reward
+		        				// action : previous state, current action and current reward
+		        				
+		        				agent.learn(state, state2, action, done);
+		        				totReward += action.get("oracle");
+		        				
+		        				state = state2;
+		                    }
+		                });
+		         
+		                Thread.sleep(wait);
+		            }
+		            catch (InterruptedException e) 
+		            {
+		                e.printStackTrace();
+		            }
+				
+				
+				
+			}
+			
+			System.out.println("-----------------------------------------------------------------------");
+			
+			// update exploration rate
+			if(explo > MIN_EXPLO_RATE) {
+				explo -= EXPLO_RATE_DIMINUTION_FACTOR;
+				if(explo < MIN_EXPLO_RATE)
+					explo = MIN_EXPLO_RATE;
+			}
+			
+			System.out.println("Episode "+i+"  reward : "+totReward+"  explo : "+explo);
+			//double testAR = test(agent, env, r, N_TEST);
+			//averageRewards.add(testAR);
+			
+			//Scanner scan = new Scanner(System.in);
+			//scan.nextLine();
+		}
+		
+        
+    }   
+	
+	
+	
+	@Override
+	public void stop() throws Exception {
+		super.stop();
+		System.exit(0);
+	}
+
+	
+	
+	public static String fileName(ArrayList<String> infos) {
+		String fileName = "";
+		
+		for(String info : infos) {
+			fileName += info + "_";
+		}
+		
+		return fileName;
+	}
+	
+	public static void writeMessage(FILE file, ArrayList<String> message) {
+		
+		file.initManualMessage();
+		
+		for(String m : message) {
+			file.addManualMessage(m);
+		}
+		
+		file.sendManualMessage();
+		
+	}
+	
+	/**
+	 * An environment in which a LearningAgent reside
+	 * @author Hugo
+	 *
+	 */
+	public interface Environment {
+		public List<String> actionSpace();
+		public List<String> perceptionSpace();
+		public HashMap<String, Double> reset();
+		public HashMap<String, Double> step(HashMap<String, Double> action);
+		public HashMap<String, Double> randomAction();
+	}
+	
+	/**
+	 * Wrapper for any kind of learning agent
+	 * @author Hugo
+	 *
+	 */
+	public interface LearningAgent {
+		public HashMap<String, Double> choose(HashMap<String, Double> state, Environment env);
+		public HashMap<String, Double> explore(HashMap<String, Double> state, Environment env);
+		public void learn(HashMap<String, Double> state, HashMap<String, Double> state2, HashMap<String, Double> action, boolean done);
+	}
+	
+	public class AmoebaRewardAndControl implements LearningAgent {
+		public double lr = 0.8;
+		public double gamma = 0.9;
+		private Random rand = new Random();
+		
+		public AmoebaRewardAndControl() {
+			amoebaSpatialReward = setupSpatialReward();
+			amoebaControlModel = setupControlModel();
+		}
+		
+		@Override
+		public HashMap<String, Double> choose(HashMap<String, Double> state, Environment env) {
+			
+//			HashMap<String, Double> stateWithVizuAdded = new HashMap<String, Double>(state);
+//			stateWithVizuAdded.put("p2", 0.0);
+//			stateWithVizuAdded.put("oracle", 0.0);
+//			HashMap<String, Double> bestFuturePosition =  amoebaSpatialReward.reinforcementRequest(stateWithVizuAdded);
+//			
+//			HashMap<String, Double> action = new HashMap<String, Double>();
+//			if(bestFuturePosition!=null) {
+//				HashMap<String, Double> requestForControlModel = new HashMap<String, Double>();
+//				requestForControlModel.put("pCurrent", state.get("p1"));
+//				requestForControlModel.put("pGoal", bestFuturePosition.get("p1"));
+//				
+//				double bestAction = amoebaControlModel.request(requestForControlModel);
+//				
+//				
+//				action.put("a1", bestAction);
+//			}
+//			action = env.randomAction();
+//			
+//			return action;
+			return null;
+		}
+
+		@Override
+		public void learn(HashMap<String, Double> state, HashMap<String, Double> positionAndReward,
+				HashMap<String, Double> action, boolean done) {
+			
+			// state : previous position and associated reward
+			// state2 : new position with current reward
+			// action : previous state, current actions and current reward
+			
+			HashMap<String, Double> previousStateCurrentStateAction = new HashMap<>();
+			previousStateCurrentStateAction.put("pCurrent", action.get("p1"));
+			previousStateCurrentStateAction.put("pGoal", positionAndReward.get("p1"));
+			previousStateCurrentStateAction.put("oracle", action.get("a1"));
+			
+
+			
+			//System.out.println("ControlModel " + previousStateCurrentStateAction + "                  ---------------- SIMPLE REIN XP 149");
+			//System.out.println("SpatialReward " + positionAndReward + "                  ---------------- SIMPLE REIN XP 149");
+			
+			amoebaSpatialReward.learn(positionAndReward);
+			amoebaControlModel.learn(previousStateCurrentStateAction);
+			
+		}
+
+		@Override
+		public HashMap<String, Double> explore(HashMap<String, Double> state, Environment env) {
+			return env.randomAction();
+		}
+	}
+	
+	
+	
+	
+	
+	public static class OneDimensionEnv implements Environment {
+		private Random rand = new Random();
+		private double x = 0;
+		private double reward = 0;
+		private double size;
+		private Drawable pos;
+		
+		public OneDimensionEnv(double envSize) {
+			
+			size = envSize;
+			
+			
+		}
+		
+		@Override
+		public HashMap<String, Double> reset(){
+			x = RandomUtils.nextDouble(rand, -size, Math.nextUp(size));
+			x = Math.round(x);
+			reward = 0.0;
+			//pos.move(x+0.5, 0.5);
+			
+			HashMap<String, Double> ret = new HashMap<>();
+			ret.put("p1", x);
+			ret.put("oracle", reward);
+			return ret;
+		}
+		
+		@Override
+		public HashMap<String, Double> step(HashMap<String, Double> actionMap){
+			double action = actionMap.get("a1");
+			//if(action == 0.0) action = rand.nextDouble();
+			if(action > 0.0) action = Math.ceil(action);
+			if(action < 0.0 ) action = Math.floor(action);
+			if(action > 1.0) action = 1.0;
+			if(action < -1.0) action = -1.0;
+			double oldX = x;
+			x = x + action;
+			
+			
+			//System.out.println("ACTIONS " + " a1 " +action + " " + " a2 " + action2);
+			if(x < -size || x > size) {
+				reward = -1000.0;
+			} else if((x == 0.0) || (sign(oldX) != sign(x) )) {
+				// win !
+				reward = 1000.0;
+			} else {
+				reward = -1.0;
+			}
+			HashMap<String, Double> ret = new HashMap<>();
+			ret.put("p1", x);
+			ret.put("oracle", reward);
+			//pos.move(x+0.5, 0.5);
+			return ret;
+		}
+
+		@Override
+		public List<String> actionSpace() {
+			ArrayList<String> l = new ArrayList<>();
+			l.add("a1 enum:true {-1, 0, 1}");
+			return l;
+		}
+
+		@Override
+		public List<String> perceptionSpace() {
+			ArrayList<String> l = new ArrayList<>();
+			l.add("p1 enum:false [-"+size+", "+size+"]");
+			return l;
+		}
+
+		@Override
+		public HashMap<String, Double> randomAction() {
+			double a1 = rand.nextBoolean() ? -1 : 1;
+			
+						
+
+			HashMap<String, Double> action = new HashMap<String, Double>();
+			action.put("a1", a1);
+			return action;
+			}
+		
+	}
+	
+	
+	
+	
+	
+	private AMOEBA setupSpatialReward() {
+		ArrayList<Pair<String, Boolean>> sensors = new ArrayList<>();
+		sensors.add(new Pair<String, Boolean>("p1", false));
+		File config;
+		try {
+			config = File.createTempFile("configSpatialReward", "xml");
+			XmlConfigGenerator.makeXML(config, sensors);
+		} catch (IOException e) {
+			e.printStackTrace();
+			System.exit(1);
+			return null; // now compilator know config is initialized
+		}
+		//File config = new File("resources/simpleReinManualTrained.xml");
+		
+		Log.defaultMinLevel = Log.Level.INFORM;
+		World.minLevel = TRACE_LEVEL.ERROR;
+		AMOEBA amoeba = new AMOEBA(amoebaSpatialRewardUI, amoebaSpatialRewardVUI, config.getAbsolutePath(), null);
+		amoeba.saver = new SaveHelperDummy();
+		
+		
+
+		
+		amoeba.setLocalModel(TypeLocalModel.MILLER_REGRESSION);
+		amoeba.getEnvironment().setMappingErrorAllowed(0.025);
+		//amoeba.setReinforcement(true);
+		
+		
+		return amoeba;
+	}
+	
+
+	private AMOEBA setupControlModel() {
+		ArrayList<Pair<String, Boolean>> sensors = new ArrayList<>();
+		sensors.add(new Pair<String, Boolean>("pCurrent", false));
+		sensors.add(new Pair<String, Boolean>("pGoal", false));
+		File config;
+		try {
+			config = File.createTempFile("configControlModel", "xml");
+			XmlConfigGenerator.makeXML(config, sensors);
+		} catch (IOException e) {
+			e.printStackTrace();
+			System.exit(1);
+			return null; // now compilator know config is initialized
+		}
+		//File config = new File("resources/simpleReinManualTrained.xml");
+		
+		Log.defaultMinLevel = Log.Level.INFORM;
+		World.minLevel = TRACE_LEVEL.ERROR;
+		AMOEBA amoeba = new AMOEBA(amoebaControlModelUI, amoebaControlModelVUI, config.getAbsolutePath(), null);
+		amoeba.saver = new SaveHelperDummy();
+		
+		
+		
+		
+		amoeba.setLocalModel(TypeLocalModel.MILLER_REGRESSION);
+		amoeba.getEnvironment().setMappingErrorAllowed(0.025);
+		
+		return amoeba;
+	}
+
+
+	private static int sign(double x) {
+		return x < 0 ? -1 : 1;
+	}
+	
+	/**
+	 * Teach a learning agent on the SimpleReinforcement problem
+	 * @param agent
+	 * @return
+	 */
+	public static ArrayList<Double> learning(LearningAgent agent, Environment env){
+		ArrayList<Double> averageRewards = new ArrayList<Double>();
+		Random rand = new Random();
+		
+		Random r = new Random();
+		HashMap<String, Double> state = env.reset();
+		HashMap<String, Double> state2;
+		double explo = EXPLO_RATE_BASE;
+		for(int i = 0; i < N_LEARN; i++) {
+			int nbStep = 0;
+			state = env.reset();
+			HashMap<String, Double> action = new HashMap<String, Double>();
+			double totReward = 0.0;
+			
+			// execute simulation cycles
+			boolean done = false;
+			boolean invalid = false;
+			
+			
+			while(!done && !invalid) {
+				nbStep++;
+				if(nbStep > MAX_STEP_PER_EPISODE) {
+					invalid = true;
+				}
+				state.remove("oracle");
+				
+				action = new HashMap<String, Double>();
+				
+				action = agent.explore(state, env);
+//				if(rand.nextDouble() < explo) {
+//					action = agent.explore(state, env);
+//				} else {
+//					action = agent.choose(state, env);
+//				}
+				
+				
+				state2 = env.step(action);  // new position with associated reward
+				
+				if(state2.get("oracle") != -1.0) { //if goal or end of world
+					done = true;
+				}
+				action.put("p1", state.get("p1")); //add previous state to action
+				
+				action.put("oracle", state2.get("oracle")); //add current reward to action
+				
+				// state : previous position and associated reward
+				// state2 : new position with current reward
+				// action : previous state, current action and current reward
+				
+				agent.learn(state, state2, action, done);
+				totReward += action.get("oracle");
+				
+				state = state2;
+			}
+			
+			System.out.println("-----------------------------------------------------------------------");
+			
+			// update exploration rate
+			if(explo > MIN_EXPLO_RATE) {
+				explo -= EXPLO_RATE_DIMINUTION_FACTOR;
+				if(explo < MIN_EXPLO_RATE)
+					explo = MIN_EXPLO_RATE;
+			}
+			
+			System.out.println("Episode "+i+"  reward : "+totReward+"  explo : "+explo);
+			//double testAR = test(agent, env, r, N_TEST);
+			//averageRewards.add(testAR);
+			
+			//Scanner scan = new Scanner(System.in);
+			//scan.nextLine();
+		}
+		
+		return averageRewards;
+	}
+	
+}
diff --git a/AMOEBAonAMAK/src/experiments/reinforcement/SimpleReinforcement1DSpatialRewardAndActionMltiUI.java b/AMOEBAonAMAK/src/experiments/reinforcement/SimpleReinforcement1DSpatialRewardAndActionMltiUI.java
new file mode 100644
index 0000000000000000000000000000000000000000..db5db34acf6a1951f30419533edc72b467bf3344
--- /dev/null
+++ b/AMOEBAonAMAK/src/experiments/reinforcement/SimpleReinforcement1DSpatialRewardAndActionMltiUI.java
@@ -0,0 +1,693 @@
+package experiments.reinforcement;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Random;
+
+import agents.context.localModel.TypeLocalModel;
+import agents.percept.Percept;
+import experiments.nDimensionsLaunchers.F_N_Manager;
+import fr.irit.smac.amak.Configuration;
+import fr.irit.smac.amak.tools.Log;
+import fr.irit.smac.amak.ui.VUIMulti;
+import fr.irit.smac.amak.ui.drawables.Drawable;
+import gui.AmoebaMultiUIWindow;
+import gui.AmoebaWindow;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.control.Slider;
+import javafx.stage.Stage;
+import kernel.AMOEBA;
+import kernel.StudiedSystem;
+import kernel.World;
+import kernel.backup.BackupSystem;
+import kernel.backup.IBackupSystem;
+import kernel.backup.SaveHelperDummy;
+import kernel.backup.SaveHelperImpl;
+import utils.Pair;
+import utils.RandomUtils;
+import utils.TRACE_LEVEL;
+import utils.XmlConfigGenerator;
+
+/**
+ * Train an amoeba on a simple reinforcement task.
+ * The goal of the task is to get to the center. When the position of the agent cross 0, it gets a reward of 100.
+ * The agent can only moves in 2 directions, of a distance of 1. Moving give a reward of -1.
+ * If the agent moves outside of the allowed range, it gets a reward of -100. 
+ * @author Hugo
+ *
+ */
+public abstract class SimpleReinforcement1DSpatialRewardAndActionMltiUI extends Application implements Serializable{
+	
+	/* Learn and Test */
+	public static final int MAX_STEP_PER_EPISODE = 200;
+	public static final int N_LEARN = 1000;//400
+	public static final int N_TEST = 100;
+	
+	/* Exploration */
+	public static final double MIN_EXPLO_RATE = 0.02;
+	public static final double EXPLO_RATE_DIMINUTION_FACTOR = 0.01;
+	public static final double EXPLO_RATE_BASE = 1;
+	
+	AMOEBA amoebaSpatialReward;
+	VUIMulti amoebaSpatialRewardVUI;
+	AmoebaMultiUIWindow amoebaSpatialRewardUI;
+	
+	AMOEBA amoebaControlModel;
+	VUIMulti amoebaControlModelVUI;
+	AmoebaMultiUIWindow amoebaControlModelUI;
+	
+	AMOEBA amoeba;
+	StudiedSystem studiedSystem;
+	VUIMulti amoebaVUI;
+	AmoebaMultiUIWindow amoebaUI;
+	
+	
+	
+	public static void main(String[] args) throws IOException {
+		
+		
+		Application.launch(args);
+		
+	}
+	
+	@Override
+	public void start(Stage arg0) throws Exception, IOException {
+
+		
+		Configuration.multiUI=true;
+		Configuration.commandLineMode = false;
+		Configuration.allowedSimultaneousAgentsExecution = 1;
+		Configuration.waitForGUI = true;
+		Configuration.plotMilliSecondsUpdate = 20000;
+		
+		amoebaVUI = new VUIMulti("2D");
+		amoebaUI = new AmoebaMultiUIWindow("ELLSA", amoebaVUI);
+		
+//		amoebaSpatialRewardVUI = new VUIMulti("2D");
+//		amoebaSpatialRewardUI = new AmoebaMultiUIWindow("SPATIAL_REWARD", amoebaSpatialRewardVUI);
+//		
+//		amoebaControlModelVUI = new VUIMulti("2D");
+//		amoebaControlModelUI = new AmoebaMultiUIWindow("CONTROL_MODEL", amoebaControlModelVUI);
+//		
+//	
+		
+		//startTask(100, 1000);
+
+
+		
+	}
+	
+	public void startTask(long wait, int cycles) 
+    {
+        // Create a Runnable
+        Runnable task = new Runnable()
+        {
+            public void run()
+            {
+                runTask(wait, cycles);
+            }
+        };
+ 
+        // Run the task in a background thread
+        Thread backgroundThread = new Thread(task);
+        // Terminate the running thread if the application exits
+        backgroundThread.setDaemon(true);
+        // Start the thread
+        backgroundThread.start();
+        
+     
+    }
+	
+	public void startTask2(long wait, int cycles) 
+    {
+        // Create a Runnable
+        Runnable task = new Runnable()
+        {
+            public void run()
+            {
+                runTask2(wait, cycles);
+            }
+        };
+ 
+        // Run the task in a background thread
+        Thread backgroundThread = new Thread(task);
+        // Terminate the running thread if the application exits
+        backgroundThread.setDaemon(true);
+        // Start the thread
+        backgroundThread.start();
+        
+     
+    }
+	
+	public void runTask(long wait, int cycles) 
+    {
+		
+		try
+        {
+             
+            // Update the Label on the JavaFx Application Thread        
+            Platform.runLater(new Runnable() 
+            {
+                @Override
+                public void run() 
+                {
+                	
+                	ArrayList<ArrayList<Double>> results = new ArrayList<>();
+        			//LearningAgent agent = new QLearning();
+        			LearningAgent agent = new AmoebaQL();
+        			//LearningAgent agent = new AmoebaCoop();
+        			Environment env = new OneDimensionEnv(10);
+        			results.add(learning(agent, env));
+ 
+            		
+            		int nbEpisodes = results.get(0).size();
+            		for(int i = 0; i < nbEpisodes; i++) {
+            			double average = 0;
+            			for(int j = 0; j < results.size(); j++) {
+            				average += results.get(j).get(i);
+            			}
+            			average /= results.size();
+            			System.out.println(""+i+"\t"+average);
+            		}
+                }
+            });
+     
+            Thread.sleep(wait);
+        }
+        catch (InterruptedException e) 
+        {
+            e.printStackTrace();
+        }
+		
+		
+		
+//        for(int i = 0; i < cycles; i++) 
+//        {
+//            try
+//            {
+//                // Get the Status
+//                final String status = "Processing " + i + " of " + cycles;
+//                 
+//                // Update the Label on the JavaFx Application Thread        
+//                Platform.runLater(new Runnable() 
+//                {
+//                    @Override
+//                    public void run() 
+//                    {
+//                    	///
+//                    }
+//                });
+//         
+//                Thread.sleep(wait);
+//            }
+//            catch (InterruptedException e) 
+//            {
+//                e.printStackTrace();
+//            }
+//        }
+    }   
+	
+	public void runTask2(long wait, int cycles) 
+    {
+		
+		try
+        {
+             
+            // Update the Label on the JavaFx Application Thread        
+            Platform.runLater(new Runnable() 
+            {
+                @Override
+                public void run() 
+                {
+                	///
+            		
+                }
+            });
+     
+            Thread.sleep(wait);
+        }
+        catch (InterruptedException e) 
+        {
+            e.printStackTrace();
+        }
+		
+		
+		
+        for(int i = 0; i < cycles; i++) 
+        {
+            try
+            {
+                // Get the Status
+                final String status = "Processing " + i + " of " + cycles;
+                 
+                // Update the Label on the JavaFx Application Thread        
+                Platform.runLater(new Runnable() 
+                {
+                    @Override
+                    public void run() 
+                    {
+                    	///
+                    }
+                });
+         
+                Thread.sleep(wait);
+            }
+            catch (InterruptedException e) 
+            {
+                e.printStackTrace();
+            }
+        }
+    }   
+	
+	@Override
+	public void stop() throws Exception {
+		super.stop();
+		System.exit(0);
+	}
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	/**
+	 * An environment in which a LearningAgent reside
+	 * @author Hugo
+	 *
+	 */
+	public interface Environment {
+		public List<String> actionSpace();
+		public List<String> perceptionSpace();
+		public HashMap<String, Double> reset();
+		public HashMap<String, Double> step(HashMap<String, Double> action);
+		public HashMap<String, Double> randomAction();
+	}
+	
+	/**
+	 * Wrapper for any kind of learning agent
+	 * @author Hugo
+	 *
+	 */
+	public interface LearningAgent {
+		public HashMap<String, Double> choose(HashMap<String, Double> state, Environment env);
+		public HashMap<String, Double> explore(HashMap<String, Double> state, Environment env);
+		public void learn(HashMap<String, Double> state, HashMap<String, Double> state2, HashMap<String, Double> action, boolean done);
+	}
+	
+	/**
+	 * Compatible only with OneDimensionEnv 
+	 * @author Hugo
+	 *
+	 */
+	public static class AmoebaQL implements LearningAgent {
+		public AMOEBA amoebaSpatialReward;
+		//public AMOEBA amoebaControlModel;
+		public double lr = 0.8;
+		public double gamma = 0.9;
+		private Random rand = new Random();
+		
+		public AmoebaQL() {
+			amoebaSpatialReward = setupSpatialReward();
+			//amoebaControlModel = setupControlModel();
+		}
+		
+		@Override
+		public HashMap<String, Double> choose(HashMap<String, Double> state, Environment env) {
+			
+//			HashMap<String, Double> stateWithVizuAdded = new HashMap<String, Double>(state);
+//			stateWithVizuAdded.put("p2", 0.0);
+//			stateWithVizuAdded.put("oracle", 0.0);
+//			HashMap<String, Double> bestFuturePosition =  amoebaSpatialReward.reinforcementRequest(stateWithVizuAdded);
+//			
+//			HashMap<String, Double> action = new HashMap<String, Double>();
+//			if(bestFuturePosition!=null) {
+//				HashMap<String, Double> requestForControlModel = new HashMap<String, Double>();
+//				requestForControlModel.put("pCurrent", state.get("p1"));
+//				requestForControlModel.put("pGoal", bestFuturePosition.get("p1"));
+//				
+//				double bestAction = amoebaControlModel.request(requestForControlModel);
+//				
+//				
+//				action.put("a1", bestAction);
+//			}
+//			action = env.randomAction();
+//			
+//			return action;
+			return null;
+		}
+
+		@Override
+		public void learn(HashMap<String, Double> state, HashMap<String, Double> positionAndReward,
+				HashMap<String, Double> action, boolean done) {
+			
+			// state : previous position and associated reward
+			// state2 : new position with current reward
+			// action : previous state, current actions and current reward
+			
+			HashMap<String, Double> previousStateCurrentStateAction = new HashMap<>();
+			previousStateCurrentStateAction.put("pCurrent", action.get("p1"));
+			previousStateCurrentStateAction.put("pGoal", positionAndReward.get("p1"));
+			previousStateCurrentStateAction.put("oracle", action.get("a1"));
+			
+
+			
+			//System.out.println("ControlModel " + previousStateCurrentStateAction + "                  ---------------- SIMPLE REIN XP 149");
+			//System.out.println("SpatialReward " + positionAndReward + "                  ---------------- SIMPLE REIN XP 149");
+			
+			amoebaSpatialReward.learn(positionAndReward);
+			//amoebaControlModel.learn(previousStateCurrentStateAction);
+			
+		}
+
+		@Override
+		public HashMap<String, Double> explore(HashMap<String, Double> state, Environment env) {
+			return env.randomAction();
+		}
+	}
+	
+	
+	
+	
+	
+	public static class OneDimensionEnv implements Environment {
+		private Random rand = new Random();
+		private double x = 0;
+		private double reward = 0;
+		private double size;
+		private Drawable pos;
+		
+		public OneDimensionEnv(double envSize) {
+			
+			size = envSize;
+			
+			if(!Configuration.commandLineMode) {
+				AmoebaWindow instance = AmoebaWindow.instance();
+				//pos = new DrawableOval(0.5, 0.5, 1, 1);
+				//pos.setColor(new Color(0.5, 0.0, 0.0, 0.5));
+				//instance.mainVUI.add(pos);
+				//instance.mainVUI.createAndAddRectangle(-50, -0.25, 100, 0.5);
+				//instance.mainVUI.createAndAddRectangle(-0.25, -1, 0.5, 2);
+				instance.point.hide();
+				//instance.rectangle.hide();
+			}
+		}
+		
+		@Override
+		public HashMap<String, Double> reset(){
+			x = RandomUtils.nextDouble(rand, -size, Math.nextUp(size));
+			x = Math.round(x);
+			reward = 0.0;
+			//pos.move(x+0.5, 0.5);
+			
+			HashMap<String, Double> ret = new HashMap<>();
+			ret.put("p1", x);
+			ret.put("oracle", reward);
+			return ret;
+		}
+		
+		@Override
+		public HashMap<String, Double> step(HashMap<String, Double> actionMap){
+			double action = actionMap.get("a1");
+			//if(action == 0.0) action = rand.nextDouble();
+			if(action > 0.0) action = Math.ceil(action);
+			if(action < 0.0 ) action = Math.floor(action);
+			if(action > 1.0) action = 1.0;
+			if(action < -1.0) action = -1.0;
+			double oldX = x;
+			x = x + action;
+			
+			
+			//System.out.println("ACTIONS " + " a1 " +action + " " + " a2 " + action2);
+			if(x < -size || x > size) {
+				reward = -1000.0;
+			} else if((x == 0.0) || (sign(oldX) != sign(x) )) {
+				// win !
+				reward = 1000.0;
+			} else {
+				reward = -1.0;
+			}
+			HashMap<String, Double> ret = new HashMap<>();
+			ret.put("p1", x);
+			ret.put("oracle", reward);
+			//pos.move(x+0.5, 0.5);
+			return ret;
+		}
+
+		@Override
+		public List<String> actionSpace() {
+			ArrayList<String> l = new ArrayList<>();
+			l.add("a1 enum:true {-1, 0, 1}");
+			return l;
+		}
+
+		@Override
+		public List<String> perceptionSpace() {
+			ArrayList<String> l = new ArrayList<>();
+			l.add("p1 enum:false [-"+size+", "+size+"]");
+			return l;
+		}
+
+		@Override
+		public HashMap<String, Double> randomAction() {
+			double a1 = rand.nextBoolean() ? -1 : 1;
+			
+						
+
+			HashMap<String, Double> action = new HashMap<String, Double>();
+			action.put("a1", a1);
+			return action;
+			}
+		
+	}
+	
+	/**
+	 * Setup an amoeba for the SimpleReinforcement problem
+	 * @return
+	 */
+	private static AMOEBA setup() {
+		ArrayList<Pair<String, Boolean>> sensors = new ArrayList<>();
+		sensors.add(new Pair<String, Boolean>("p1", false));
+		File config;
+		try {
+			config = File.createTempFile("config", "xml");
+			XmlConfigGenerator.makeXML(config, sensors);
+		} catch (IOException e) {
+			e.printStackTrace();
+			System.exit(1);
+			return null; // now compilator know config is initialized
+		}
+		//File config = new File("resources/simpleReinManualTrained.xml");
+		
+		Log.defaultMinLevel = Log.Level.INFORM;
+		World.minLevel = TRACE_LEVEL.ERROR;
+		AMOEBA amoeba = new AMOEBA(null, null, config.getAbsolutePath(), null);
+		amoeba.saver = new SaveHelperDummy();
+		
+	
+
+		
+		return amoeba;
+	}
+	
+	
+	
+	private static AMOEBA setupSpatialReward() {
+		ArrayList<Pair<String, Boolean>> sensors = new ArrayList<>();
+		sensors.add(new Pair<String, Boolean>("p1", false));
+		File config;
+		try {
+			config = File.createTempFile("configSpatialReward", "xml");
+			XmlConfigGenerator.makeXML(config, sensors);
+		} catch (IOException e) {
+			e.printStackTrace();
+			System.exit(1);
+			return null; // now compilator know config is initialized
+		}
+		//File config = new File("resources/simpleReinManualTrained.xml");
+		
+		Log.defaultMinLevel = Log.Level.INFORM;
+		World.minLevel = TRACE_LEVEL.ERROR;
+		AMOEBA amoeba = new AMOEBA(null, null, config.getAbsolutePath(), null);
+		amoeba.saver = new SaveHelperDummy();
+		
+		
+
+		
+		amoeba.setLocalModel(TypeLocalModel.MILLER_REGRESSION);
+		amoeba.getEnvironment().setMappingErrorAllowed(0.025);
+		//amoeba.setReinforcement(true);
+		
+		
+		return amoeba;
+	}
+	
+
+	private static AMOEBA setupControlModel() {
+		ArrayList<Pair<String, Boolean>> sensors = new ArrayList<>();
+		sensors.add(new Pair<String, Boolean>("pCurrent", false));
+		sensors.add(new Pair<String, Boolean>("pGoal", false));
+		File config;
+		try {
+			config = File.createTempFile("configControlModel", "xml");
+			XmlConfigGenerator.makeXML(config, sensors);
+		} catch (IOException e) {
+			e.printStackTrace();
+			System.exit(1);
+			return null; // now compilator know config is initialized
+		}
+		//File config = new File("resources/simpleReinManualTrained.xml");
+		
+		Log.defaultMinLevel = Log.Level.INFORM;
+		World.minLevel = TRACE_LEVEL.ERROR;
+		AMOEBA amoeba = new AMOEBA(null, null, config.getAbsolutePath(), null);
+		amoeba.saver = new SaveHelperDummy();
+		
+		
+		
+		
+		amoeba.setLocalModel(TypeLocalModel.MILLER_REGRESSION);
+		amoeba.getEnvironment().setMappingErrorAllowed(0.025);
+		
+		return amoeba;
+	}
+	
+	/**
+	 * Teach a learning agent on the SimpleReinforcement problem
+	 * @param agent
+	 * @return
+	 */
+	public static ArrayList<Double> learning(LearningAgent agent, Environment env){
+		ArrayList<Double> averageRewards = new ArrayList<Double>();
+		Random rand = new Random();
+		
+		Random r = new Random();
+		HashMap<String, Double> state = env.reset();
+		HashMap<String, Double> state2;
+		double explo = EXPLO_RATE_BASE;
+		for(int i = 0; i < N_LEARN; i++) {
+			int nbStep = 0;
+			state = env.reset();
+			HashMap<String, Double> action = new HashMap<String, Double>();
+			double totReward = 0.0;
+			
+			// execute simulation cycles
+			boolean done = false;
+			boolean invalid = false;
+			
+			
+			while(!done && !invalid) {
+				nbStep++;
+				if(nbStep > MAX_STEP_PER_EPISODE) {
+					invalid = true;
+				}
+				state.remove("oracle");
+				
+				action = new HashMap<String, Double>();
+				
+				action = agent.explore(state, env);
+//				if(rand.nextDouble() < explo) {
+//					action = agent.explore(state, env);
+//				} else {
+//					action = agent.choose(state, env);
+//				}
+				
+				
+				state2 = env.step(action);  // new position with associated reward
+				
+				if(state2.get("oracle") != -1.0) { //if goal or end of world
+					done = true;
+				}
+				action.put("p1", state.get("p1")); //add previous state to action
+				
+				action.put("oracle", state2.get("oracle")); //add current reward to action
+				
+				// state : previous position and associated reward
+				// state2 : new position with current reward
+				// action : previous state, current action and current reward
+				
+				agent.learn(state, state2, action, done);
+				totReward += action.get("oracle");
+				
+				state = state2;
+			}
+			
+			System.out.println("-----------------------------------------------------------------------");
+			
+			// update exploration rate
+			if(explo > MIN_EXPLO_RATE) {
+				explo -= EXPLO_RATE_DIMINUTION_FACTOR;
+				if(explo < MIN_EXPLO_RATE)
+					explo = MIN_EXPLO_RATE;
+			}
+			
+			System.out.println("Episode "+i+"  reward : "+totReward+"  explo : "+explo);
+			//double testAR = test(agent, env, r, N_TEST);
+			//averageRewards.add(testAR);
+			
+			//Scanner scan = new Scanner(System.in);
+			//scan.nextLine();
+		}
+		
+		return averageRewards;
+	}
+
+	private static double test(LearningAgent agent, Environment env, Random r, int nbTest) {
+		HashMap<String, Double> state;
+		HashMap<String, Double> state2;
+		double nbPositiveReward = 0.0;
+		double tot_reward = 0.0;
+		for(int i = 0; i < nbTest; i++) {
+			double reward = 0.0;
+			state = env.reset();
+			
+			// execute simulation cycles
+			boolean done = false;
+			int nbStep = 0;
+			while(!done) {
+				nbStep++;
+				if(nbStep > 200) {
+					done = true;
+				}
+				state.remove("oracle");
+				 HashMap<String, Double> a = agent.choose(state, env);
+				
+				state2 = env.step(a);
+				
+				if(state2.get("oracle") != -1.0) {
+					done = true;
+				}
+				
+				reward += state2.get("oracle");
+				
+				state = state2;
+			}
+			if(reward > 0) {
+				nbPositiveReward += 1.0;
+			}
+			tot_reward += reward;
+		}
+		double averageReward = tot_reward/nbTest;
+		System.out.println("Test average reward : "+averageReward+"  Positive reward %: "+(nbPositiveReward/nbTest));
+		
+		return averageReward;
+	}
+	
+	
+	
+	private static int sign(double x) {
+		return x < 0 ? -1 : 1;
+	}
+
+}
\ No newline at end of file
diff --git a/AMOEBAonAMAK/src/gui/AmoebaMultiUIWindow.java b/AMOEBAonAMAK/src/gui/AmoebaMultiUIWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..511c19cf1f7b1299259d8d0d7b624ae1e547de9e
--- /dev/null
+++ b/AMOEBAonAMAK/src/gui/AmoebaMultiUIWindow.java
@@ -0,0 +1,166 @@
+package gui;
+
+import java.util.HashMap;
+
+import javax.management.InstanceAlreadyExistsException;
+
+import fr.irit.smac.amak.tools.Log;
+import fr.irit.smac.amak.tools.RunLaterHelper;
+import fr.irit.smac.amak.ui.AmakPlot;
+import fr.irit.smac.amak.ui.AmakPlot.ChartType;
+import fr.irit.smac.amak.ui.AmasMultiUIWindow;
+import fr.irit.smac.amak.ui.MainWindow;
+import fr.irit.smac.amak.ui.SchedulerToolbar;
+import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.VUIMulti;
+import fr.irit.smac.amak.ui.drawables.Drawable;
+import javafx.application.Application;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.control.Button;
+import javafx.scene.control.Menu;
+import javafx.scene.control.Slider;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.Tooltip;
+import javafx.scene.paint.Color;
+import kernel.AMOEBA;
+import kernel.backup.SaveHelperImpl;
+
+/**
+ * The multi window for AMOEBA GUI.
+ * @author Bruno
+ *
+ */
+public class AmoebaMultiUIWindow extends AmasMultiUIWindow{
+
+	protected HashMap<String, AmakPlot> plots = new HashMap<>();
+	
+	/**
+	 * The main {@link VUI} for AMOEBA, by default it's the 2D representation of the contexts.
+	 */
+	public VUIMulti mainVUI;
+	
+	public Drawable point;
+	public Drawable rectangle;
+	public ToggleButton toggleRender;
+	public SchedulerToolbar schedulerToolbar;
+	public DimensionSelector dimensionSelector;
+	public Menu windowMenu;
+	
+	public AmoebaMultiUIWindow(String title, VUIMulti vui) {
+		super(title);
+		mainVUI = vui;
+	}
+	
+	public void initialize(AMOEBA amoeba) {
+		
+
+		mainVUI.setDefaultView(200, 0, 0);
+		//addTabbedPanel("2D VUI", mainVUI.getPanel());
+		
+		// scheduler toolbar
+		schedulerToolbar = new SchedulerToolbar("AMOEBA", amoeba.getScheduler());
+		addToolbar(schedulerToolbar);	
+		
+		// plots
+		point = mainVUI.createAndAddPoint(0, 0);
+		point.setName("Cursor");
+		rectangle = mainVUI.createAndAddRectangle(10, 10, 10, 10);
+		rectangle.setName("Neighborhood");
+		rectangle.setColor(new Color(1, 1, 1, 0));
+		
+		plots.put("This loop NCS", new AmakPlot(this, "This loop NCS", ChartType.LINE, "Cycle", "Number of NCS"));
+		plots.put("All time NCS", new AmakPlot(this, "All time NCS", ChartType.LINE, "Cycle", "Number of NCS"));
+		plots.put("Number of agents", new AmakPlot(this, "Number of agents", ChartType.LINE, "Cycle", "Number of agents"));
+		plots.put("Errors", new AmakPlot(this, "Errors", ChartType.LINE, "Cycle", "Coefficients"));
+		plots.put("Distances to models", new AmakPlot(this, "Distances to models", ChartType.LINE, "Cycle", "Distances"));
+		plots.put("Global Mapping Criticality", new AmakPlot(this, "Global Mapping Criticality", ChartType.LINE, "Cycle", "Criticalities"));
+		plots.put("Time Execution", new AmakPlot(this, "Time Execution", ChartType.LINE, "Cycle", "Times"));
+		plots.put("Criticalities", new AmakPlot(this, "Criticalities", ChartType.LINE, "Cycle", "Criticalities"));
+		
+		// update render button
+		toggleRender = new ToggleButton("Allow Rendering");
+		toggleRender.setOnAction(evt -> {
+			amoeba.setRenderUpdate(toggleRender.isSelected()); 
+			if(amoeba.isRenderUpdate()) {
+				amoeba.updateAgentsVisualisation();
+				amoeba.nextCycleRunAllAgents();
+			}
+		});
+		toggleRender.setSelected(amoeba.isRenderUpdate());
+		addToolbar(toggleRender);
+		
+		// dimension selector
+		dimensionSelector = new DimensionSelector(amoeba.getPercepts(), new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				amoeba.updateAgentsVisualisation();
+			}
+		});
+		RunLaterHelper.runLater(()->mainVUI.toolbar.getItems().add(dimensionSelector));
+		
+		// contextMenu "Request Here" on VUI
+		new ContextMenuVUIMulti(amoeba, mainVUI); //the ContextMenu add itself to the VUI
+		
+		// manual save button
+		addToolbar(newManualSaveButton(amoeba));
+		
+		Slider slider = new Slider(0, 0.1, 0.1);
+		slider.setShowTickLabels(true);
+		slider.setShowTickMarks(true);
+		slider.valueProperty().addListener(new ChangeListener<Number>() {
+			@Override
+			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+				amoeba.getEnvironment().mappingErrorAllowed = newValue.doubleValue();
+			}
+		});
+		addToolbar(slider);
+	}
+	
+	
+	
+	/**
+	 * Get an existing {@link AmakPlot}. 
+	 * @param name name of the plot to get
+	 * @return an existing plot.
+	 * @see AmoebaMultiUIWindow#addPlot(String, AmakPlot)
+	 */
+	public AmakPlot getPlot(String name) {
+		return plots.get(name);
+	}
+	
+	/**
+	 * Add an {@link AmakPlot} to le map of plots. Allowing for easy access with {@code AmoebaWindow.instance().getPlot(name)}
+	 * @param name name of the plot to add
+	 * @param plot the plot to add
+	 * @see AmoebaMultiUIWindow#getPlot(String)
+	 */
+	public void addPlot(String name, AmakPlot plot) {
+		plots.put(name, plot);
+	}
+	
+	/**
+	 * Create a button 'Quick Save' button, when clicked create a manual save point using an amoeba's saver.
+	 * @param amoeba
+	 * @return
+	 * @see AMOEBA#saver
+	 * @see SaveHelperImpl#newManualSave(String)
+	 */
+	public Button newManualSaveButton(AMOEBA amoeba) {
+		Button button = new Button("Quick save");
+		button.setTooltip(new Tooltip("Create a new save point. You will be able to find it in 'Save Explorer' -> 'Manual Saves'"));
+		button.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				if(amoeba.saver != null) {
+					amoeba.saver.newManualSave("manualSaveButton");
+				} else {
+					Log.defaultLog.error("Main Window", "Cannot make a save point of an amoeba without saver");
+				}
+			}
+		});
+		return button;
+	}
+}
diff --git a/AMOEBAonAMAK/src/gui/ContextMenuVUI.java b/AMOEBAonAMAK/src/gui/ContextMenuVUI.java
index eb47bc31e8ec91b5b6416e88163a31e30a75af14..07d72a504280fdf50a7719a5cdc0869bace1ddb1 100644
--- a/AMOEBAonAMAK/src/gui/ContextMenuVUI.java
+++ b/AMOEBAonAMAK/src/gui/ContextMenuVUI.java
@@ -6,6 +6,7 @@ import java.util.Optional;
 import agents.percept.Percept;
 import fr.irit.smac.amak.tools.Log;
 import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.VUIMulti;
 import javafx.event.ActionEvent;
 import javafx.event.EventHandler;
 import javafx.scene.control.Button;
diff --git a/AMOEBAonAMAK/src/gui/ContextMenuVUIMulti.java b/AMOEBAonAMAK/src/gui/ContextMenuVUIMulti.java
new file mode 100644
index 0000000000000000000000000000000000000000..68999ebe6d9d75dc4948c5ee1644fe17e852fcc1
--- /dev/null
+++ b/AMOEBAonAMAK/src/gui/ContextMenuVUIMulti.java
@@ -0,0 +1,250 @@
+package gui;
+
+import java.util.HashMap;
+import java.util.Optional;
+
+import agents.percept.Percept;
+import fr.irit.smac.amak.tools.Log;
+import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.VUIMulti;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.control.Button;
+import javafx.scene.control.ButtonBar.ButtonData;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.ContextMenu;
+import javafx.scene.control.Dialog;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.TextField;
+import javafx.scene.input.ContextMenuEvent;
+import javafx.scene.layout.VBox;
+import kernel.AMOEBA;
+
+/**
+ * The ContextMenu that is shown when right-clicking the {@link VUI} canvas
+ * @author Hugo
+ *
+ */
+public class ContextMenuVUIMulti extends ContextMenu {
+	/**
+	 * If true will skip window asking for input in 2D problems
+	 */
+	public static boolean quick2DRequest = false;
+	private double reqHereX;
+	private double reqHereY;
+	
+	/**
+	 * Create a {@link ContextMenu} suited for our needs, composed of 2 items : "Request Here" and "Learn here".<br/>
+	 * Set itself as the vui canvas {@link ContextMenu}. 
+	 * @param amoeba the amoeba where {@link AMOEBA#request(HashMap)} and {@link AMOEBA#learn(HashMap)} will be executed.
+	 * @param vui the {@link VUI} hosting the {@link ContextMenuVUIMulti}
+	 */
+	public ContextMenuVUIMulti(AMOEBA amoeba, VUIMulti vui) {
+		// "request here" menu item
+		setupRequestHereMenuItem(amoeba, vui);
+		
+		// "learn here" menu item
+		setupLearnHereMenuItem(amoeba, vui);
+		
+		// show context menu on context menu event from VUI's canvas
+		vui.getCanvas().setOnContextMenuRequested(new EventHandler<ContextMenuEvent>() {
+			@Override
+			public void handle(ContextMenuEvent event) {
+				reqHereX = event.getX();
+				reqHereY = event.getY();
+				ContextMenuVUIMulti.this.show(vui.getCanvas(), event.getScreenX(), event.getScreenY());
+			}
+		});	
+	}
+
+	private void setupRequestHereMenuItem(AMOEBA amoeba, VUIMulti vui) {
+		MenuItem reqHere = new MenuItem("Request Here");
+		reqHere.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				if(quick2DRequest && amoeba.getPercepts().size() == 2) {
+					reqTwoDimension(amoeba, vui);
+				} else {
+					reqNDimension(amoeba, vui);
+				}
+			}
+
+		});
+		this.getItems().add(reqHere);
+	}
+	
+	/**
+	 * The "Request Here" action performed when the amoeba is 2D.<br/>
+	 * Execute a {@link AMOEBA#request(HashMap)} at the position of the click.
+	 * @param amoeba
+	 * @param vui
+	 */
+	private void reqTwoDimension(AMOEBA amoeba, VUIMulti vui) {
+		double x = vui.screenToWorldX(reqHereX);
+		double y = vui.screenToWorldY(reqHereY);
+		HashMap<String, Double> req = new HashMap<String, Double>();
+		req.put(amoeba.getDimensionSelector().d1().getName(), x);
+		req.put(amoeba.getDimensionSelector().d2().getName(), y);
+		req.put("oracle", 0.0);
+		double res = amoeba.request(req);
+		Log.defaultLog.inform("AMOEBA", "Request Here for x:"+x+" y:"+y+" -> "+res+".");
+	}
+	
+	/**
+	 * The "Request Here" action performed when the amoeba is not 2D.<br/>
+	 * Show a {@link Dialog} prompting the user to inputs value for the {@link AMOEBA#request(HashMap)}.
+	 * @param amoeba
+	 * @param vui
+	 */
+	private void reqNDimension(AMOEBA amoeba, VUIMulti vui) {
+		double x = vui.screenToWorldX(reqHereX);
+		double y = vui.screenToWorldY(reqHereY);
+		
+		Dialog<HashMap<String, Double>> dialog = new Dialog<>();
+		dialog.setTitle("Inputs");
+		dialog.setHeaderText("Fill inputs");
+		
+	    // Set the button types.
+	    ButtonType okButtonType = new ButtonType("OK", ButtonData.OK_DONE);
+	    dialog.getDialogPane().getButtonTypes().addAll(okButtonType, ButtonType.CANCEL);
+		
+		// inputs
+		HashMap<String, TextField> textFields = new HashMap<>();
+		VBox vbox = new VBox();
+		for(Percept p : amoeba.getPercepts()) {
+			TextField tf = new TextField();
+			textFields.put(p.getName(), tf);
+			tf.setPromptText(p.getName());
+			if(p.getName().equals(amoeba.getDimensionSelector().d1().getName())) {
+				tf.setText(x+"");
+			}
+			if(p.getName().equals(amoeba.getDimensionSelector().d2().getName())) {
+				tf.setText(y+"");
+			}
+			vbox.getChildren().add(tf);
+		}
+		
+		dialog.getDialogPane().setContent(vbox);
+		dialog.setResultConverter(dialogButton -> {
+	        if (dialogButton == okButtonType) {
+	        	HashMap<String, Double> req = new HashMap<String, Double>();
+	        	for(String k : textFields.keySet()) {
+	        		req.put(k, Double.valueOf(textFields.get(k).getText()));
+	        	}
+	        	req.put("oracle", 0.0);
+	            return req;
+	        }
+	        return null;
+	    });
+		
+		Optional<HashMap<String, Double>> result = dialog.showAndWait();
+		result.ifPresent(req -> {
+			double res = amoeba.request(req);
+			Log.defaultLog.inform("AMOEBA", "Request Here for "+req+"\n-> "+res+".");
+		});
+	}
+	
+	private void setupLearnHereMenuItem(AMOEBA amoeba, VUIMulti vui) {
+		MenuItem learnHere = new MenuItem("Learn Here");
+		learnHere.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				if(amoeba.getPercepts().size() == 2) {
+					learnTwoDimension(amoeba, vui);
+				} else {
+					learnNDimebsion(amoeba, vui);
+				}
+			}
+
+		});
+		this.getItems().add(learnHere);
+	}
+	
+	/**
+	 * The "Learn Here" action performed when the amoeba is 2D.<br/>
+	 * Execute a {@link AMOEBA#learn(HashMap)} at the position of the click.
+	 * @param amoeba
+	 * @param vui
+	 */
+	private void learnTwoDimension(AMOEBA amoeba, VUIMulti vui) {
+		double x = vui.screenToWorldX(reqHereX);
+		double y = vui.screenToWorldY(reqHereY);
+		HashMap<String, Double> req = new HashMap<String, Double>();
+		req.put(amoeba.getDimensionSelector().d1().getName(), x);
+		req.put(amoeba.getDimensionSelector().d2().getName(), y);
+		req.put("oracle", amoeba.studiedSystem.requestOracle(req));
+		amoeba.learn(req);
+	}
+	
+	/**
+	 * The "Learn Here" action performed when the amoeba is not 2D.<br/>
+	 * Show a {@link Dialog} prompting the user to inputs value for the {@link AMOEBA#learn(HashMap)}.
+	 * @param amoeba
+	 * @param vui
+	 */
+	private void learnNDimebsion(AMOEBA amoeba, VUIMulti vui) {
+		double x = vui.screenToWorldX(reqHereX);
+		double y = vui.screenToWorldY(reqHereY);
+		
+		Dialog<HashMap<String, Double>> dialog = new Dialog<>();
+		dialog.setTitle("Inputs");
+		dialog.setHeaderText("Fill inputs");
+		
+	    // Set the button types.
+	    ButtonType okButtonType = new ButtonType("OK", ButtonData.OK_DONE);
+	    dialog.getDialogPane().getButtonTypes().addAll(okButtonType, ButtonType.CANCEL);
+		
+		// inputs
+		HashMap<String, TextField> textFields = new HashMap<>();
+		VBox vbox = new VBox();
+		for(Percept p : amoeba.getPercepts()) {
+			TextField tf = new TextField();
+			textFields.put(p.getName(), tf);
+			tf.setPromptText(p.getName());
+			if(p.getName().equals(amoeba.getDimensionSelector().d1().getName())) {
+				tf.setText(x+"");
+			}
+			if(p.getName().equals(amoeba.getDimensionSelector().d2().getName())) {
+				tf.setText(y+"");
+			}
+			vbox.getChildren().add(tf);
+		}
+		
+		//oracle
+		TextField oracle = new TextField();
+		textFields.put("oracle", oracle);
+		oracle.setPromptText("oracle");
+		Button autoOracle = new Button("Autofill oracle");
+		autoOracle.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				HashMap<String, Double> req = new HashMap<String, Double>();
+				for(String k : textFields.keySet()) {
+					if(!"oracle".equals(k)) {
+						req.put(k, Double.valueOf(textFields.get(k).getText()));
+					}
+	        	}
+				oracle.setText(amoeba.studiedSystem.requestOracle(req)+"");
+			}
+		});
+		vbox.getChildren().addAll(oracle, autoOracle);
+		
+		dialog.getDialogPane().setContent(vbox);
+		dialog.setResultConverter(dialogButton -> {
+	        if (dialogButton == okButtonType) {
+	        	HashMap<String, Double> req = new HashMap<String, Double>();
+	        	for(String k : textFields.keySet()) {
+	        		req.put(k, Double.valueOf(textFields.get(k).getText()));
+	        	}
+	            return req;
+	        }
+	        return null;
+	    });
+		
+		Optional<HashMap<String, Double>> result = dialog.showAndWait();
+		result.ifPresent(req -> {
+			amoeba.learn(req);
+			Log.defaultLog.inform("AMOEBA", "Learn Here for "+req+" done.");
+		});
+	}
+}
diff --git a/AMOEBAonAMAK/src/gui/ContextRendererFX.java b/AMOEBAonAMAK/src/gui/ContextRendererFX.java
index 23f4160219d97d02da8a8ac0d9914b99ed48b854..b9df13932aa2612a5d0df449891c0683479db291 100644
--- a/AMOEBAonAMAK/src/gui/ContextRendererFX.java
+++ b/AMOEBAonAMAK/src/gui/ContextRendererFX.java
@@ -2,6 +2,7 @@ package gui;
 
 import agents.context.Context;
 import agents.percept.Percept;
+import fr.irit.smac.amak.ui.VUIMulti;
 import fr.irit.smac.amak.ui.drawables.DrawableRectangle;
 import gui.utils.ContextColor;
 import javafx.scene.paint.Color;
@@ -99,8 +100,8 @@ public class ContextRendererFX extends RenderStrategy {
 	 * window.
 	 */
 	@Override
-	public void initialize() {
-		getDrawable().setName(context.toString()); // create the drawable if it does not exist
+	public void initialize(VUIMulti vui) {
+		getDrawable(vui).setName(context.toString()); // create the drawable if it does not exist
 
 	}
 
@@ -116,10 +117,10 @@ public class ContextRendererFX extends RenderStrategy {
 	 * 
 	 * @return
 	 */
-	public DrawableRectangle getDrawable() {
+	public DrawableRectangle getDrawable(VUIMulti vui) {
 		if (!context.isDying() && drawable == null) {
 			drawable = new DrawableContext(0, 0, 0, 0, context);
-			AmoebaWindow.instance().mainVUI.add(drawable);
+			vui.add(drawable);
 		}
 		return drawable;
 	}
diff --git a/AMOEBAonAMAK/src/gui/NoneRenderer.java b/AMOEBAonAMAK/src/gui/NoneRenderer.java
index 6d154fdbf4b7ea73541d8e593b34fe66841d0e4c..438a9809fe1a6c41d54ca611719fdcd80e2e25b7 100644
--- a/AMOEBAonAMAK/src/gui/NoneRenderer.java
+++ b/AMOEBAonAMAK/src/gui/NoneRenderer.java
@@ -1,5 +1,7 @@
 package gui;
 
+import fr.irit.smac.amak.ui.VUIMulti;
+
 /**
  * A render strategy that does nothing.
  * @author Hugo
@@ -12,7 +14,7 @@ public class NoneRenderer extends RenderStrategy {
 	}
 
 	@Override
-	public void initialize() {
+	public void initialize(VUIMulti vui) {
 	}
 
 	@Override
diff --git a/AMOEBAonAMAK/src/gui/RenderStrategy.java b/AMOEBAonAMAK/src/gui/RenderStrategy.java
index d6bf4f7424a9b9166fb9d1ffe30ae5a34d740e99..7552c9518486d3050b687bb8f78114e2d274e774 100644
--- a/AMOEBAonAMAK/src/gui/RenderStrategy.java
+++ b/AMOEBAonAMAK/src/gui/RenderStrategy.java
@@ -1,5 +1,7 @@
 package gui;
 
+import fr.irit.smac.amak.ui.VUIMulti;
+
 /**
  * Strategy on how to render an object.
  * See {@link ContextRendererFX} for example on how to extends this class.
@@ -16,7 +18,8 @@ public abstract class RenderStrategy {
 	/**
 	 * Called when the rendered object need to be initialized
 	 */
-	abstract public void initialize();
+	//abstract public void initialize();
+	abstract public void initialize(VUIMulti vui);
 	
 	/**
 	 * Called to render the object.
diff --git a/AMOEBAonAMAK/src/gui/saveExplorer/SaveExplorer.java b/AMOEBAonAMAK/src/gui/saveExplorer/SaveExplorer.java
index 1cb91054d89b371ffccabf2fc05bc2551eab408c..325f0160b06ab9f6dad1394f332bd6d0cd057fa1 100644
--- a/AMOEBAonAMAK/src/gui/saveExplorer/SaveExplorer.java
+++ b/AMOEBAonAMAK/src/gui/saveExplorer/SaveExplorer.java
@@ -257,7 +257,7 @@ public class SaveExplorer extends VBox {
 	 */
 	public static void main(String[] args) throws ClassNotFoundException, IOException {
 		System.out.println("New AMOEBA launched.");
-		AMOEBA amoeba = new AMOEBA(args[0], (StudiedSystem)SerializeBase64.deserialize(args[1]));
+		AMOEBA amoeba = new AMOEBA(null,null,args[0], (StudiedSystem)SerializeBase64.deserialize(args[1]));
 		//amoeba.allowGraphicalScheduler(false);
 		for(Percept p : amoeba.getPercepts()) {
 			p.setValue(amoeba.getPerceptions(p.getName()));
diff --git a/AMOEBAonAMAK/src/kernel/AMOEBA.java b/AMOEBAonAMAK/src/kernel/AMOEBA.java
index 3eef319e9653a6fef3c5c8916ef840a84979110b..3703f834e22b855f9cbafdec95628a492b52a5d9 100644
--- a/AMOEBAonAMAK/src/kernel/AMOEBA.java
+++ b/AMOEBAonAMAK/src/kernel/AMOEBA.java
@@ -22,6 +22,8 @@ import fr.irit.smac.amak.Scheduling;
 import fr.irit.smac.amak.tools.Log;
 import fr.irit.smac.amak.tools.RunLaterHelper;
 import fr.irit.smac.amak.ui.AmakPlot;
+import fr.irit.smac.amak.ui.VUIMulti;
+import gui.AmoebaMultiUIWindow;
 import gui.AmoebaWindow;
 import gui.DimensionSelector;
 import kernel.backup.IBackupSystem;
@@ -37,6 +39,9 @@ import utils.PrintOnce;
  */
 public class AMOEBA extends Amas<World> implements IAMOEBA {
 	// -- Attributes
+	
+	
+	public VUIMulti vuiMulti;
 	/**
 	 * Utility to save, autosave, and load amoebas.
 	 */
@@ -47,6 +52,8 @@ public class AMOEBA extends Amas<World> implements IAMOEBA {
 	 */
 	public StudiedSystem studiedSystem;
 	
+	public AmoebaMultiUIWindow multiUIWindow;
+	
 	private Head head;
 	private TypeLocalModel localModel = TypeLocalModel.MILLER_REGRESSION;
 	private HashMap<String, Double> perceptions = new HashMap<String, Double>();
@@ -82,8 +89,9 @@ public class AMOEBA extends Amas<World> implements IAMOEBA {
 	 * @param studiedSystem
 	 *            the studied system
 	 */
-	public AMOEBA() {
-		super(new World(), Scheduling.HIDDEN);
+	public AMOEBA(AmoebaMultiUIWindow window, VUIMulti vui) {
+		super(window, vui, new World(), Scheduling.HIDDEN);
+		vuiMulti = vui;
 	}
 	
 	/**
@@ -91,8 +99,9 @@ public class AMOEBA extends Amas<World> implements IAMOEBA {
 	 * 
 	 * @param path path to the config file.
 	 */
-	public AMOEBA(String path, StudiedSystem studiedSystem) {
-		super(new World(), Scheduling.HIDDEN);
+	public AMOEBA(AmoebaMultiUIWindow window, VUIMulti vui, String path, StudiedSystem studiedSystem) {
+		super(window, vui, new World(), Scheduling.HIDDEN);
+		vuiMulti = vui;
 		this.studiedSystem = studiedSystem;
 		setRenderUpdate(true);
 		saver = new SaveHelperImpl(this);
@@ -112,23 +121,22 @@ public class AMOEBA extends Amas<World> implements IAMOEBA {
 	
 	@Override
 	protected void onRenderingInitialization() {
-		AmoebaWindow.instance().initialize(this);
+		((AmoebaMultiUIWindow) amasMultiUIWindow).initialize(this);
 	}
 
 	@Override
 	protected void onUpdateRender() {
 		// Update statistics
-		if(AmoebaWindow.isInstance()) {
-			AmoebaWindow window = AmoebaWindow.instance();
-
-			AmakPlot loopNCS = window.getPlot("This loop NCS");
-			AmakPlot allNCS = window.getPlot("All time NCS");
-			AmakPlot nbAgent = window.getPlot("Number of agents");
-			AmakPlot errors = window.getPlot("Errors");
-			AmakPlot distancesToModels = window.getPlot("Distances to models");
-			AmakPlot gloabalMappingCriticality = window.getPlot("Global Mapping Criticality");
-			AmakPlot timeExecution = window.getPlot("Time Execution");
-			AmakPlot criticalities = window.getPlot("Criticalities");
+		if(amasMultiUIWindow!=null) {
+
+			AmakPlot loopNCS = ((AmoebaMultiUIWindow)amasMultiUIWindow).getPlot("This loop NCS");
+			AmakPlot allNCS = ((AmoebaMultiUIWindow)amasMultiUIWindow).getPlot("All time NCS");
+			AmakPlot nbAgent = ((AmoebaMultiUIWindow)amasMultiUIWindow).getPlot("Number of agents");
+			AmakPlot errors = ((AmoebaMultiUIWindow)amasMultiUIWindow).getPlot("Errors");
+			AmakPlot distancesToModels = ((AmoebaMultiUIWindow)amasMultiUIWindow).getPlot("Distances to models");
+			AmakPlot gloabalMappingCriticality = ((AmoebaMultiUIWindow)amasMultiUIWindow).getPlot("Global Mapping Criticality");
+			AmakPlot timeExecution = ((AmoebaMultiUIWindow)amasMultiUIWindow).getPlot("Time Execution");
+			AmakPlot criticalities = ((AmoebaMultiUIWindow)amasMultiUIWindow).getPlot("Criticalities");
 			
 			
 			boolean notify = isRenderUpdate();
@@ -165,6 +173,8 @@ public class AMOEBA extends Amas<World> implements IAMOEBA {
 			timeExecution.addData("BestContextInNeighbors", cycle, data.executionTimesSums[8], notify);
 			timeExecution.addData("CreateContext", cycle, data.executionTimesSums[9], notify);
 			timeExecution.addData("UpdateStatitics", cycle, data.executionTimesSums[10], notify);
+			timeExecution.addData("ChildContext", cycle, data.executionTimesSums[11], notify);
+			timeExecution.addData("PotentialRequest", cycle, data.executionTimesSums[12], notify);
 			
 			criticalities.addData("Prediction", cycle, data.evolutionCriticalityPrediction, notify);
 			criticalities.addData("Mapping", cycle, data.evolutionCriticalityMapping, notify);
@@ -172,7 +182,7 @@ public class AMOEBA extends Amas<World> implements IAMOEBA {
 		}
 		
 		if (isRenderUpdate()) {
-			AmoebaWindow.instance().mainVUI.updateCanvas();
+			((AmoebaMultiUIWindow)amasMultiUIWindow).mainVUI.updateCanvas();
 			updateAgentsVisualisation();
 			RunLaterHelper.runLater(() -> {resetCycleWithoutRender();});
 		}
@@ -467,7 +477,7 @@ public class AMOEBA extends Amas<World> implements IAMOEBA {
 	 */
 	public void allowGraphicalScheduler(boolean allow) {
 		if (!Configuration.commandLineMode) {
-			AmoebaWindow.instance().schedulerToolbar.setDisable(!allow);
+			((AmoebaMultiUIWindow)amasMultiUIWindow).schedulerToolbar.setDisable(!allow);
 		}
 	}
 
@@ -489,7 +499,7 @@ public class AMOEBA extends Amas<World> implements IAMOEBA {
 		super.addPendingAgents();
 		nextCycleRunAllAgents();
 		if(!Configuration.commandLineMode) {
-			AmoebaWindow.instance().dimensionSelector.update(getPercepts());
+			((AmoebaMultiUIWindow)amasMultiUIWindow).dimensionSelector.update(getPercepts());
 			updateAgentsVisualisation();
 		}
 	}
@@ -525,7 +535,7 @@ public class AMOEBA extends Amas<World> implements IAMOEBA {
 	public void setRenderUpdate(boolean renderUpdate) {
 		if (!Configuration.commandLineMode) {
 			this.renderUpdate = renderUpdate;
-			AmoebaWindow.instance().toggleRender.setSelected(renderUpdate);
+			((AmoebaMultiUIWindow)amasMultiUIWindow).toggleRender.setSelected(renderUpdate);
 			if(renderUpdate == true)
 				nextCycleRunAllAgents();
 		}
@@ -622,13 +632,13 @@ public class AMOEBA extends Amas<World> implements IAMOEBA {
 		for(Agent<? extends Amas<World>, World> a : getAgents()) {
 			a.onUpdateRender();
 		}
-		AmoebaWindow.instance().point.move(AmoebaWindow.instance().dimensionSelector.d1().getValue(), AmoebaWindow.instance().dimensionSelector.d2().getValue());
-		AmoebaWindow.instance().rectangle.setHeight(2*getEnvironment().getContextCreationNeighborhood(null, AmoebaWindow.instance().dimensionSelector.d2()));
-		AmoebaWindow.instance().rectangle.setWidth(2*getEnvironment().getContextCreationNeighborhood(null, AmoebaWindow.instance().dimensionSelector.d1()));
-		AmoebaWindow.instance().rectangle.move(AmoebaWindow.instance().dimensionSelector.d1().getValue() - getEnvironment().getContextCreationNeighborhood(null, AmoebaWindow.instance().dimensionSelector.d1()), AmoebaWindow.instance().dimensionSelector.d2().getValue() - getEnvironment().getContextCreationNeighborhood(null, AmoebaWindow.instance().dimensionSelector.d2()));
-		AmoebaWindow.instance().mainVUI.updateCanvas();
-		AmoebaWindow.instance().point.toFront();
-		AmoebaWindow.instance().point.setInfo(getCursorInfo());
+		((AmoebaMultiUIWindow)amasMultiUIWindow).point.move(((AmoebaMultiUIWindow)amasMultiUIWindow).dimensionSelector.d1().getValue(), ((AmoebaMultiUIWindow)amasMultiUIWindow).dimensionSelector.d2().getValue());
+		((AmoebaMultiUIWindow)amasMultiUIWindow).rectangle.setHeight(2*getEnvironment().getContextCreationNeighborhood(null, ((AmoebaMultiUIWindow)amasMultiUIWindow).dimensionSelector.d2()));
+		((AmoebaMultiUIWindow)amasMultiUIWindow).rectangle.setWidth(2*getEnvironment().getContextCreationNeighborhood(null, ((AmoebaMultiUIWindow)amasMultiUIWindow).dimensionSelector.d1()));
+		((AmoebaMultiUIWindow)amasMultiUIWindow).rectangle.move(((AmoebaMultiUIWindow)amasMultiUIWindow).dimensionSelector.d1().getValue() - getEnvironment().getContextCreationNeighborhood(null, ((AmoebaMultiUIWindow)amasMultiUIWindow).dimensionSelector.d1()), ((AmoebaMultiUIWindow)amasMultiUIWindow).dimensionSelector.d2().getValue() - getEnvironment().getContextCreationNeighborhood(null, ((AmoebaMultiUIWindow)amasMultiUIWindow).dimensionSelector.d2()));
+		((AmoebaMultiUIWindow)amasMultiUIWindow).mainVUI.updateCanvas();
+		((AmoebaMultiUIWindow)amasMultiUIWindow).point.toFront();
+		((AmoebaMultiUIWindow)amasMultiUIWindow).point.setInfo(getCursorInfo());
 	}
 	
 	/**
@@ -636,7 +646,7 @@ public class AMOEBA extends Amas<World> implements IAMOEBA {
 	 * @return
 	 */
 	public DimensionSelector getDimensionSelector() {
-		return AmoebaWindow.instance().dimensionSelector;
+		return ((AmoebaMultiUIWindow)amasMultiUIWindow).dimensionSelector;
 	}
 	
 	/**
diff --git a/AMOEBAonAMAK/src/kernel/AmoebaData.java b/AMOEBAonAMAK/src/kernel/AmoebaData.java
index 56c0503461b1498a9f9cf26e941897129c2e58e8..35866c4d4f1b7b16c9e8e158dc82d13defa3609c 100644
--- a/AMOEBAonAMAK/src/kernel/AmoebaData.java
+++ b/AMOEBAonAMAK/src/kernel/AmoebaData.java
@@ -79,4 +79,6 @@ public class AmoebaData implements Serializable {
 	public double[] executionTimesSums = new double[20];
 	
 	public double initRegressionPerformance = 1.0;
+	
+	public double averageRegressionPerformanceIndicator;
 }
\ No newline at end of file
diff --git a/AMOEBAonAMAK/src/kernel/backup/SaveHelperImpl.java b/AMOEBAonAMAK/src/kernel/backup/SaveHelperImpl.java
index b2258c51d799c99816a3b9d049b84901f5374c1f..152a7433223b7797e72c658192964cb5cbf59d3e 100644
--- a/AMOEBAonAMAK/src/kernel/backup/SaveHelperImpl.java
+++ b/AMOEBAonAMAK/src/kernel/backup/SaveHelperImpl.java
@@ -11,6 +11,7 @@ import java.util.List;
 
 import fr.irit.smac.amak.Configuration;
 import fr.irit.smac.amak.ui.MainWindow;
+import gui.AmoebaMultiUIWindow;
 import gui.AmoebaWindow;
 import gui.saveExplorer.SaveExplorer;
 import javafx.event.ActionEvent;
@@ -33,6 +34,9 @@ public class SaveHelperImpl implements ISaveHelper{
 	public static final String autosaveDirName = "autosave";
 	public static final String manualsaveDirName = "manual";
 
+	
+	public AmoebaMultiUIWindow amoebaMultiUIWindow;
+	
 	/**
 	 * The backup system used by the SaveHelper.
 	 */
@@ -116,6 +120,51 @@ public class SaveHelperImpl implements ISaveHelper{
 			setupGraphicalTool();
 		}
 	}
+	
+	public SaveHelperImpl(AMOEBA amoeba, AmoebaMultiUIWindow window) {
+		amoebaMultiUIWindow = window;
+		autoSave = !Configuration.commandLineMode;
+		this.amoeba = amoeba;
+		backupSystem = new BackupSystem(amoeba);
+		String dirName = amoeba.toString() + "_" + System.currentTimeMillis();
+		dir = Paths.get(savesRoot, dirName);
+		if (autoSave) {
+			dirAuto = Paths.get(dir.toString(), autosaveDirName);
+			try {
+				Files.createDirectories(dirAuto);
+			} catch (IOException e) {
+				e.printStackTrace();
+				System.err.println("Cannot create auto save directory. Auto saving is disabled.");
+				dirAuto = null;
+				autoSave = false;
+			}
+		}
+		dirManual = Paths.get(dir.toString(), manualsaveDirName);
+		try {
+			Files.createDirectories(dirManual);
+		} catch (IOException e) {
+			e.printStackTrace();
+			System.err.println("Cannot create manual save directory.");
+			dirManual = null;
+		}
+
+		// add graphical element if relevant
+		if (AmoebaWindow.isInstance()) {
+			SaveExplorer se = new SaveExplorer(amoeba);
+			AmoebaWindow.addTabbedPanel("Save Explorer", se);
+			AmoebaWindow.addOnCloseAction(()-> {
+				if(deleteFolderOnClose) {
+					try {
+						DeleteDirectory.deleteDirectoryRecursion(dir);
+					} catch (IOException e) {
+						e.printStackTrace();
+						System.err.println("Failed to delete saves files on close.");
+					}
+				}
+			});
+			setupGraphicalTool();
+		}
+	}
 
 	@Override
 	public void load(String path) {
@@ -187,7 +236,7 @@ public class SaveHelperImpl implements ISaveHelper{
 	 * Add save/load options in the main window.
 	 */
 	private void setupGraphicalTool() {
-		MainWindow mw = AmoebaWindow.instance();
+		AmoebaMultiUIWindow mw = amoebaMultiUIWindow;
 		// TODO remove if they exist items Save and Load in menu Option.
 		FileChooser fileChooser = new FileChooser();
 		fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("XML", "*.xml"),
@@ -198,7 +247,7 @@ public class SaveHelperImpl implements ISaveHelper{
 			@Override
 			public void handle(ActionEvent event) {
 				amoeba.getScheduler().stop();
-				File file = fileChooser.showOpenDialog(mw.stage);
+				File file = fileChooser.showOpenDialog(mw);
 				if (file != null)
 					backupSystem.load(file);
 			}
@@ -210,7 +259,7 @@ public class SaveHelperImpl implements ISaveHelper{
 			@Override
 			public void handle(ActionEvent event) {
 				amoeba.getScheduler().stop();
-				File file = fileChooser.showSaveDialog(mw.stage);
+				File file = fileChooser.showSaveDialog(mw);
 				if (file != null)
 					backupSystem.save(file);
 			}
diff --git a/AMOEBAonAMAK/src/ros/Main.java b/AMOEBAonAMAK/src/ros/Main.java
index e5d502c6c7e510dba9e4612e0145f5fc8a0561b6..b0a594150f3710bc932385dcdff4813a7769b2f8 100644
--- a/AMOEBAonAMAK/src/ros/Main.java
+++ b/AMOEBAonAMAK/src/ros/Main.java
@@ -30,7 +30,7 @@ public class Main {
 			}
 		}
 		
-		AMOEBA amoeba = new AMOEBA(config, null);
+		AMOEBA amoeba = new AMOEBA(null,null,config, null);
 		amoeba.allowGraphicalScheduler(false);
 
 		RosBridge bridge = new RosBridge();
diff --git a/README.md b/README.md
index fca184d21948de172da6065b449abe7e4266e65a..f53d24a2fa7562aece206214c5727c58fbe7b41b 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,15 @@
 # AMAKFX and AMOEBAonAMAK 
 
-Tested with openjdk 11.
+Tested with OpenJDK 11, should work with Java 8.
 
-## Build Jar with dependencies
+Check [the documentation directory](documentation) for more detailed and in depth instruction and explanation.
+
+Check AMAKFX's [README](AMAKFX/README.md) for more detail on how to use it.
+
+# Quick start :
+## Maven build Jar with dependencies
 `mvn clean compile assembly:single`
-Note that the resulting jars are not platform independant. 
+Note that the resulting jars are not platform independent. 
 
 ## Use with Eclipse
 Clone this repo and import it as a Maven project.
@@ -16,5 +21,13 @@ AMOEBA-parent
   | AMAKFX
   | AMOEBAonAMAK
 ```
-For more detail on [AMOEBAonAMAK](AMOEBAonAMAK/README.md) or [AMAKFX](AMAKFX/README.md), check their respective README.
 
+Check AMAKFX's [README](AMAKFX/README.md) for more detail on how to use it.
+
+Check [the documentation directory](documentation) for more detailed and in depth instruction and explanation on AMOEBA.
+
+### Amoeba Quick start
+```Java
+StudiedSystem studiedSystem = new F_XY_System(50.0);
+AMOEBA amoeba = new AMOEBA("resources/twoDimensionsLauncher.xml", studiedSystem);
+```
\ No newline at end of file
diff --git a/documentation/GUI_description.pdf b/documentation/GUI_description.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..0b3f72f7104237c4e91c88d76ca29f9c1dd0d728
Binary files /dev/null and b/documentation/GUI_description.pdf differ
diff --git a/documentation/GUI_description_1.jpg b/documentation/GUI_description_1.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..f43f90287ae52c1053b5f30c916b2d9a3b172320
Binary files /dev/null and b/documentation/GUI_description_1.jpg differ
diff --git a/documentation/GUI_description_2.jpg b/documentation/GUI_description_2.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..69d573416f07c978cff3173c1e19a8d136f57ede
Binary files /dev/null and b/documentation/GUI_description_2.jpg differ
diff --git a/documentation/gui.md b/documentation/gui.md
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9e7084ec56fbd2f58616f3fc2f2d229391434175 100644
--- a/documentation/gui.md
+++ b/documentation/gui.md
@@ -0,0 +1,5 @@
+# GUI description
+[Hi-res pdf](GUI_description.pdf)
+
+![Main VUI](GUI_description_1.jpg)
+![Save Explorer](GUI_description_2.jpg)
\ No newline at end of file
diff --git a/documentation/py4j.md b/documentation/py4j.md
new file mode 100644
index 0000000000000000000000000000000000000000..19bcf82b9b08de5529e3ee4f2f86fd0814728dd4
--- /dev/null
+++ b/documentation/py4j.md
@@ -0,0 +1,11 @@
+# Using Py4j
+[Py4j](https://www.py4j.org/) is a Python/Java tool allowing control java code with python.
+
+Demos are available in the [py4j_demo](py4j_demo) directory.
+
+A minimalistic main is provided at `py4j.Main.java`. Please note that py4j use socket, so only one process of that main can be executed at a given time. You can have multiple amoeba on a same process, but only one GUI on the same process. If you need to launch multiple amoebas with GUI at a same time, check py4j official documentation on how to change sockets.
+
+To compile AMOEBA as an executable jar with this main, do :
+```
+mvn clean compile assembly:single -Dmain.class=py4j.Main
+```
\ No newline at end of file
diff --git a/documentation/py4j_demo/README.md b/documentation/py4j_demo/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..65c3fe0dcd1f70cd60f98fb59a715da0c932044d
--- /dev/null
+++ b/documentation/py4j_demo/README.md
@@ -0,0 +1,32 @@
+Official py4j documentation [here](https://www.py4j.org/).
+
+# Install :
+Install python dependencies :
+```
+pip3 install requirements.txt
+```
+Optional : check [openai gym](https://gym.openai.com/) documentation for a full install.
+
+Run the setup script [setup.sh](setup.sh) (linux only). It just clean some file, compile the correct AMOEBAonAMAK jar and copy it for ease of use. 
+
+You're good to go, check that py4j is correctly working with [basic_demo.py](basic_demo.py) :
+```
+python3 basic_demo.py
+```
+You should get the classic amoeba's GUI, and random context appearing.
+
+Then you can look at some actual learning, inside [learn_gym.py](learn_gym.py).
+
+
+# Optional : Ros2Learn
+[Ros2Learn](https://github.com/AcutronicRobotics/ros2learn) provide some tools for machine learning on robots, using Ros2, openAI gym, and gazebo. 
+
+Install Ros2 and Ros2learn, follow Ros2Learn instruction. Make sure your version of gazebo is at least 9.9.
+
+In your python script, import gym_gazebo2. Before running your python code make sure that you properly loaded Ros2 and Ros2learn environment using their provision scripts.
+
+You can now use gym environment provided by Ros2learn as regular gym environment.
+```Python
+env = gym.make('MARA-v0')
+```
+
diff --git a/documentation/py4j_demo/learn_gym.py b/documentation/py4j_demo/learn_gym.py
index d21dcae6757358d86309c8bf21068a9c7cca1b70..ffc206c3186b3bcdc945bb21e877a2d40d82e764 100644
--- a/documentation/py4j_demo/learn_gym.py
+++ b/documentation/py4j_demo/learn_gym.py
@@ -1,6 +1,6 @@
 import os
 import gym
-import gym_gazebo2
+#import gym_gazebo2
 import subprocess
 import time
 import math
diff --git a/documentation/save_system.md b/documentation/save_system.md
new file mode 100644
index 0000000000000000000000000000000000000000..4219c489f00ceb30e162ed33cdf81f37300279d7
--- /dev/null
+++ b/documentation/save_system.md
@@ -0,0 +1,54 @@
+#The Save System
+The save system is composed of two main elements, and some GUI elements :
+
+## The Backup System :
+Found in `kernel.backup`.
+
+A backup system is an object responsible for reading/writing the state of amoeba from/to a file. It implement the interface `IBackupSystem`. The implementation provided is `BackupSystem` and use xml to store data.
+
+Usage :
+```Java
+AMOEBA amoeba = new AMOEBA()
+IBackupSystem backupSystem = new BackupSystem(amoeba);
+backupSystem.load(new File("path/to/a/save.xml"));
+// do some learning with the amoeba ...
+backupSystem.save(new File("path/to/a/new/file.xml"))
+```
+
+## The Save Helper :
+Found in `kernel.backup`.
+
+A save helper is an object that provide additional functionality over a BackupSystem for the user or other components. Most importantly it :
+- Create a temporary directory for saves
+- Allow automatic saving during amoeba execution
+- Add the options to save/load on the GUI
+- Add functionality used by the SaveExplorer.
+
+See `ISaveHelper` for more.
+
+Two implementation are provided : the fully functioning `SaveHelperImpl` and `SaveHelperDummy` that do nothing. The dummy is used to deactivate ALL automatic saving, giving a HUGE performance boost.
+
+Each amoeba has a save helper, available with `amoeba.saver`
+```Java
+AMOEBA amoeba = new AMOEBA();
+// This constructor initialize the amoeba's saver with a dummy, meaning :
+amoeba.saver.save("this_does_not_save");
+// will do nothing
+
+// or ...
+AMOEBA amoeba = new AMOEBA("path/to/save.xml", null);
+// This constructor create and use a SaveHelperImpl to initilize the amoeba
+amoeba.saver.save("a/valid/path/save.xml");
+// will create a save, and
+amoeba.saver.autosave();
+// will create a save in the temporary directory creates by the save helper, located in 
+System.out.println(SaveHelperImpl.dir)
+```
+If amoeba crash, the save files will not be automatically deleted, you may have to manually clean the save directory.  
+
+## The Save Explorer
+Found in `gui.saveExplorer`.
+
+The save explorer read saves from an amoeba's SaveHelper and offer some graphical tool, most importantly the ability to preview a save and quickly cycle trough saves. Allowing to visually play back the execution of an amoeba.
+
+See the [GUI description](gui.md) for more detail on the GUI.
diff --git a/documentation/usage.md b/documentation/usage.md
index 352759c9a5788a13213e84dabdc1dcac627f1b07..b7235148d0d75d1ce59ae9429eb6fd0dcb84c012 100644
--- a/documentation/usage.md
+++ b/documentation/usage.md
@@ -16,7 +16,7 @@ Depending on your problem, determine what AMOEBA should learn, and from what. Bu
 A config file is a xml file used to initialize your amoeba. Most importantly it contain the list of percepts.
 
 You can use `utils/genFiles.py` to generate your xml file based on a list of percept. `python genFiles.py MySystem false px py` will create `MySystem.xml` (and `MySystem.msg`, but ignore it if you don't use [AMOEBA and ROS](rosbridge.md)) containing something looking like the following example :
-
+(if you don't want to use python, a java class doing the same job is available at `utils.XmlConfigGenerator`)
 ```xml
 <?xml version="1.0" encoding="UTF-8"?>
 <System>
@@ -144,6 +144,6 @@ public static void main(String[] args) {
     }
 }
 ```
-We can ask amoeba how well it performed on the last prediction with `amoeba.getHeads().get(0).getCriticity()`.
+We can ask amoeba how well it performed on the last prediction with `amoeba.getHeadAgent().getCriticity()`.
 
 For more example on how to use amoeba, check `AdvancedMain.java`, `MinimalMain.java`, and `Main.java` in the `experiments` package.
\ No newline at end of file