/**
 * File:    WEActionBarContributor.java
 * Created: 27.11.2005
 *
 *
/*******************************************************************************
 * 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:
 *    Markus Bauer, Programming distributed Systems Lab, University of Augsburg 
 *    	- initial API and implementation
 *    Christian Saad, Programming distributed Systems Lab, University of Augsburg 
 *    	- additional functionality
 *******************************************************************************/

package org.eclipse.jwt.we.internal;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.GEFActionConstants;
import org.eclipse.gef.ui.actions.ZoomComboContributionItem;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.ICoolBarManager;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarContributionItem;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jwt.we.PluginProperties;
import org.eclipse.jwt.we.editors.WEEditor;
import org.eclipse.jwt.we.editors.actions.external.WEExternalAction;
import org.eclipse.jwt.we.editors.actions.external.WEExternalActionsManager;
import org.eclipse.jwt.we.editors.actions.internalActions.CreateChildAction;
import org.eclipse.jwt.we.editors.actions.internalActions.CreateSiblingAction;
import org.eclipse.jwt.we.editors.actions.managed.sheet.ExternalSheetAction;
import org.eclipse.jwt.we.editors.actions.managed.zoom.ZoomControl;
import org.eclipse.jwt.we.editors.pages.activityEditor.internal.IActivityEditor;
import org.eclipse.jwt.we.editors.pages.externalSheet.internal.WEExternalSheetManager;
import org.eclipse.jwt.we.misc.logging.Logger;
import org.eclipse.jwt.we.misc.views.Views;
import org.eclipse.jwt.we.model.view.Reference;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.part.EditorActionBarContributor;

/**
 * Contributes actions to a toolbar. This class is tied to the editor in the
 * definition of editor-extension (see plugin.xml).
 * 
 * <p>
 * It also creates the global menus and the context menus. They provide editing
 * actions.
 * </p>
 * 
 * <p>
 * The actions from the {@link IActivityEditor} are passed using an
 * {@link ActionRegistry} .
 * </p>
 * 
 * @version $Id: ActionBarContributor.java,v 1.5 2009/11/26 12:41:52 chsaad Exp
 *          $
 * @author Markus Bauer (markusbauer@users.sourceforge.net), Programming
 *         distributed Systems Lab, University of Augsburg, Germany,
 *         www.ds-lab.org
 */
