<commit_before>package dynamake.models;

import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;

import dynamake.caching.Memoizer1;
import dynamake.commands.Command;
import dynamake.commands.CommandFactory;
import dynamake.commands.CommandState;
import dynamake.commands.ForwardableCommand;
import dynamake.commands.ForwardableOutput;
import dynamake.commands.PendingCommandState;
import dynamake.commands.RelativeCommand;
import dynamake.commands.RewrapCommand;
import dynamake.commands.SetPropertyCommand;
import dynamake.commands.UnwrapCommand;
import dynamake.delegates.Func1;
import dynamake.delegates.Runner;
import dynamake.menubuilders.CompositeMenuBuilder;
import dynamake.models.LiveModel.LivePanel;
import dynamake.models.factories.ModelCreation;
import dynamake.models.factories.ModelFactory;
import dynamake.numbers.Fraction;
import dynamake.numbers.RectangleF;
import dynamake.transcription.PendingCommandFactory;
import dynamake.transcription.Execution;
import dynamake.transcription.HistoryHandler;
import dynamake.transcription.Collector;
import dynamake.transcription.LocalHistoryHandler;
import dynamake.transcription.SimplePendingCommandFactory;
import dynamake.transcription.Trigger;

public class CanvasModel extends Model {
	private static class Entry implements Serializable {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		public final Location id;
		public final Model model;
		
		public Entry(Location id, Model model) {
			this.id = id;
			this.model = model;
		}
	}
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private int nextId;
	private ArrayList<Entry> models;
	
	public CanvasModel() {
		models = new ArrayList<Entry>();
	}
	
	public CanvasModel(ArrayList<Entry> models) {
		this.models = models;
	}
	
	public static class AddedModelChange {
		public final int index;
		public final Model model;
		
		public AddedModelChange(int index, Model model) {
			this.index = index;
			this.model = model;
		}
	}
	
	@Override
	public RestorableModel toRestorable(boolean includeLocalHistory) {
		return CanvasRestorableModel.wrap(this, includeLocalHistory);
	}
	
	@Override
	protected void modelScale(Fraction hChange, Fraction vChange, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		for(Entry entry: models)
			entry.model.scale(hChange, vChange, propCtx, propDistance, collector);
	}
	
	@Override
	public void destroy(PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		// Destroy inner models
		for(Entry entry: models)
			entry.model.destroy(propCtx, propDistance, collector);
		
		// Then destroy self
		super.destroy(propCtx, propDistance, collector);
	}

	public int getModelCount() {
		return models.size();
	}
	
	@Override
	public Model cloneBase() {
		return new CanvasModel();
	}
	
	public static class RemovedModelChange {
		public final int index;
		public final Model model;
		
		public RemovedModelChange(int index, Model model) {
			this.index = index;
			this.model = model;
		}
	}
	
	public static class MoveModelCommand implements Command<Model> {
		public static class Output implements Serializable {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;
			public final Location canvasSourceLocation;
			public final Location canvasTargetLocation;
			public final Location movedFromInSource;
			public final Location movedToInTarget;
			
			public Output(Location canvasSourceLocation, Location canvasTargetLocation, Location movedFromInSource, Location movedToInTarget) {
				this.canvasSourceLocation = canvasSourceLocation;
				this.canvasTargetLocation = canvasTargetLocation;
				this.movedFromInSource = movedFromInSource;
				this.movedToInTarget = movedToInTarget;
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		
		private Location canvasSourceLocation;
		private Location canvasTargetLocation;
		private Location locationInSource;

		public MoveModelCommand(Location canvasSourceLocation, Location canvasTargetLocation, Location locationInSource) {
			this.canvasSourceLocation = canvasSourceLocation;
			this.canvasTargetLocation = canvasTargetLocation;
			this.locationInSource = locationInSource;
		}

		@Override
		public Object executeOn(PropogationContext propCtx, Model prevalentSystem, Collector<Model> collector, Location location) {
			CanvasModel canvasSource = (CanvasModel)CompositeLocation.getChild(prevalentSystem, location, canvasSourceLocation);
			CanvasModel canvasTarget = (CanvasModel)CompositeLocation.getChild(prevalentSystem, location, canvasTargetLocation);
			Model model = (Model)canvasSource.getModelByLocation(locationInSource);

			canvasSource.removeModelByLocation(locationInSource, propCtx, 0, collector);
			canvasTarget.addModel(model, propCtx, 0, collector);
			Location locationInTarget = canvasTarget.getLocationOf(model);
			
			return new Output(canvasSourceLocation, canvasTargetLocation, locationInSource, locationInTarget);
		}
	}
	
	public static class MoveBackModelCommand implements Command<Model> {
		public static class AfterMove implements CommandFactory<Model> {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			public Command<Model> createCommand(Object output) {
				MoveModelCommand.Output moveOutput = (MoveModelCommand.Output)output;
				return new MoveBackModelCommand(moveOutput.canvasTargetLocation, moveOutput.canvasSourceLocation, moveOutput.movedToInTarget, moveOutput.movedFromInSource);
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		
		private Location canvasSourceLocation;
		private Location canvasTargetLocation;
		private Location locationInSource;
		private Location locationInTarget;

		public MoveBackModelCommand(Location canvasSourceLocation, Location canvasTargetLocation, Location locationInSource, Location locationInTarget) {
			this.canvasSourceLocation = canvasSourceLocation;
			this.canvasTargetLocation = canvasTargetLocation;
			this.locationInSource = locationInSource;
			this.locationInTarget = locationInTarget;
		}

		@Override
		public Object executeOn(PropogationContext propCtx, Model prevalentSystem, Collector<Model> collector, Location location) {
			CanvasModel canvasSource = (CanvasModel)CompositeLocation.getChild(prevalentSystem, location, canvasSourceLocation);
			CanvasModel canvasTarget = (CanvasModel)CompositeLocation.getChild(prevalentSystem, location, canvasTargetLocation);
			Model model = (Model)canvasSource.getModelByLocation(locationInSource);

			canvasSource.removeModelByLocation(locationInSource, propCtx, 0, collector);
			canvasTarget.restoreModelByLocation(locationInTarget, model, propCtx, 0, collector);
			
			return new MoveModelCommand.Output(canvasSourceLocation, canvasTargetLocation, locationInSource, locationInTarget);
		}
	}
	
	public static class AddModelCommand implements ForwardableCommand<Model> {
		public static class Output implements Serializable, ForwardableOutput {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;
			public final Location location;

			public Output(Location location) {
				this.location = location;
			}

			@Override
			public Object forForwarding() {
				return new AddModelCommand.Output(location.forForwarding());
			}
			
