/**
 * File:    WEEditorSheet.java
 * Created: 20.06.2008
 *
/*******************************************************************************
 * Copyright (c) 2005-2011
 * University of Augsburg, Germany <www.ds-lab.org>
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Christian Saad, Programming distributed Systems Lab, University of Augsburg 
 *    	- implemented from scratch based now upon GraphicalEditorWithFlyoutPalette
 *    Marc Blachon, Bull SAS, Grenoble, France
 *      - getting required factories from factoryRegistry
 *    Mickael Istria, Open Wide, Lyon, France
 *      - DND Drop support
 *******************************************************************************/

package org.eclipse.jwt.we.editors.pages.activityEditor;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.SnapToGeometry;
import org.eclipse.gef.SnapToGrid;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.ui.palette.PaletteViewer;
import org.eclipse.gef.ui.parts.GraphicalViewerKeyHandler;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.util.TransferDropTargetListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jwt.meta.model.processes.Activity;
import org.eclipse.jwt.we.Plugin;
import org.eclipse.jwt.we.PluginProperties;
import org.eclipse.jwt.we.editors.WEEditor;
import org.eclipse.jwt.we.editors.dnd.IDropListenerFactory;
import org.eclipse.jwt.we.editors.dnd.internal.DropListenerExtensionPoint;
import org.eclipse.jwt.we.editors.pages.activityEditor.internal.CustomGraphicalEditorWithFlyoutPalette;
import org.eclipse.jwt.we.editors.pages.activityEditor.internal.CustomTemplateTransferDropTargetListener;
import org.eclipse.jwt.we.editors.pages.activityEditor.internal.IActivityEditor;
import org.eclipse.jwt.we.editors.pages.activityEditor.internal.WEMouseMoveTrackListener;
import org.eclipse.jwt.we.editors.preferences.PreferenceReader;
import org.eclipse.jwt.we.editors.selection.EditPartViewerSelectionConverter;
import org.eclipse.jwt.we.misc.logging.Logger;
import org.eclipse.jwt.we.parts.EditPartAdapter;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IWorkbenchPart;

/**
 * The graphical GEF editor for displaying and manipulating JWT activities which
 * implements the IActivityEditor interface. It is built upon the
 * GraphicalEditorWithFlyoutPalette implementation of a graphical editor which
 * comes with GEF. It currently contains support for: Grid, Zoom, MouseListener,
 * SelectionSynchronizer, Context menu and DragnDrop from outline view.
 * 
 * NOTE: This editor sheet doesn't manage any actions (an empty actionregistry
 * is returned), so the actions have to manage their enabled state themselves.
 * This is because other sheets (e.g. overview page) may exist that also don't
 * update the global actions.
 * 
 * NOTE: The selection is managed by the selectionsynchronizer provided by the
 * parent multi-editor. The graphical viewer returns the selected edit parts.
 * 
 * @version $Id: WEEditorSheet.java,v 1.13 2009-12-04 14:56:42 chsaad Exp $
 * @author Christian Saad, Programming distributed Systems Lab, University of
 *         Augsburg, Germany, www.ds-lab.org
 * @since 0.5.0
 */