public class ActionBarContributor extends EditorActionBarContributor implements
		ISelectionChangedListener, IMenuListener
{

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

	/**
	 * This keeps track of the active editor.
	 */
	private IEditorPart activeEditorPart;

	/**
	 * This keeps track of the current selection provider.
	 */
	private ISelectionProvider selectionProvider;

	/**
	 * This will contain one
	 * {@link org.eclipse.emf.edit.ui.action.CreateChildAction} corresponding to
	 * each descriptor generated for the current selection by the item provider.
	 */
	private Collection createChildActions;

	/**
	 * This will contain one
	 * {@link org.eclipse.emf.edit.ui.action.CreateSiblingAction} corresponding
	 * to each descriptor generated for the current selection by the item
	 * provider.
	 */
	private Collection createSiblingActions;

	/**
	 * This keeps track of the current selected Object.
	 */
	private Object selectedObject;

	/**
	 * The coolbar manager.
	 */
	private ICoolBarManager coolBarManager;


	/**
	 * Constructor.
	 */
	public ActionBarContributor()
	{
		logger.instanceCreation();
	}


	/**
	 * @see EditorActionBarContributor#init(IActionBars)
	 */
	@Override
	public void init(IActionBars bars)
	{
		super.init(bars);
	}


	/**
	 * Returns this contributor's ActionRegsitry.
	 * 
	 * @return the ActionRegistry
	 */
	protected ActionRegistry getActionRegistry()
	{
		if (activeEditorPart == null)
		{
			return null;
		}
		return (ActionRegistry) activeEditorPart.getAdapter(ActionRegistry.class);
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @seeorg.eclipse.ui.part.EditorActionBarContributor#setActiveEditor(org.
	 * eclipse.ui. IEditorPart)
	 */
	@Override
	public void setActiveEditor(IEditorPart newEditor)
	{
		logger.enter(newEditor.getClass().getName());

		// always update the views toolbar state
		Views.getInstance().refreshViewsState((WEEditor) activeEditorPart);

		// return if nothing changed
		if (newEditor == activeEditorPart)
		{
			return;
		}

		// set the active part to the new part
		activeEditorPart = newEditor;

		// refresh zoom if different file was selected
		((WEEditor) activeEditorPart).refreshZoom();

		// refresh grid if different file was selected
		if (((WEEditor) activeEditorPart).getActiveEditor() instanceof IActivityEditor)
		{
			((WEEditor) activeEditorPart).getCurrentActivitySheet().setGridProperties();
		}

		// register global actions for editor (actions for outline are
		// registered in
		// ModelContentOutlinePage)
		IActionBars bars = getActionBars();
		ArrayList globalActionIDs = ((WEEditor) activeEditorPart).getGlobalActionKeys();

		for (int i = 0; i < globalActionIDs.size(); i++)
		{
			String id = (String) globalActionIDs.get(i);
			bars.setGlobalActionHandler(id, getActionRegistry().getAction(id));
		}
		bars.updateActionBars();

		// Switch to the new selection provider.
		if (selectionProvider != null)
		{
			selectionProvider.removeSelectionChangedListener(this);
		}

		if (activeEditorPart == null)
		{
			selectionProvider = null;
		}
		else
		{
			selectionProvider = activeEditorPart.getSite().getSelectionProvider();
			selectionProvider.addSelectionChangedListener(this);

			// Fake a selection changed event to update the menus.
			if (selectionProvider.getSelection() != null)
			{
				selectionChanged(new SelectionChangedEvent(selectionProvider,
						selectionProvider.getSelection()));
			}
		}
	}


	/**
	 * This implements
	 * {@link org.eclipse.jface.viewers.ISelectionChangedListener}, handling
	 * {@link org.eclipse.jface.viewers.SelectionChangedEvent}s by querying for
	 * the children and siblings that can be added to the selected object and
	 * updating the menus accordingly.
	 * 
	 * @param event
	 *            The event.
	 */
	public void selectionChanged(SelectionChangedEvent event)
	{
		selectedObject = null;

		// Query the new selection for appropriate new child/sibling descriptors
		Collection newChildDescriptors = null;
		Collection newSiblingDescriptors = null;

		ISelection selection = event.getSelection();

		if (selection instanceof IStructuredSelection
				&& ((IStructuredSelection) selection).size() == 1)
		{
			selectedObject = ((IStructuredSelection) selection).getFirstElement();

			if (selectedObject instanceof Reference)
			{
				selectedObject = ((Reference) selectedObject).getReference();
			}

			logger.valueChanged("selectedObject", selectedObject); //$NON-NLS-1$

			EditingDomain domain = AdapterFactoryEditingDomain
					.getEditingDomainFor(selectedObject);

			if (domain != null)
			{
				newChildDescriptors = domain.getNewChildDescriptors(selectedObject, null);
				newSiblingDescriptors = domain.getNewChildDescriptors(null,
						selectedObject);
				selection = new StructuredSelection(selectedObject);
			}
		}

		// Generate actions for selection
		createChildActions = generateCreateChildActions(newChildDescriptors, selection);
		createSiblingActions = generateCreateSiblingActions(newSiblingDescriptors,
				selection);
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jface.action.IMenuListener#menuAboutToShow(org.eclipse.jface
	 * .action .IMenuManager)
	 */
	public void menuAboutToShow(IMenuManager menuManager)
	{
		logger.enter();

		// add standard actions to popup menu
		GEFActionConstants.addStandardActionGroups(menuManager);

		// add manual actions to popup menu
		addGlobalActionsToPopup(menuManager);

		// add external actions and sheets to popup menu
		boolean externalGroup1 = addExternalActionsToPopup(menuManager);
		boolean externalGroup2 = addExternalSheetsToPopup(menuManager);

		if (externalGroup1 || externalGroup2)
		{
			menuManager.appendToGroup(IWorkbenchActionConstants.GROUP_ADD,
					new Separator());
		}

		// add create child/sibling actions to popup menu
		addCreationSubMenusToPopup(menuManager);
	}


	/**
	 * This inserts global actions.
	 * 
	 * @param menuManager
	 *            The menu
	 */
	protected void addGlobalActionsToPopup(IMenuManager menuManager)
	{
		if (getActionRegistry() == null)
		{
			return;
		}

		// add direct edit action
		menuManager.appendToGroup(GEFActionConstants.GROUP_EDIT, getActionRegistry()
				.getAction(GEFActionConstants.DIRECT_EDIT));

		// add grid group
		menuManager.insertBefore(GEFActionConstants.GROUP_VIEW, new GroupMarker(
				"org.eclipse.jwt.we.grid")); //$NON-NLS-1$
	}


	/**
	 * Adds the all external actions to the context menu.
	 * 
	 * @param menuManager
	 */
	protected boolean addExternalActionsToPopup(IMenuManager menuManager)
	{
		// if no external actions exist -> return
		if (WEExternalActionsManager.getInstance().getExternalActions().size() == 0)
		{
			return false;
		}

		// initalize sub menu manager
		IMenuManager subMenuManager = new MenuManager(
				PluginProperties.menu_External_label, "external"); //$NON-NLS-1$

		// add external functions to sub menu
		for (Iterator iterator = WEExternalActionsManager.getInstance()
				.getExternalActions().iterator(); iterator.hasNext();)
		{
			WEExternalAction externalAction = (WEExternalAction) iterator.next();

			subMenuManager.add(externalAction);
		}

		// add the external submenu to the menu manager
		menuManager.appendToGroup(IWorkbenchActionConstants.GROUP_ADD, subMenuManager);

		return true;
	}


	/**
	 * Adds the all external sheets.
	 * 
	 * @param menuManager
	 */
	protected boolean addExternalSheetsToPopup(IMenuManager menuManager)
	{
		// if no external sheets exist -> return
		if (WEExternalSheetManager.getInstance().getExternalSheets().size() == 0)
		{
			return false;
		}

		// initalize sub menu manager
		IMenuManager subMenuManager = new MenuManager(
				PluginProperties.menu_ExternalSheet_label, "externalsheet"); //$NON-NLS-1$

		// add external sheets to sub menu
		for (Iterator iterator = WEExternalSheetManager.getInstance().getExternalSheets()
				.keySet().iterator(); iterator.hasNext();)
		{
			String externalSheetTitle = (String) iterator.next();
			ExternalSheetAction externalSheetAction = new ExternalSheetAction(
					externalSheetTitle);

			subMenuManager.add(externalSheetAction);
		}

		// add the external submenu to the menu manager
		menuManager.appendToGroup(IWorkbenchActionConstants.GROUP_ADD, subMenuManager);

		return true;
	}


	/**
	 * Add submenus to create new objects.
	 * 
	 * @param menuManager
	 *            The menu
	 */
	protected void addCreationSubMenusToPopup(IMenuManager menuManager)
	{
		MenuManager submenuManager = null;

		submenuManager = new MenuManager(PluginProperties.menu_CreateChild_item);
		populateManager(submenuManager, createChildActions, null);
		menuManager.appendToGroup(IWorkbenchActionConstants.GROUP_ADD, submenuManager);

		submenuManager = new MenuManager(PluginProperties.menu_CreateSibling_item);
		populateManager(submenuManager, createSiblingActions, null);
		menuManager.appendToGroup(IWorkbenchActionConstants.GROUP_ADD, submenuManager);
	}


	/**
	 * This generates a {@link org.eclipse.emf.edit.ui.action.CreateChildAction}
	 * for each object in <code>descriptors</code>, and returns the collection
	 * of these actions.
	 */
	protected Collection generateCreateChildActions(Collection descriptors,
			ISelection selection)
	{
		ArrayList actions = new ArrayList();
		if (descriptors != null)
		{
			actions.ensureCapacity(descriptors.size());
			for (Iterator i = descriptors.iterator(); i.hasNext();)
			{
				actions.add(new CreateChildAction(activeEditorPart, selection, i.next()));
			}
		}
		return actions;
	}


	/**
	 * This generates a
	 * {@link org.eclipse.emf.edit.ui.action.CreateSiblingAction} for each
	 * object in <code>descriptors</code>, and returns the collection of these
	 * actions.
	 */
	protected Collection generateCreateSiblingActions(Collection descriptors,
			ISelection selection)
	{
		ArrayList actions = new ArrayList();
		if (descriptors != null)
		{
			actions.ensureCapacity(descriptors.size());
			for (Iterator i = descriptors.iterator(); i.hasNext();)
			{
				actions
						.add(new CreateSiblingAction(activeEditorPart, selection, i
								.next()));
			}
		}
		return actions;
	}


	/**
	 * This populates the specified <code>manager</code> with
	 * {@link org.eclipse.jface.action.ActionContributionItem}s based on the
	 * {@link org.eclipse.jface.action.IAction}s contained in the
	 * <code>actions</code> collection, by inserting them before the specified
	 * contribution item <code>contributionID</code>. If <code>ID</code> is
	 * <code>null</code>, they are simply added.
	 */
	protected void populateManager(IContributionManager manager, Collection actions,
			String contributionID)
	{
		if (actions != null)
		{
			for (Iterator i = actions.iterator(); i.hasNext();)
			{
				IAction action = (IAction) i.next();
				if (contributionID != null)
				{
					manager.insertBefore(contributionID, action);
				}
				else
				{
					manager.add(action);
				}
			}
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.ui.part.EditorActionBarContributor#contributeToCoolBar(org
	 * .eclipse. jface.action.ICoolBarManager)
	 */
	@Override
	public void contributeToCoolBar(ICoolBarManager coolBarManager)
	{
		this.coolBarManager = coolBarManager;

		// zoom group
		addZoomGroupToCoolbar(coolBarManager);

		// external actions
		addExternalGroupCoolbar(coolBarManager);
	}


	/**
	 * This adds the following entries to the coolbar: The zoom combo box
	 * 
	 * @param toolBarManager
	 *            The coolBarManager
	 */
	private void addZoomGroupToCoolbar(ICoolBarManager coolBarManager)
	{
		// the group where the actions should be put
		ToolBarContributionItem zoomGroup = (ToolBarContributionItem) coolBarManager
				.find("org.eclipse.jwt.we.zoomBar"); //$NON-NLS-1$

		// add zoom combo to toolbar
		String[] zoomStrings = new String[]
		{ ZoomManager.FIT_ALL, ZoomManager.FIT_HEIGHT, ZoomManager.FIT_WIDTH };

		ZoomComboContributionItem zoomItem = new ZoomComboContributionItem(getPage(),
				zoomStrings);

		zoomGroup.getToolBarManager().insertAfter("org.eclipse.jwt.we.zoomBar.zoomIn", //$NON-NLS-1$
				zoomItem);

		ZoomControl.getInstance().setZoomItem(zoomItem);
	}


	/**
	 * This adds the following entries to the toolbar: Externally defined
	 * actions
	 * 
	 * @param toolBarManager
	 *            The toolBarManager
	 */
	private void addExternalGroupCoolbar(ICoolBarManager coolBarManager)
	{
		// the group where the actions should be put
		ToolBarContributionItem externalGroup = (ToolBarContributionItem) coolBarManager
				.find("org.eclipse.jwt.we.externalBar"); //$NON-NLS-1$

		// exit if there are no external actions (and remove external group)
		if (WEExternalActionsManager.getInstance().getExternalActions().size() == 0)
		{
			externalGroup.getToolBarManager().removeAll();

			return;
		}

		// the external actions are now added dynamically (see plugin.xml)
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.part.EditorActionBarContributor#dispose()
	 */
	@Override
	public void dispose()
	{
		super.dispose();

		// remove manually added coolbar group items
		ToolBarContributionItem zoomGroup = (ToolBarContributionItem) coolBarManager
				.find("org.eclipse.jwt.we.zoomBar"); //$NON-NLS-1$
		zoomGroup.getToolBarManager().remove(GEFActionConstants.ZOOM_TOOLBAR_WIDGET);
		zoomGroup.getToolBarManager().update(true);

		coolBarManager.update(true);

		coolBarManager = null;

		// disable zoom
		ZoomControl.getInstance().disableZoom();
	}

}