			@Override
			public String toString() {
				return "Added to " + location;
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		public final ModelFactory factory;
		
		public AddModelCommand(ModelFactory factory) {
			this.factory = factory;
		}
		
		@Override
		public Object executeOn(final PropogationContext propCtx, final Model rootPrevalentSystem, Collector<Model> collector, final Location location) {
			/*
			TODO: Consider
			Model creation and add could be changed as follows:
			Instead of during everything here in this method, the create and add procedure could leveraged by using the collector:
			- First, create the basic model and add this model to a special area of the canvas (something like: "model embryos")
			- Second, request the factory to do any additional creation; this creation must involve executing on the collector
			- Third, execute on the collector that the registered model embryo is to be added
			*/
			
			final CanvasModel canvas = (CanvasModel)location.getChild(rootPrevalentSystem);
			final ModelCreation modelCreation = factory.create(rootPrevalentSystem, propCtx, 0, collector, location);
			final Model model = modelCreation.createModel(rootPrevalentSystem, propCtx, 0, collector, location);
			
			canvas.addModel(model, new PropogationContext(), 0, collector);
			final Location addedModelLocation = canvas.getLocationOf(model);
//			System.out.println("***Adding model " + model + " at " + addedModelLocation + " in " + canvas + "***");
			
			collector.execute(new Trigger<Model>() {
				@Override
				public void run(Collector<Model> collector) {
					modelCreation.setup(rootPrevalentSystem, model, addedModelLocation, propCtx, 0, collector, location);
				}
			});
			
			return new Output(addedModelLocation);
		}
		
		@Override
		public Command<Model> forForwarding(Object output) {
			// When a model is added to a canvas, map id to ForwardedId (if not only already ForwardedId)
			// When a model is removed from a canvas, map id to ForwardedId (if not only already ForwardedId)
			AddModelCommand.Output addModelOutput = (AddModelCommand.Output)output;
			
			Location forwardedLocation = addModelOutput.location.forForwarding();
			ModelFactory newFactory = this.factory.forForwarding();
			CanvasModel.ForwardedAddModelCommand newAddCommand = new CanvasModel.ForwardedAddModelCommand(forwardedLocation, newFactory);

			return newAddCommand;
		}
		
		@Override
		public String toString() {
			return "Add " + factory;
		}
	}
	
	public static class ForwardedAddModelCommand implements ForwardableCommand<Model> {
		public static class Output implements Serializable {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;
			public final Location location;

			public Output(Location location) {
				this.location = location;
			}
			
			@Override
			public String toString() {
				return "Added model at " + location;
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		public final Location modelLocation;
		public final ModelFactory factory;
		
		public ForwardedAddModelCommand(Location modelLocation, ModelFactory factory) {
			this.modelLocation = modelLocation;
			this.factory = factory;
		}
		
		@Override
		public Object executeOn(PropogationContext propCtx, Model rootPrevalentSystem, Collector<Model> collector, Location location) {
			final CanvasModel canvas = (CanvasModel)location.getChild(rootPrevalentSystem);
			ModelCreation modelCreation = factory.create(rootPrevalentSystem, propCtx, 0, collector, location);
			final Model model = modelCreation.createModel(rootPrevalentSystem, propCtx, 0, collector, location);
			
			canvas.restoreModelByLocation(modelLocation, model, new PropogationContext(), 0, collector);
			Location addedModelLocation = canvas.getLocationOf(model);
			
			modelCreation.setup(rootPrevalentSystem, model, addedModelLocation, propCtx, 0, collector, location);
			
			return new Output(addedModelLocation);
		}
		
		@Override
		public Command<Model> forForwarding(Object output) {
			// When a model is added to a canvas, map id to ForwardedId (if not only already ForwardedId)
			// When a model is removed from a canvas, map id to ForwardedId (if not only already ForwardedId)
			AddModelCommand.Output addModelOutput = (AddModelCommand.Output)output;
			
			Location mappedLocation = addModelOutput.location.forForwarding();
			CanvasModel.ForwardedAddModelCommand newAddCommand = new CanvasModel.ForwardedAddModelCommand(mappedLocation, this.factory);

			return newAddCommand;
		}
	}
	
	// Should be MappableCommand due to RestorableModel?
	public static class RestoreModelCommand implements Command<Model>, Cloneable {
		public static final class AfterRemove implements CommandFactory<Model>  
		{
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			public Command<Model> createCommand(Object output) {
				RestorableModel restorableModel = ((RemoveModelCommand.Output)output).restorableModel;
				Location location = ((RemoveModelCommand.Output)output).location;
				// TODO: Consider the following:
				// What if the model what observing/being observed before its removal?
				// What if the model's observers/observees aren't all in existence anymore?
				// What if the model's observers/observees are restored after this model is restored?
				// Are all of the above cases possible?
				// Perhaps, the best solution would be to save the history and replay this history?
				
				return new CanvasModel.RestoreModelCommand(location, restorableModel);
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private Location modelLocationToRestore;
		private RestorableModel restorableModel;
		
		public RestoreModelCommand(Location modelLocationToRestore, RestorableModel restorableModel) {
			this.modelLocationToRestore = modelLocationToRestore;
			this.restorableModel = restorableModel;
		}
		
		@Override
		public Object executeOn(PropogationContext propCtx, Model prevalentSystem, Collector<Model> collector, Location location) {
			CanvasModel canvas = (CanvasModel)location.getChild(prevalentSystem);
			
			Model modelBase = restorableModel.unwrapBase(propCtx, 0, collector);
			System.out.println("***Restoring model " + modelBase + " at " + modelLocationToRestore + " from " + canvas + "***");
			restorableModel.restoreOriginsOnBase(modelBase, propCtx, 0, collector);
			
			canvas.restoreModelByLocation(modelLocationToRestore, modelBase, new PropogationContext(), 0, collector);
			
			restorableModel.restoreChangesOnBase(modelBase, propCtx, 0, collector);
			
			return new AddModelCommand.Output(modelLocationToRestore);
		}
	}
	
	public static class DestroyModelCommand implements ForwardableCommand<Model> {
		public static class Output implements Serializable, ForwardableOutput {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;
			public final Location location;

			public Output(Location location) {
				this.location = location;
			}
			
			@Override
			public Object forForwarding() {
				return new DestroyModelCommand.Output(location.forForwarding());
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private Location locationOfModelToDestroy;
		
		public DestroyModelCommand(Location locationOfModelToDestroy) {
			this.locationOfModelToDestroy = locationOfModelToDestroy;
		}

		@Override
		public Object executeOn(PropogationContext propCtx, Model prevalentSystem, Collector<Model> collector, Location location) {
			CanvasModel canvas = (CanvasModel)location.getChild(prevalentSystem);
			Model modelToDestroy = canvas.getModelByLocation(locationOfModelToDestroy);
			System.out.println("***Destroying model " + modelToDestroy + " at " + locationOfModelToDestroy + " from " + canvas + "***");
			
			modelToDestroy.destroy(propCtx, 0, collector);

			return new Output(locationOfModelToDestroy);
		}
		
		@Override
		public Command<Model> forForwarding(Object output) {
			DestroyModelCommand.Output destroyModelOutput = (DestroyModelCommand.Output)output;
			
			Location mappedLocation = destroyModelOutput.location.forForwarding();
			CanvasModel.DestroyModelCommand newDestroyCommand = new CanvasModel.DestroyModelCommand(mappedLocation);

			return newDestroyCommand;
		}
	}
	