public class WEEditorSheet extends CustomGraphicalEditorWithFlyoutPalette implements
		IActivityEditor
{

	/**
	 * The Logger.
	 */
	private static final Logger logger = Logger.getLogger(WEEditorSheet.class);

	/**
	 * The WEEditor hosting this sheet.
	 */
	private WEEditor parentEditor;

	/**
	 * The activity model which is displayed in the editor.
	 */
	private Activity activityModel;

	/**
	 * The palette viewer of the activity editor sheet.
	 */
	private PaletteViewer paletteViewer;

	/**
	 * The Root EditPart.
	 */
	private ScalableFreeformRootEditPart rootEditPart;


	/**
	 * Creates an WEEditorSheet.
	 * 
	 * @param parent
	 *            The Workflow editor
	 * @param activityModel
	 *            The active activity model
	 */
	public WEEditorSheet(WEEditor parent)
	{
		parentEditor = parent;
		this.setEditDomain(parent.getEditDomain());
	}


	// ///////////////////////////////////////////////////////////////////////////
	// Getter and Setter
	//

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette#getPaletteRoot
	 * ()
	 */
	@Override
	protected PaletteRoot getPaletteRoot()
	{
		return Plugin.getInstance().getFactoryRegistry().getPaletteFactory()
				.getPaletteRoot(parentEditor);

	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.ui.parts.GraphicalEditor#getGraphicalViewer()
	 */
	@Override
	public GraphicalViewer getGraphicalViewer()
	{
		return super.getGraphicalViewer();
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.ui.parts.GraphicalEditor#getEditDomain()
	 */
	@Override
	public DefaultEditDomain getEditDomain()
	{
		return super.getEditDomain();
	}


	/**
	 * @return Returns the activity which is displayed.
	 */
	public Activity getActivityModel()
	{
		return activityModel;
	}


	/**
	 * Returns the rootEditPart.
	 * 
	 * @return The rootEditPart
	 */
	public ScalableFreeformRootEditPart getRootEditPart()
	{
		return rootEditPart;
	}


	/**
	 * Returns the palette viewer.
	 * 
	 * @return The palette viewer.
	 */
	public PaletteViewer getPaletteViewer()
	{
		return paletteViewer;
	}


	// ///////////////////////////////////////////////////////////////////////////
	// Initialization
	//

	/*
	 * (non-Javadoc)
	 * 
	 * @seeorg.eclipse.jwt.we.editors.pages.activityEditor.
	 * CustomGraphicalEditorWithFlyoutPalette
	 * #createPartControl(org.eclipse.swt.widgets.Composite)
	 */
	public void createPartControl(Composite parent)
	{
		super.createPartControl(parent);

		// save the palette viewer of this sheet
		paletteViewer = getEditDomain().getPaletteViewer();
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.ui.parts.GraphicalEditor#hookGraphicalViewer()
	 */
	@Override
	protected void hookGraphicalViewer()
	{
		// use custom selection synchronizer
		parentEditor.getSelectionSynchronizer().setSelection(new StructuredSelection());
		parentEditor.getSelectionSynchronizer().addSelectionProvider(
				getGraphicalViewer(),
				new EditPartViewerSelectionConverter(getGraphicalViewer()));

		getSite().setSelectionProvider(getGraphicalViewer());
	}


	/**
	 * Called to configure the graphical viewer before it receives its contents.
	 * This is where the root editpart should be configured. Subclasses should
	 * extend or override this method as needed.
	 */
	@Override
	protected void configureGraphicalViewer()
	{
		super.configureGraphicalViewer();

		// set the background color of the graphical viewer
		getGraphicalViewer().getControl().setBackground(
				PreferenceReader.appearanceEditorColor.get());

		// create root edit part for viewer
		getGraphicalViewer().setEditPartFactory(
				Plugin.getInstance().getFactoryRegistry().getEditPartFactory());
		rootEditPart = new ScalableFreeformRootEditPart();
		getGraphicalViewer().setRootEditPart(rootEditPart);
		getGraphicalViewer().setKeyHandler(
				new GraphicalViewerKeyHandler(getGraphicalViewer()));

		// configure the context menu provider
		MenuManager contextMenu = new MenuManager();
		contextMenu.setRemoveAllWhenShown(true);
		contextMenu.addMenuListener(parentEditor.getContextMenuProvider());
		getGraphicalViewer().setContextMenu(contextMenu);
		getSite().registerContextMenu(contextMenu, getGraphicalViewer());

		EditPartViewer viewer = getGraphicalViewer();
		// listen for drops from the outline view
		viewer.addDropTargetListener(new CustomTemplateTransferDropTargetListener(
				getGraphicalViewer()));

		// configure zoom manager
		List<String> zoomLevels = new ArrayList<String>(3);
		zoomLevels.add(ZoomManager.FIT_ALL);
		zoomLevels.add(ZoomManager.FIT_WIDTH);
		zoomLevels.add(ZoomManager.FIT_HEIGHT);
		getRootEditPart().getZoomManager().setZoomLevelContributions(zoomLevels);

		// add mouse listener to viewer (depending on preferences)
		if (PreferenceReader.appearanceMousePosShow.get())
		{
			getGraphicalViewer().getControl().addMouseMoveListener(
					WEMouseMoveTrackListener.getInstance());
			getGraphicalViewer().getControl().addMouseTrackListener(
					WEMouseMoveTrackListener.getInstance());
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.ui.parts.GraphicalEditor#initializeActionRegistry()
	 */
	@Override
	protected void initializeActionRegistry()
	{
		// do not use an action registry
	}


	/**
	 * Paints or removes the grid and sets related options.
	 */
	public void setGridProperties()
	{
		if (getGraphicalViewer() != null)
		{
			// read grid properties from preference store
			int gridWidth = PreferenceReader.gridWidth.get();
			int gridHeight = PreferenceReader.gridHeight.get();
			boolean gridVisible = PreferenceReader.gridVisible.get();
			boolean gridSnap = PreferenceReader.gridSnap.get();

			// set grid properties
			getGraphicalViewer().setProperty(SnapToGrid.PROPERTY_GRID_SPACING,
					new Dimension(gridWidth, gridHeight));

			getGraphicalViewer().setProperty(SnapToGrid.PROPERTY_GRID_VISIBLE,
					gridVisible);

			getGraphicalViewer().setProperty(SnapToGrid.PROPERTY_GRID_ENABLED, gridSnap);
			getGraphicalViewer().setProperty(SnapToGeometry.PROPERTY_SNAP_ENABLED,
					gridSnap);
		}
	}


	// ///////////////////////////////////////////////////////////////////////////
	// The Model
	//

	/**
	 * Sets the {@link Activity} that is shown in the editor.
	 * 
	 * THIS METHOD SHOULD NOT BE CALLED EXTERNALLY TO SET/REMOVE THE ACTIVITY.
	 * USE openActivityInCurrentPage or openActivityInNewPage INSTEAD
	 * 
	 * @param newActivityModel
	 *            The activityModel to set.
	 */
	public void loadActivityModel(Activity newActivityModel)
	{
		logger.enter();

		// check if model can be loaded and is not null
		try
		{
			parentEditor.getModel().getClass();
		}
		catch (Exception e)
		{
			String message = PluginProperties
					.editor_ErrorLoadingFile_message(getEditorInput().getName());
			logger.severe("Could not load input from " + getEditorInput().getName(), e); //$NON-NLS-1$ //$NON-NLS-2$
			MessageDialog.openError(getEditorSite().getShell(), message, e.getMessage());
			return;
		}

		// if no model was supplied -> use the first one
		if (newActivityModel == null)
		{
			newActivityModel = parentEditor.getFirstActivityModel();
		}

		// if old model == new model -> refresh
		if (getActivityModel() == newActivityModel)
		{
			refreshEditorSheet();
			return;
		}
		// if not, load new model
		else
		{
			// remove the property listener from the old activity and add to new
			parentEditor.getEditorPropertyUpdater().removeUpdateListener(
					getActivityModel());
			parentEditor.getEditorPropertyUpdater().addUpdateListener(newActivityModel);

			// set the new activity internally
			activityModel = newActivityModel;

			// load the new activity into the graphical viewer
			getGraphicalViewer().setContents(getActivityModel());

			// select the new activity
			parentEditor.getSelectionProvider().setSelection(
					new StructuredSelection(getActivityModel()));
		}

		// DND extension point
		GraphicalViewer viewer = getGraphicalViewer();
		for (IDropListenerFactory dropFactory : DropListenerExtensionPoint
				.getDropListenersFactories())
		{
			TransferDropTargetListener dropListener = dropFactory
					.createWEEditorSheetDropListener(this, this.getActivityModel());
			if (dropListener != null)
				viewer.addDropTargetListener(dropListener);
		}
	}


	/**
	 * Updates the display. This is always called for all editors and all open
	 * activities if appearance preferences (e.g. the current view) have been
	 * changed.
	 */
	public void refreshEditorSheet()
	{
		if (getActivityModel() != null)
		{
			// refresh the contents of the graphical viewer and the outline page
			GraphicalViewer gv = getGraphicalViewer();

			// set contents to null (clears editpartadapters)
			gv.setContents(null);
			// set new editpartadapterfactory
			getGraphicalViewer().setEditPartFactory(
					Plugin.getInstance().getFactoryRegistry().getEditPartFactory());
			// set new input
			gv.setContents(activityModel);

			// remove/add mouse listener (depending on preferences)
			getGraphicalViewer().getControl().removeMouseMoveListener(
					WEMouseMoveTrackListener.getInstance());
			getGraphicalViewer().getControl().removeMouseTrackListener(
					WEMouseMoveTrackListener.getInstance());

			if (PreferenceReader.appearanceMousePosShow.get())
			{
				getGraphicalViewer().getControl().addMouseMoveListener(
						WEMouseMoveTrackListener.getInstance());
				getGraphicalViewer().getControl().addMouseTrackListener(
						WEMouseMoveTrackListener.getInstance());
			}

			// set the background color of the graphical viewer
			getGraphicalViewer().getControl().setBackground(
					PreferenceReader.appearanceEditorColor.get());

			// ???
			// EditPart contents = gv.getContents();
			// if (contents == null) return;
			// List childrenlist = contents.getChildren();
			// for (Object ep: childrenlist)
			// {
			// ((EditPart) ep).refresh();
			// }
			// getGraphicalViewer().getContents().refresh();
			// ???
		}
	}


	// ///////////////////////////////////////////////////////////////////////////
	// Configuration
	//

	/**
	 * @see org.eclipse.ui.ISelectionListener#selectionChanged(IWorkbenchPart,
	 *      ISelection)
	 */
	@Override
	public void selectionChanged(IWorkbenchPart part, ISelection selection)
	{
		// do not react to selection changed
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @seeorg.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.
	 * IProgressMonitor)
	 */
	@Override
	public void doSave(IProgressMonitor monitor)
	{
		parentEditor.doSave(monitor);
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
	 */
	@Override
	public boolean isSaveAsAllowed()
	{
		return true;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.ui.parts.GraphicalEditor#isDirty()
	 */
	@Override
	public boolean isDirty()
	{
		return parentEditor.isDirty();
	}


	/**
	 * @param x
	 * @return
	 */
	public int absoluteToRelativeX(int x)
	{
		int baseX = 0;
		Control control = this.getGraphicalControl();
		while (control != null)
		{
			baseX += control.getLocation().x;
			control = control.getParent();
		}
		return x - baseX;
	}


	/**
	 * @param y
	 * @return
	 */
	public int absoluteToRelativeY(int y)
	{
		int baseY = 0;
		Control control = this.getGraphicalControl();
		while (control != null)
		{
			baseY += control.getLocation().y;
			control = control.getParent();
		}
		return y - baseY;
	}


	// ///////////////////////////////////////////////////////////////////////////
	// Dispose
	//

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.ui.parts.GraphicalEditor#dispose()
	 */
	@Override
	public void dispose()
	{
		// deselect
		getGraphicalViewer().deselectAll();

		// disposeFigure(rootEditPart);

		getGraphicalViewer().setContents(null);

		// set paletteViewer reference to null
		paletteViewer = null;

		// remove selection synchronizer
		if (parentEditor != null)
		{
			// remove selection provider
			parentEditor.getSelectionSynchronizer().removeSelectionProvider(
					getGraphicalViewer());

			// remove the property listener from the activity
			parentEditor.getEditorPropertyUpdater().removeUpdateListener(
					getActivityModel());
		}

		// remove mouse listener from graphical viewer
		if (getGraphicalViewer().getControl() != null)
		{
			getGraphicalViewer().getControl().removeMouseMoveListener(
					WEMouseMoveTrackListener.getInstance());
			getGraphicalViewer().getControl().removeMouseTrackListener(
					WEMouseMoveTrackListener.getInstance());
		}

		// deactivate root editpart
		if (rootEditPart != null)
		{
			rootEditPart.deactivate();
			rootEditPart = null;
		}

		// remove activity modell
		activityModel = null;

		super.dispose();
	}

}