	public static class RemoveModelCommand implements ForwardableCommand<Model> {
		public static class Output implements Serializable, ForwardableOutput {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;
			public final Location location;
			public final RestorableModel restorableModel;

			public Output(Location location, RestorableModel restorableModel) {
				this.location = location;
				this.restorableModel = restorableModel;
			}
			
			@Override
			public Object forForwarding() {
				return new RemoveModelCommand.Output(location.forForwarding(), restorableModel);
			}
		}
		
		public static final class AfterAdd implements /*Forwardable*/CommandFactory<Model>  
		{
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			public Command<Model> createCommand(Object output) {
				return new CanvasModel.RemoveModelCommand(((CanvasModel.AddModelCommand.Output)output).location);
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private Location locationOfModelToRemove;
		
		public RemoveModelCommand(Location locationOfModelToRemove) {
			this.locationOfModelToRemove = locationOfModelToRemove;
		}
		
		@Override
		public Object executeOn(final PropogationContext propCtx, Model prevalentSystem, Collector<Model> collector, Location location) {
			final CanvasModel canvas = (CanvasModel)location.getChild(prevalentSystem);
			Model modelToRemove = canvas.getModelByLocation(locationOfModelToRemove);
			System.out.println("***Removing model " + modelToRemove + " at " + locationOfModelToRemove + " from " + canvas + "***");
			
			RestorableModel restorableModel = modelToRemove.toRestorable(true);

			canvas.removeModelByLocation(locationOfModelToRemove, propCtx, 0, collector);
			
			return new Output(locationOfModelToRemove, restorableModel);
		}
		
		@Override
		public Command<Model> forForwarding(Object output) {
			// When a model is added to a canvas, map id to ForwardedId (if not only already ForwardedId)
			// When a model is removed from a canvas, map id to ForwardedId (if not only already ForwardedId)
			RemoveModelCommand.Output removeModelOutput = (RemoveModelCommand.Output)output;
			
			Location mappedLocation = removeModelOutput.location.forForwarding();
			CanvasModel.RemoveModelCommand newRemoveCommand = new CanvasModel.RemoveModelCommand(mappedLocation);

			return newRemoveCommand;
		}
	}
	
	public void restoreModelByLocation(Location id, Model model, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		int index = models.size();
		models.add(index, new Entry(id, model));
		model.setParent(this);
		sendChanged(new AddedModelChange(index, model), propCtx, propDistance, 0, collector);
	}

	public void addModel(Model model, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		addModel(models.size(), model, propCtx, propDistance, collector);
	}

	public Model getModelByIndex(int index) {
		return models.get(index).model;
	}
	
	/**
	 * location is assumed to be an id itself.
	 * 
	 * @param location
	 * @return
	 */
	public Model getModelByLocation(Location location) {
		for(Entry entry: models) {
			if(entry.id.equals(location))
				return entry.model;
		}
		return null;
	}

	public void addModel(int index, Model model, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		int id = nextId++;
		
		models.add(index, new Entry(new IdLocation(id), model));
		model.setParent(this);
		sendChanged(new AddedModelChange(index, model), propCtx, propDistance, 0, collector);
		
//		System.out.println("Add model with id " + id + " in canvas " + this);
	}
	
	public void removeModel(Model model, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		int indexOfModel = indexOfModel(model);
		removeModelByIndex(indexOfModel, propCtx, propDistance, collector);
	}
	
	public void removeModelByIndex(int index, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		Model model = models.get(index).model;
		models.remove(index);
		model.setParent(null);
		sendChanged(new RemovedModelChange(index, model), propCtx, propDistance, 0, collector);
	}

	public void removeModelByLocation(Location location, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		// TODO: Figure out: How to support ForwardLocations here?
		int indexOf = getIndexOfModelByLocation(location);
		removeModelByIndex(indexOf, propCtx, propDistance, collector);
		
//		System.out.println("Removed model with id " + ((IdLocation)location).id + " in canvas " + this);
	}
	
	public int indexOfModel(Model model) {
		for(int i = 0; i < models.size(); i++) {
			if(models.get(i).model == model)
				return i;
		}
		
		return -1;
	}

	private int getIndexOfModelByLocation(Location id) {
		for(int i = 0; i < models.size(); i++) {
			if(models.get(i).id.equals(id))
				return i;
		}
		
		return -1;
	}
	
	public Location getLocationOf(Model model) {
		int indexOfModel = indexOfModel(model);
		return models.get(indexOfModel).id;
	}

	public Location getNextLocation() {
		return new IdLocation(nextId);
	}
	
	private static class CanvasPanel extends JLayeredPane implements ModelComponent {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private CanvasModel model;
		private ModelTranscriber modelTranscriber;
		private HashSet<Model> shownModels = new HashSet<Model>();
		private Memoizer1<Model, Binding<ModelComponent>> modelToModelComponentMap;
		
		public CanvasPanel(final ModelComponent rootView, CanvasModel model, final ModelTranscriber modelTranscriber, final ViewManager viewManager) {
			this.model = model;
			this.modelTranscriber = modelTranscriber;
			setLayout(null);
			setBorder(BorderFactory.createLineBorder(Color.BLACK));
			setOpaque(true);
			
			modelToModelComponentMap = new Memoizer1<Model, Binding<ModelComponent>>(new Func1<Model, Binding<ModelComponent>>() {
				@Override
				public Binding<ModelComponent> call(Model model) {
					final Binding<ModelComponent> modelView = model.createView(rootView, viewManager, modelTranscriber.extend(new ItemLocator(CanvasPanel.this.model, model)));
					
					Rectangle bounds = new Rectangle(
						((Fraction)model.getProperty("X")).intValue(),
						((Fraction)model.getProperty("Y")).intValue(),
						((Fraction)model.getProperty("Width")).intValue(),
						((Fraction)model.getProperty("Height")).intValue()
					);
					
					((JComponent)modelView.getBindingTarget()).setBounds(bounds);
					
					return modelView;
				}
			});
		}

		@Override
		public Model getModelBehind() {
			return model;
		}
		
		@Override
		public void appendContainerTransactions(
				final LivePanel livePanel, CompositeMenuBuilder menuBuilder, final ModelComponent child) {
			menuBuilder.addMenuBuilder("Remove", new Trigger<Model>() {
				@Override
				public void run(Collector<Model> collector) {
					CanvasModel.appendRemoveTransaction(collector, livePanel, child, model);
				}
			});
		}

		@Override
		public void appendTransactions(ModelComponent livePanel, CompositeMenuBuilder menuBuilder) {
			Model.appendComponentPropertyChangeTransactions(livePanel, model, modelTranscriber, menuBuilder);
			// The canvas model can be unwrap only if all the following cases are true:
			// - It has one ore more models contained in itself
			// - Its parent is a canvas model; i.e. canvases can only be unwrapped into other canvases
			if(model.getModelCount() > 0 && ModelComponent.Util.getParent(this).getModelBehind() instanceof CanvasModel) {
				menuBuilder.addMenuBuilder("Unwrap", new Trigger<Model>() {
					@Override
					public void run(Collector<Model> collector) {
						ModelComponent parent = ModelComponent.Util.getParent(CanvasPanel.this);
						ArrayList<CommandState<Model>> pendingCommands = new ArrayList<CommandState<Model>>();
						CanvasModel.appendUnwrapTransaction(pendingCommands, CanvasPanel.this, parent);
						
						PendingCommandFactory.Util.sequence(collector, parent.getModelBehind(), pendingCommands, LocalHistoryHandler.class);
					}
				});
			}
		}
		@Override
		public void appendDroppedTransactions(ModelComponent livePanel, ModelComponent target, Rectangle droppedBounds, CompositeMenuBuilder menuBuilder) {
			Model.appendGeneralDroppedTransactions(livePanel, this, target, droppedBounds, menuBuilder);
		}
		
		@Override
		public void appendDropTargetTransactions(final ModelComponent livePanel,
				final ModelComponent dropped, final Rectangle droppedBounds, final Point dropPoint, CompositeMenuBuilder menuBuilder) {
			if(dropped.getModelTranscriber().getParent() != null && 
				dropped.getModelTranscriber().getParent() != CanvasPanel.this.modelTranscriber &&
				!isContainerOf(dropped.getModelTranscriber(), this.modelTranscriber) /*Dropee cannot be child of dropped*/) {
				menuBuilder.addMenuBuilder("Move", new Trigger<Model>() {
					@Override
					public void run(Collector<Model> collector) {
						final ModelComponent modelToMove = dropped;

						// Reference is closest common ancestor
						ModelComponent source = ModelComponent.Util.getParent(modelToMove);
						ModelComponent targetOver = CanvasPanel.this;
						ModelComponent referenceMC = ModelComponent.Util.closestCommonAncestor(source, targetOver);
						
						Location locationOfSource = ModelComponent.Util.locationFromAncestor(referenceMC, source);
						Location locationOfTarget = ModelComponent.Util.locationFromAncestor(referenceMC, targetOver);
						
						ArrayList<CommandState<Model>> pendingCommands = new ArrayList<CommandState<Model>>();
						
						CanvasModel.appendMoveTransaction(pendingCommands, (LivePanel)livePanel, source, modelToMove, targetOver, droppedBounds.getLocation(), locationOfSource, locationOfTarget);
						
						PendingCommandFactory.Util.sequence(collector, referenceMC.getModelBehind(), pendingCommands, LocalHistoryHandler.class);
					}
				});
			}
		}
		
		private boolean isContainerOf(ModelTranscriber container, ModelTranscriber item) {
			ModelTranscriber parent = item.getParent();
			if(parent != null) {
				if(parent == container)
					return true;
				return isContainerOf(container, parent);
			}
			return false;
		}

		@Override
		public ModelTranscriber getModelTranscriber() {
			return modelTranscriber;
		}
		
		@Override
		public void initialize() {

		}
	}
	
	public static void appendUnwrapTransaction(List<CommandState<Model>> commandStates, ModelComponent toUnwrap, ModelComponent parent) {
		CanvasModel target = (CanvasModel)parent.getModelBehind();
		CanvasModel modelToBeUnwrapped = (CanvasModel)toUnwrap.getModelBehind();
		Location wrapperLocationInTarget = target.getLocationOf(modelToBeUnwrapped);
		RectangleF creationBoundsInSelection = new RectangleF(
			(Fraction)modelToBeUnwrapped.getProperty("X"),
			(Fraction)modelToBeUnwrapped.getProperty("Y"),
			(Fraction)modelToBeUnwrapped.getProperty("Width"),
			(Fraction)modelToBeUnwrapped.getProperty("Height")
		);
		
		commandStates.add(new PendingCommandState<Model>(
			new UnwrapCommand(wrapperLocationInTarget, creationBoundsInSelection),
			new RewrapCommand.AfterUnwrap(),
			new UnwrapCommand.AfterWrap()
		));
	}
	
	public static void appendRemoveTransaction(List<CommandState<Model>> commandStates, LivePanel livePanel, ModelComponent child, CanvasModel model) {
		Location locationOfModel = model.getLocationOf(child.getModelBehind());
		
		commandStates.add(new PendingCommandState<Model>(
			new RemoveModelCommand(locationOfModel),
			new RestoreModelCommand.AfterRemove(),
			new RemoveModelCommand.AfterAdd()
		));
	}
	
	public static void appendRemoveTransaction(Collector<Model> collector, LivePanel livePanel, ModelComponent child, final CanvasModel model) {
		final Location locationOfModel = model.getLocationOf(child.getModelBehind());
		
		ArrayList<CommandState<Model>> pendingCommands = new ArrayList<CommandState<Model>>();
		
		pendingCommands.add(new PendingCommandState<Model>(
			new CanvasModel.DestroyModelCommand(locationOfModel),
			new Command.Null<Model>()
		));
		pendingCommands.add(new PendingCommandState<Model>(
			new RemoveModelCommand(locationOfModel),
			new RestoreModelCommand.AfterRemove(),
			new RemoveModelCommand.AfterAdd()
		));
		
		PendingCommandFactory.Util.sequence(collector, model, pendingCommands, LocalHistoryHandler.class);
	}
	
	public static void appendMoveTransaction(List<CommandState<Model>> commandStates, LivePanel livePanel, ModelComponent source, ModelComponent modelToMove, ModelComponent target, final Point moveLocation, Location canvasSourceLocation, Location canvasTargetLocation) {
		CanvasModel sourceCanvas = (CanvasModel)source.getModelBehind();
		Location locationInSource = sourceCanvas.getLocationOf(modelToMove.getModelBehind());
		
		Location canvasTargetLocationAfter = canvasTargetLocation;
		
		Location modelLocationAfterMove = new CompositeLocation(canvasTargetLocationAfter, ((CanvasModel)target.getModelBehind()).getNextLocation());
		
		commandStates.add(new PendingCommandState<Model>(
			new CanvasModel.MoveModelCommand(canvasSourceLocation, canvasTargetLocation, locationInSource), 
			new CanvasModel.MoveBackModelCommand.AfterMove(),
			new CanvasModel.MoveBackModelCommand.AfterMove()<commit_msg>Extended appendRemoveTransaction(List...) to add destroy command. Changed appendRemoveTransaction(Collector...) to invoke appendRemoveTransaction(List...).<commit_after>package dynamake.models;

import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;

import dynamake.caching.Memoizer1;
import dynamake.commands.Command;
import dynamake.commands.CommandFactory;
import dynamake.commands.CommandState;
import dynamake.commands.ForwardableCommand;
import dynamake.commands.ForwardableOutput;
import dynamake.commands.PendingCommandState;
import dynamake.commands.RelativeCommand;
import dynamake.commands.RewrapCommand;
import dynamake.commands.SetPropertyCommand;
import dynamake.commands.UnwrapCommand;
import dynamake.delegates.Func1;
import dynamake.delegates.Runner;
import dynamake.menubuilders.CompositeMenuBuilder;
import dynamake.models.LiveModel.LivePanel;
import dynamake.models.factories.ModelCreation;
import dynamake.models.factories.ModelFactory;
import dynamake.numbers.Fraction;
import dynamake.numbers.RectangleF;
import dynamake.transcription.PendingCommandFactory;
import dynamake.transcription.Execution;
import dynamake.transcription.HistoryHandler;
import dynamake.transcription.Collector;
import dynamake.transcription.LocalHistoryHandler;
import dynamake.transcription.SimplePendingCommandFactory;
import dynamake.transcription.Trigger;

public class CanvasModel extends Model {
	private static class Entry implements Serializable {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		public final Location id;
		public final Model model;
		
		public Entry(Location id, Model model) {
			this.id = id;
			this.model = model;
		}
	}
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private int nextId;
	private ArrayList<Entry> models;
	
	public CanvasModel() {
		models = new ArrayList<Entry>();
	}
	
	public CanvasModel(ArrayList<Entry> models) {
		this.models = models;
	}
	
	public static class AddedModelChange {
		public final int index;
		public final Model model;
		
		public AddedModelChange(int index, Model model) {
			this.index = index;
			this.model = model;
		}
	}
	
	@Override
	public RestorableModel toRestorable(boolean includeLocalHistory) {
		return CanvasRestorableModel.wrap(this, includeLocalHistory);
	}
	
	@Override
	protected void modelScale(Fraction hChange, Fraction vChange, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		for(Entry entry: models)
			entry.model.scale(hChange, vChange, propCtx, propDistance, collector);
	}
	
	@Override
	public void destroy(PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		// Destroy inner models
		for(Entry entry: models)
			entry.model.destroy(propCtx, propDistance, collector);
		
		// Then destroy self
		super.destroy(propCtx, propDistance, collector);
	}

	public int getModelCount() {
		return models.size();
	}
	
	@Override
	public Model cloneBase() {
		return new CanvasModel();
	}
	
	public static class RemovedModelChange {
		public final int index;
		public final Model model;
		
		public RemovedModelChange(int index, Model model) {
			this.index = index;
			this.model = model;
		}
	}
	
	public static class MoveModelCommand implements Command<Model> {
		public static class Output implements Serializable {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;
			public final Location canvasSourceLocation;
			public final Location canvasTargetLocation;
			public final Location movedFromInSource;
			public final Location movedToInTarget;
			
			public Output(Location canvasSourceLocation, Location canvasTargetLocation, Location movedFromInSource, Location movedToInTarget) {
				this.canvasSourceLocation = canvasSourceLocation;
				this.canvasTargetLocation = canvasTargetLocation;
				this.movedFromInSource = movedFromInSource;
				this.movedToInTarget = movedToInTarget;
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		
		private Location canvasSourceLocation;
		private Location canvasTargetLocation;
		private Location locationInSource;

		public MoveModelCommand(Location canvasSourceLocation, Location canvasTargetLocation, Location locationInSource) {
			this.canvasSourceLocation = canvasSourceLocation;
			this.canvasTargetLocation = canvasTargetLocation;
			this.locationInSource = locationInSource;
		}

		@Override
		public Object executeOn(PropogationContext propCtx, Model prevalentSystem, Collector<Model> collector, Location location) {
			CanvasModel canvasSource = (CanvasModel)CompositeLocation.getChild(prevalentSystem, location, canvasSourceLocation);
			CanvasModel canvasTarget = (CanvasModel)CompositeLocation.getChild(prevalentSystem, location, canvasTargetLocation);
			Model model = (Model)canvasSource.getModelByLocation(locationInSource);

			canvasSource.removeModelByLocation(locationInSource, propCtx, 0, collector);
			canvasTarget.addModel(model, propCtx, 0, collector);
			Location locationInTarget = canvasTarget.getLocationOf(model);
			
			return new Output(canvasSourceLocation, canvasTargetLocation, locationInSource, locationInTarget);
		}
	}
	
	public static class MoveBackModelCommand implements Command<Model> {
		public static class AfterMove implements CommandFactory<Model> {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			public Command<Model> createCommand(Object output) {
				MoveModelCommand.Output moveOutput = (MoveModelCommand.Output)output;
				return new MoveBackModelCommand(moveOutput.canvasTargetLocation, moveOutput.canvasSourceLocation, moveOutput.movedToInTarget, moveOutput.movedFromInSource);
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		
		private Location canvasSourceLocation;
		private Location canvasTargetLocation;
		private Location locationInSource;
		private Location locationInTarget;

		public MoveBackModelCommand(Location canvasSourceLocation, Location canvasTargetLocation, Location locationInSource, Location locationInTarget) {
			this.canvasSourceLocation = canvasSourceLocation;
			this.canvasTargetLocation = canvasTargetLocation;
			this.locationInSource = locationInSource;
			this.locationInTarget = locationInTarget;
		}

		@Override
		public Object executeOn(PropogationContext propCtx, Model prevalentSystem, Collector<Model> collector, Location location) {
			CanvasModel canvasSource = (CanvasModel)CompositeLocation.getChild(prevalentSystem, location, canvasSourceLocation);
			CanvasModel canvasTarget = (CanvasModel)CompositeLocation.getChild(prevalentSystem, location, canvasTargetLocation);
			Model model = (Model)canvasSource.getModelByLocation(locationInSource);

			canvasSource.removeModelByLocation(locationInSource, propCtx, 0, collector);
			canvasTarget.restoreModelByLocation(locationInTarget, model, propCtx, 0, collector);
			
			return new MoveModelCommand.Output(canvasSourceLocation, canvasTargetLocation, locationInSource, locationInTarget);
		}
	}
	
	public static class AddModelCommand implements ForwardableCommand<Model> {
		public static class Output implements Serializable, ForwardableOutput {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;
			public final Location location;

			public Output(Location location) {
				this.location = location;
			}

			@Override
			public Object forForwarding() {
				return new AddModelCommand.Output(location.forForwarding());
			}
			
			@Override
			public String toString() {
				return "Added to " + location;
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		public final ModelFactory factory;
		
		public AddModelCommand(ModelFactory factory) {
			this.factory = factory;
		}
		
		@Override
		public Object executeOn(final PropogationContext propCtx, final Model rootPrevalentSystem, Collector<Model> collector, final Location location) {
			/*
			TODO: Consider
			Model creation and add could be changed as follows:
			Instead of during everything here in this method, the create and add procedure could leveraged by using the collector:
			- First, create the basic model and add this model to a special area of the canvas (something like: "model embryos")
			- Second, request the factory to do any additional creation; this creation must involve executing on the collector
			- Third, execute on the collector that the registered model embryo is to be added
			*/
			
			final CanvasModel canvas = (CanvasModel)location.getChild(rootPrevalentSystem);
			final ModelCreation modelCreation = factory.create(rootPrevalentSystem, propCtx, 0, collector, location);
			final Model model = modelCreation.createModel(rootPrevalentSystem, propCtx, 0, collector, location);
			
			canvas.addModel(model, new PropogationContext(), 0, collector);
			final Location addedModelLocation = canvas.getLocationOf(model);
//			System.out.println("***Adding model " + model + " at " + addedModelLocation + " in " + canvas + "***");
			
			collector.execute(new Trigger<Model>() {
				@Override
				public void run(Collector<Model> collector) {
					modelCreation.setup(rootPrevalentSystem, model, addedModelLocation, propCtx, 0, collector, location);
				}
			});
			
			return new Output(addedModelLocation);
		}
		
		@Override
		public Command<Model> forForwarding(Object output) {
			// When a model is added to a canvas, map id to ForwardedId (if not only already ForwardedId)
			// When a model is removed from a canvas, map id to ForwardedId (if not only already ForwardedId)
			AddModelCommand.Output addModelOutput = (AddModelCommand.Output)output;
			
			Location forwardedLocation = addModelOutput.location.forForwarding();
			ModelFactory newFactory = this.factory.forForwarding();
			CanvasModel.ForwardedAddModelCommand newAddCommand = new CanvasModel.ForwardedAddModelCommand(forwardedLocation, newFactory);

			return newAddCommand;
		}
		
		@Override
		public String toString() {
			return "Add " + factory;
		}
	}
	
	public static class ForwardedAddModelCommand implements ForwardableCommand<Model> {
		public static class Output implements Serializable {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;
			public final Location location;

			public Output(Location location) {
				this.location = location;
			}
			
			@Override
			public String toString() {
				return "Added model at " + location;
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		public final Location modelLocation;
		public final ModelFactory factory;
		
		public ForwardedAddModelCommand(Location modelLocation, ModelFactory factory) {
			this.modelLocation = modelLocation;
			this.factory = factory;
		}
		
		@Override
		public Object executeOn(PropogationContext propCtx, Model rootPrevalentSystem, Collector<Model> collector, Location location) {
			final CanvasModel canvas = (CanvasModel)location.getChild(rootPrevalentSystem);
			ModelCreation modelCreation = factory.create(rootPrevalentSystem, propCtx, 0, collector, location);
			final Model model = modelCreation.createModel(rootPrevalentSystem, propCtx, 0, collector, location);
			
			canvas.restoreModelByLocation(modelLocation, model, new PropogationContext(), 0, collector);
			Location addedModelLocation = canvas.getLocationOf(model);
			
			modelCreation.setup(rootPrevalentSystem, model, addedModelLocation, propCtx, 0, collector, location);
			
			return new Output(addedModelLocation);
		}
		
		@Override
		public Command<Model> forForwarding(Object output) {
			// When a model is added to a canvas, map id to ForwardedId (if not only already ForwardedId)
			// When a model is removed from a canvas, map id to ForwardedId (if not only already ForwardedId)
			AddModelCommand.Output addModelOutput = (AddModelCommand.Output)output;
			
			Location mappedLocation = addModelOutput.location.forForwarding();
			CanvasModel.ForwardedAddModelCommand newAddCommand = new CanvasModel.ForwardedAddModelCommand(mappedLocation, this.factory);

			return newAddCommand;
		}
	}
	
	// Should be MappableCommand due to RestorableModel?
	public static class RestoreModelCommand implements Command<Model>, Cloneable {
		public static final class AfterRemove implements CommandFactory<Model>  
		{
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			public Command<Model> createCommand(Object output) {
				RestorableModel restorableModel = ((RemoveModelCommand.Output)output).restorableModel;
				Location location = ((RemoveModelCommand.Output)output).location;
				// TODO: Consider the following:
				// What if the model what observing/being observed before its removal?
				// What if the model's observers/observees aren't all in existence anymore?
				// What if the model's observers/observees are restored after this model is restored?
				// Are all of the above cases possible?
				// Perhaps, the best solution would be to save the history and replay this history?
				
				return new CanvasModel.RestoreModelCommand(location, restorableModel);
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private Location modelLocationToRestore;
		private RestorableModel restorableModel;
		
		public RestoreModelCommand(Location modelLocationToRestore, RestorableModel restorableModel) {
			this.modelLocationToRestore = modelLocationToRestore;
			this.restorableModel = restorableModel;
		}
		
		@Override
		public Object executeOn(PropogationContext propCtx, Model prevalentSystem, Collector<Model> collector, Location location) {
			CanvasModel canvas = (CanvasModel)location.getChild(prevalentSystem);
			
			Model modelBase = restorableModel.unwrapBase(propCtx, 0, collector);
			System.out.println("***Restoring model " + modelBase + " at " + modelLocationToRestore + " from " + canvas + "***");
			restorableModel.restoreOriginsOnBase(modelBase, propCtx, 0, collector);
			
			canvas.restoreModelByLocation(modelLocationToRestore, modelBase, new PropogationContext(), 0, collector);
			
			restorableModel.restoreChangesOnBase(modelBase, propCtx, 0, collector);
			
			return new AddModelCommand.Output(modelLocationToRestore);
		}
	}
	
	public static class DestroyModelCommand implements ForwardableCommand<Model> {
		public static class Output implements Serializable, ForwardableOutput {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;
			public final Location location;

			public Output(Location location) {
				this.location = location;
			}
			
			@Override
			public Object forForwarding() {
				return new DestroyModelCommand.Output(location.forForwarding());
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private Location locationOfModelToDestroy;
		
		public DestroyModelCommand(Location locationOfModelToDestroy) {
			this.locationOfModelToDestroy = locationOfModelToDestroy;
		}

		@Override
		public Object executeOn(PropogationContext propCtx, Model prevalentSystem, Collector<Model> collector, Location location) {
			CanvasModel canvas = (CanvasModel)location.getChild(prevalentSystem);
			Model modelToDestroy = canvas.getModelByLocation(locationOfModelToDestroy);
			System.out.println("***Destroying model " + modelToDestroy + " at " + locationOfModelToDestroy + " from " + canvas + "***");
			
			modelToDestroy.destroy(propCtx, 0, collector);

			return new Output(locationOfModelToDestroy);
		}
		
		@Override
		public Command<Model> forForwarding(Object output) {
			DestroyModelCommand.Output destroyModelOutput = (DestroyModelCommand.Output)output;
			
			Location mappedLocation = destroyModelOutput.location.forForwarding();
			CanvasModel.DestroyModelCommand newDestroyCommand = new CanvasModel.DestroyModelCommand(mappedLocation);

			return newDestroyCommand;
		}
	}
	
	public static class RemoveModelCommand implements ForwardableCommand<Model> {
		public static class Output implements Serializable, ForwardableOutput {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;
			public final Location location;
			public final RestorableModel restorableModel;

			public Output(Location location, RestorableModel restorableModel) {
				this.location = location;
				this.restorableModel = restorableModel;
			}
			
			@Override
			public Object forForwarding() {
				return new RemoveModelCommand.Output(location.forForwarding(), restorableModel);
			}
		}
		
		public static final class AfterAdd implements /*Forwardable*/CommandFactory<Model>  
		{
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			public Command<Model> createCommand(Object output) {
				return new CanvasModel.RemoveModelCommand(((CanvasModel.AddModelCommand.Output)output).location);
			}
		}
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private Location locationOfModelToRemove;
		
		public RemoveModelCommand(Location locationOfModelToRemove) {
			this.locationOfModelToRemove = locationOfModelToRemove;
		}
		
		@Override
		public Object executeOn(final PropogationContext propCtx, Model prevalentSystem, Collector<Model> collector, Location location) {
			final CanvasModel canvas = (CanvasModel)location.getChild(prevalentSystem);
			Model modelToRemove = canvas.getModelByLocation(locationOfModelToRemove);
			System.out.println("***Removing model " + modelToRemove + " at " + locationOfModelToRemove + " from " + canvas + "***");
			
			RestorableModel restorableModel = modelToRemove.toRestorable(true);

			canvas.removeModelByLocation(locationOfModelToRemove, propCtx, 0, collector);
			
			return new Output(locationOfModelToRemove, restorableModel);
		}
		
		@Override
		public Command<Model> forForwarding(Object output) {
			// When a model is added to a canvas, map id to ForwardedId (if not only already ForwardedId)
			// When a model is removed from a canvas, map id to ForwardedId (if not only already ForwardedId)
			RemoveModelCommand.Output removeModelOutput = (RemoveModelCommand.Output)output;
			
			Location mappedLocation = removeModelOutput.location.forForwarding();
			CanvasModel.RemoveModelCommand newRemoveCommand = new CanvasModel.RemoveModelCommand(mappedLocation);

			return newRemoveCommand;
		}
	}
	
	public void restoreModelByLocation(Location id, Model model, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		int index = models.size();
		models.add(index, new Entry(id, model));
		model.setParent(this);
		sendChanged(new AddedModelChange(index, model), propCtx, propDistance, 0, collector);
	}

	public void addModel(Model model, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		addModel(models.size(), model, propCtx, propDistance, collector);
	}

	public Model getModelByIndex(int index) {
		return models.get(index).model;
	}
	
	/**
	 * location is assumed to be an id itself.
	 * 
	 * @param location
	 * @return
	 */
	public Model getModelByLocation(Location location) {
		for(Entry entry: models) {
			if(entry.id.equals(location))
				return entry.model;
		}
		return null;
	}

	public void addModel(int index, Model model, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		int id = nextId++;
		
		models.add(index, new Entry(new IdLocation(id), model));
		model.setParent(this);
		sendChanged(new AddedModelChange(index, model), propCtx, propDistance, 0, collector);
		
//		System.out.println("Add model with id " + id + " in canvas " + this);
	}
	
	public void removeModel(Model model, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		int indexOfModel = indexOfModel(model);
		removeModelByIndex(indexOfModel, propCtx, propDistance, collector);
	}
	
	public void removeModelByIndex(int index, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		Model model = models.get(index).model;
		models.remove(index);
		model.setParent(null);
		sendChanged(new RemovedModelChange(index, model), propCtx, propDistance, 0, collector);
	}

	public void removeModelByLocation(Location location, PropogationContext propCtx, int propDistance, Collector<Model> collector) {
		// TODO: Figure out: How to support ForwardLocations here?
		int indexOf = getIndexOfModelByLocation(location);
		removeModelByIndex(indexOf, propCtx, propDistance, collector);
		
//		System.out.println("Removed model with id " + ((IdLocation)location).id + " in canvas " + this);
	}
	
	public int indexOfModel(Model model) {
		for(int i = 0; i < models.size(); i++) {
			if(models.get(i).model == model)
				return i;
		}
		
		return -1;
	}

	private int getIndexOfModelByLocation(Location id) {
		for(int i = 0; i < models.size(); i++) {
			if(models.get(i).id.equals(id))
				return i;
		}
		
		return -1;
	}
	
	public Location getLocationOf(Model model) {
		int indexOfModel = indexOfModel(model);
		return models.get(indexOfModel).id;
	}

	public Location getNextLocation() {
		return new IdLocation(nextId);
	}
	
	private static class CanvasPanel extends JLayeredPane implements ModelComponent {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private CanvasModel model;
		private ModelTranscriber modelTranscriber;
		private HashSet<Model> shownModels = new HashSet<Model>();
		private Memoizer1<Model, Binding<ModelComponent>> modelToModelComponentMap;
		
		public CanvasPanel(final ModelComponent rootView, CanvasModel model, final ModelTranscriber modelTranscriber, final ViewManager viewManager) {
			this.model = model;
			this.modelTranscriber = modelTranscriber;
			setLayout(null);
			setBorder(BorderFactory.createLineBorder(Color.BLACK));
			setOpaque(true);
			
			modelToModelComponentMap = new Memoizer1<Model, Binding<ModelComponent>>(new Func1<Model, Binding<ModelComponent>>() {
				@Override
				public Binding<ModelComponent> call(Model model) {
					final Binding<ModelComponent> modelView = model.createView(rootView, viewManager, modelTranscriber.extend(new ItemLocator(CanvasPanel.this.model, model)));
					
					Rectangle bounds = new Rectangle(
						((Fraction)model.getProperty("X")).intValue(),
						((Fraction)model.getProperty("Y")).intValue(),
						((Fraction)model.getProperty("Width")).intValue(),
						((Fraction)model.getProperty("Height")).intValue()
					);
					
					((JComponent)modelView.getBindingTarget()).setBounds(bounds);
					
					return modelView;
				}
			});
		}

		@Override
		public Model getModelBehind() {
			return model;
		}
		
		@Override
		public void appendContainerTransactions(
				final LivePanel livePanel, CompositeMenuBuilder menuBuilder, final ModelComponent child) {
			menuBuilder.addMenuBuilder("Remove", new Trigger<Model>() {
				@Override
				public void run(Collector<Model> collector) {
					CanvasModel.appendRemoveTransaction(collector, livePanel, child, model);
				}
			});
		}

		@Override
		public void appendTransactions(ModelComponent livePanel, CompositeMenuBuilder menuBuilder) {
			Model.appendComponentPropertyChangeTransactions(livePanel, model, modelTranscriber, menuBuilder);
			// The canvas model can be unwrap only if all the following cases are true:
			// - It has one ore more models contained in itself
			// - Its parent is a canvas model; i.e. canvases can only be unwrapped into other canvases
			if(model.getModelCount() > 0 && ModelComponent.Util.getParent(this).getModelBehind() instanceof CanvasModel) {
				menuBuilder.addMenuBuilder("Unwrap", new Trigger<Model>() {
					@Override
					public void run(Collector<Model> collector) {
						ModelComponent parent = ModelComponent.Util.getParent(CanvasPanel.this);
						ArrayList<CommandState<Model>> pendingCommands = new ArrayList<CommandState<Model>>();
						CanvasModel.appendUnwrapTransaction(pendingCommands, CanvasPanel.this, parent);
						
						PendingCommandFactory.Util.sequence(collector, parent.getModelBehind(), pendingCommands, LocalHistoryHandler.class);
					}
				});
			}
		}
		@Override
		public void appendDroppedTransactions(ModelComponent livePanel, ModelComponent target, Rectangle droppedBounds, CompositeMenuBuilder menuBuilder) {
			Model.appendGeneralDroppedTransactions(livePanel, this, target, droppedBounds, menuBuilder);
		}
		
		@Override
		public void appendDropTargetTransactions(final ModelComponent livePanel,
				final ModelComponent dropped, final Rectangle droppedBounds, final Point dropPoint, CompositeMenuBuilder menuBuilder) {
			if(dropped.getModelTranscriber().getParent() != null && 
				dropped.getModelTranscriber().getParent() != CanvasPanel.this.modelTranscriber &&
				!isContainerOf(dropped.getModelTranscriber(), this.modelTranscriber) /*Dropee cannot be child of dropped*/) {
				menuBuilder.addMenuBuilder("Move", new Trigger<Model>() {
					@Override
					public void run(Collector<Model> collector) {
						final ModelComponent modelToMove = dropped;

						// Reference is closest common ancestor
						ModelComponent source = ModelComponent.Util.getParent(modelToMove);
						ModelComponent targetOver = CanvasPanel.this;
						ModelComponent referenceMC = ModelComponent.Util.closestCommonAncestor(source, targetOver);
						
						Location locationOfSource = ModelComponent.Util.locationFromAncestor(referenceMC, source);
						Location locationOfTarget = ModelComponent.Util.locationFromAncestor(referenceMC, targetOver);
						
						ArrayList<CommandState<Model>> pendingCommands = new ArrayList<CommandState<Model>>();
						
						CanvasModel.appendMoveTransaction(pendingCommands, (LivePanel)livePanel, source, modelToMove, targetOver, droppedBounds.getLocation(), locationOfSource, locationOfTarget);
						
						PendingCommandFactory.Util.sequence(collector, referenceMC.getModelBehind(), pendingCommands, LocalHistoryHandler.class);
					}
				});
			}
		}
		
		private boolean isContainerOf(ModelTranscriber container, ModelTranscriber item) {
			ModelTranscriber parent = item.getParent();
			if(parent != null) {
				if(parent == container)
					return true;
				return isContainerOf(container, parent);
			}
			return false;
		}

		@Override
		public ModelTranscriber getModelTranscriber() {
			return modelTranscriber;
		}
		
		@Override
		public void initialize() {

		}
	}
	
	public static void appendUnwrapTransaction(List<CommandState<Model>> commandStates, ModelComponent toUnwrap, ModelComponent parent) {
		CanvasModel target = (CanvasModel)parent.getModelBehind();
		CanvasModel modelToBeUnwrapped = (CanvasModel)toUnwrap.getModelBehind();
		Location wrapperLocationInTarget = target.getLocationOf(modelToBeUnwrapped);
		RectangleF creationBoundsInSelection = new RectangleF(
			(Fraction)modelToBeUnwrapped.getProperty("X"),
			(Fraction)modelToBeUnwrapped.getProperty("Y"),
			(Fraction)modelToBeUnwrapped.getProperty("Width"),
			(Fraction)modelToBeUnwrapped.getProperty("Height")
		);
		
		commandStates.add(new PendingCommandState<Model>(
			new UnwrapCommand(wrapperLocationInTarget, creationBoundsInSelection),
			new RewrapCommand.AfterUnwrap(),
			new UnwrapCommand.AfterWrap()
		));
	}
	
	public static void appendRemoveTransaction(List<CommandState<Model>> pendingCommands, LivePanel livePanel, ModelComponent child, CanvasModel model) {
		Location locationOfModel = model.getLocationOf(child.getModelBehind());

		pendingCommands.add(new PendingCommandState<Model>(
			new CanvasModel.DestroyModelCommand(locationOfModel),
			new Command.Null<Model>()
		));
		pendingCommands.add(new PendingCommandState<Model>(
			new RemoveModelCommand(locationOfModel),
			new RestoreModelCommand.AfterRemove(),
			new RemoveModelCommand.AfterAdd()
		));
	}
	
	public static void appendRemoveTransaction(Collector<Model> collector, LivePanel livePanel, ModelComponent child, final CanvasModel model) {
		ArrayList<CommandState<Model>> pendingCommands = new ArrayList<CommandState<Model>>();
		
		appendRemoveTransaction(pendingCommands, livePanel, child, model);
		
		PendingCommandFactory.Util.sequence(collector, model, pendingCommands, LocalHistoryHandler.class);
	}
	
	public static void appendMoveTransaction(List<CommandState<Model>> commandStates, LivePanel livePanel, ModelComponent source, ModelComponent modelToMove, ModelComponent target, final Point moveLocation, Location canvasSourceLocation, Location canvasTargetLocation) {
		CanvasModel sourceCanvas = (CanvasModel)source.getModelBehind();
		Location locationInSource = sourceCanvas.getLocationOf(modelToMove.getModelBehind());
		
		Location canvasTargetLocationAfter = canvasTargetLocation;
		
		Location modelLocationAfterMove = new CompositeLocation(canvasTargetLocationAfter, ((CanvasModel)target.getModelBehind()).getNextLocation());
		
		commandStates.add(new PendingCommandState<Model>(
			new CanvasModel.MoveModelCommand(canvasSourceLocation, canvasTargetLocation, locationInSource), 
			new CanvasModel.MoveBackModelCommand.AfterMove(),
			new CanvasModel.MoveBackModelCommand.AfterMove()
{'fraction_non_alphanumeric': 0.08546086539614048, 'fraction_numerical': 0.001678053457988733, 'mean_word_length': 4.898596101403899, 'pattern_counts': {'":': 0, '<': 162, '<?xml version=': 0, '>': 160, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4032528', 'n_tokens_mistral': 18439, 'n_tokens_neox': 15600, 'n_words': 3995}