//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation and others.
// 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:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.authoring.ui.views;

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

import org.eclipse.emf.common.command.BasicCommandStack;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.IDisposable;
import org.eclipse.emf.edit.ui.dnd.LocalTransfer;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.epf.authoring.ui.AuthoringUIHelpContexts;
import org.eclipse.epf.authoring.ui.AuthoringUIPlugin;
import org.eclipse.epf.authoring.ui.AuthoringUIResources;
import org.eclipse.epf.authoring.ui.PerspectiveListUtil;
import org.eclipse.epf.authoring.ui.UIActionDispatcher;
import org.eclipse.epf.authoring.ui.actions.ConfigurationViewEditAction;
import org.eclipse.epf.authoring.ui.actions.ILibraryActionBarContributor;
import org.eclipse.epf.authoring.ui.actions.LibraryActionBarContributor;
import org.eclipse.epf.authoring.ui.actions.LibraryViewFindElementAction;
import org.eclipse.epf.authoring.ui.dnd.LibraryViewerDragAdapter;
import org.eclipse.epf.authoring.ui.providers.ConfigurationLabelProvider;
import org.eclipse.epf.library.ILibraryManager;
import org.eclipse.epf.library.LibraryService;
import org.eclipse.epf.library.LibraryServiceUtil;
import org.eclipse.epf.library.configuration.ConfigurationFilter;
import org.eclipse.epf.library.edit.IFilter;
import org.eclipse.epf.library.edit.TngAdapterFactory;
import org.eclipse.epf.library.events.ILibraryChangeListener;
import org.eclipse.epf.library.ui.LibraryUIManager;
import org.eclipse.epf.library.ui.LibraryUIUtil;
import org.eclipse.epf.uma.MethodConfiguration;
import org.eclipse.epf.uma.MethodLibrary;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
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.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.HTMLTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;

/**
 * Displays the content of the current method configuration.
 * <p>
 * The current configuration is set via the Configuration combobox in the system
 * toolbar.
 * 
 * @author Jinhua Xi
 * @author Phong Nguyen Le
 * @author Kelvin Low
 * @since 1.0
 */
public class ConfigurationView extends AbstractBaseView implements
		ILibraryChangeListener {

	/**
	 * The view ID.
	 */
	public static final String VIEW_ID = ConfigurationView.class.getName();

	private String configName;

	private TreeViewer treeViewer;

	private IDoubleClickListener doubleClickListener;

	/**
	 * Creates a new instance.
	 */
	public ConfigurationView() {
	}

	/**
	 * @see org.eclipse.ui.part.ViewPart#init(IViewSite)
	 */
	public void init(IViewSite site) throws PartInitException {
		super.init(site);
		createActions(site);
		LibraryUIManager.getInstance().addConfigurationContribution();
	}

	/**
	 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(Composite)
	 */
	public void createPartControl(Composite parent) {
		super.createPartControl(parent);

		PlatformUI.getWorkbench().getHelpSystem().setHelp(parent,
				AuthoringUIHelpContexts.CONFIGURATION_VIEW_CONTEXT);

		editingDomain = new AdapterFactoryEditingDomain(adapterFactory,
				new BasicCommandStack());
		adapterFactory = (ComposedAdapterFactory) editingDomain
				.getAdapterFactory();

		// Open the current configuration.
		setConfiguration(null);

		// Add a library change listener.
		ILibraryManager manager = LibraryService.getInstance().getCurrentLibraryManager();
		if (manager != null) {
			manager.addListener(this);
		}
	}

	/**
	 * @see org.eclipse.epf.library.ILibraryServiceListener#libraryCreated(MethodLibrary)
	 */
	public void libraryCreated(MethodLibrary library) {
	}

	/**
	 * @see org.eclipse.epf.library.ILibraryServiceListener#libraryOpened(MethodLibrary)
	 */
	public void libraryOpened(MethodLibrary library) {
	}

	/**
	 * @see org.eclipse.epf.library.ILibraryServiceListener#libraryClosed(MethodLibrary)
	 */
	public void libraryClosed(MethodLibrary library) {
		setInputForViewer(null);
		if (adapterFactory instanceof ComposedAdapterFactory) {
			((ComposedAdapterFactory) adapterFactory).dispose();
		}
	}

	/**
	 * @see org.eclipse.epf.library.ILibraryServiceListener#librarySet(MethodLibrary)
	 */
	public void librarySet(MethodLibrary library) {
		setMethodConfiguration(null);
	}

	/**
	 * @see  org.eclipse.epf.library.events.ILibraryChangeListener#libraryChanged(int, Collection)
	 */	
	public void libraryChanged(int option, Collection changedItems) {
		switch (option) {
		case ILibraryChangeListener.OPTION_DELETED:
			if (changedItems != null) {
				for (Iterator it = changedItems.iterator(); it.hasNext();) {
					Object element = it.next();
					if (element instanceof MethodConfiguration) {
						if (((MethodConfiguration) element).getName().equals(
								getCurrentConfig())) {
							setMethodConfiguration(null);
						}
					}
				}
			}
			break;
		}
	}

	/**
	 * @see org.eclipse.epf.library.ILibraryServiceListener#configurationSet(MethodConfiguration)
	 */
	public void configurationSet(MethodConfiguration config) {
		setMethodConfiguration(config);
	}

	public String getCurrentConfig() {
		return configName;
	}

	public void setFocus() {
		actionBarContributor.setActiveView(this);

		IStructuredSelection selected = (IStructuredSelection) getSelection();
		if (selected.isEmpty())
			actionBarContributor.disableGlobalEditMenu();
		else
			actionBarContributor.enableGlobalEditMenu();

		if (treeViewer != null) {
			treeViewer.getControl().setFocus();
		}
	}

	/**
	 * Creates the viewer.
	 */
	public void createViewer(Composite parent) {
		// Create the controls.
		Composite content = new Composite(parent, SWT.NONE);
		content.setLayoutData(new GridData(GridData.FILL_BOTH));
		GridLayout layout = new GridLayout();
		content.setLayout(layout);

		treeViewer = new TreeViewer(content);
		treeViewer.getTree().setLayoutData(new GridData(GridData.FILL_BOTH));

		// Add drag-and-drop support.
		int dndOperations = DND.DROP_COPY | DND.DROP_MOVE;
		Transfer[] transfers = new Transfer[] { HTMLTransfer.getInstance(),
				LocalTransfer.getInstance() };
		treeViewer.addDragSupport(dndOperations, transfers,
				new LibraryViewerDragAdapter(treeViewer));

		// Add a double click listener.
		doubleClickListener = new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				UIActionDispatcher.getInstance().handleDoubleClickEvent(event);
			}
		};
		treeViewer.addDoubleClickListener(doubleClickListener);

		createContextMenuFor(treeViewer);
	}

	/**
	 * Creates the actions.
	 */
	public void createActions(IViewSite viewSite) {
		IAction editAction = new Action(AuthoringUIResources.actionLabel_edit) { //$NON-NLS-1$
			public void run() {
				UIActionDispatcher.editConfiguration(configName);
			}
		};
		editAction.setToolTipText(AuthoringUIResources.editConfiguration_tooltip); //$NON-NLS-1$
	}

	private void reset() {
		// Prevent memory leak.
		configName = null;
		if (adapterFactory != null) {
			if (adapterFactory instanceof IDisposable) {
				((IDisposable) adapterFactory).dispose();
			}
			adapterFactory = null;
		}
		editingDomain.setAdapterFactory(null);
		IContentProvider contentProvider = treeViewer.getContentProvider();
		if (contentProvider != null) {
			contentProvider.dispose();
		}
		IBaseLabelProvider labelProvider = treeViewer.getLabelProvider();
		if (labelProvider != null) {
			labelProvider.dispose();
		}
		StructuredSelection emptySelection = new StructuredSelection();
		actionDispatcher.setSelection(emptySelection);
		UIActionDispatcher.getInstance().setSelection(emptySelection);
	}
	
	public void setMethodConfiguration(MethodConfiguration config) {
		Control ctrl = this.getViewer().getControl();
		if (ctrl == null || ctrl.isDisposed()) {
			return;
		}

		if (treeViewer.getInput() == config)
			return;
		String title = ""; //$NON-NLS-1$

		if (config != null) {
			this.configName = config.getName();
			IFilter configFilter = new ConfigurationFilter(config, treeViewer);
			adapterFactory = TngAdapterFactory.INSTANCE
					.getConfigurationView_AdapterFactory(configFilter);
			editingDomain.setAdapterFactory(adapterFactory);
			treeViewer.setContentProvider(new AdapterFactoryContentProvider(
					adapterFactory));
			treeViewer.setLabelProvider(new ConfigurationLabelProvider(config,
					adapterFactory));
			title = config.getName();
		} else {
			// Prevent memory leak.
			configName = null;
			if (adapterFactory != null) {
				if (adapterFactory instanceof IDisposable) {
					((IDisposable) adapterFactory).dispose();
				}
				adapterFactory = null;
			}
			editingDomain.setAdapterFactory(null);
			IContentProvider contentProvider = treeViewer.getContentProvider();
			if (contentProvider != null) {
				contentProvider.dispose();
			}
			IBaseLabelProvider labelProvider = treeViewer.getLabelProvider();
			if (labelProvider != null) {
				labelProvider.dispose();
			}
			StructuredSelection emptySelection = new StructuredSelection();
			actionDispatcher.setSelection(emptySelection);
			UIActionDispatcher.getInstance().setSelection(emptySelection);
		}

		treeViewer.setInput(config);
		setContentDescription(title);
	}

	public void setConfiguration(String name) {
		configName = name;

		if (name == null) {
			MethodConfiguration config = LibraryService.getInstance()
					.getCurrentMethodConfiguration();
			if (config != null) {
				configName = config.getName();
			}
		}

		if (configName != null) {
			MethodConfiguration config = LibraryServiceUtil
					.getMethodConfiguration(LibraryService.getInstance()
							.getCurrentMethodLibrary(), configName);
			if (config != null) {
				setMethodConfiguration(config);
				return;
			}
		}

		this.configName = null;
	}

	public void dispose() {
		super.dispose();
		if (doubleClickListener != null) {
			treeViewer.removeDoubleClickListener(doubleClickListener);
		}
	}

	public static ConfigurationView getView() {
		try {
			IWorkbenchPage activePage = PlatformUI.getWorkbench()
					.getActiveWorkbenchWindow().getActivePage();
			if (activePage != null) {
				IViewPart view = activePage.findView(VIEW_ID);
				if (view == null) {
					view = activePage.showView(VIEW_ID);
				}
				return (ConfigurationView) view;
			}

		} catch (Exception e) {
			AuthoringUIPlugin.getDefault().getLogger().logError(e);
		}

		return null;
	}

	/**
	 * @see org.eclipse.epf.authoring.ui.views.AbstractBaseView#getViewer()
	 */
	public Viewer getViewer() {
		return treeViewer;
	}

	private class ConfigurationViewActionBarContributor extends
			LibraryActionBarContributor {

		private LibraryViewFindElementAction libraryViewFindElementAction;

		public ConfigurationViewActionBarContributor(EditingDomain editingDomain) {
			super(editingDomain);
		}

		/**
		 * @see org.eclipse.epf.authoring.ui.actions.LibraryActionBarContributor#init(IActionBars)
		 */
		public void init(IActionBars actionBars) {
			viewActionBars = actionBars;
			configViewEditAction = new ConfigurationViewEditAction(
					AuthoringUIResources.actionLabel_edit); //$NON-NLS-1$
			actionBars.setGlobalActionHandler(
					"org.eclipse.epf.authoring.ui.actions.openElement", //$NON-NLS-1$
					configViewEditAction);
			libraryViewFindElementAction = new LibraryViewFindElementAction(
					AuthoringUIResources.actionLabel_findElementInLibNav); //$NON-NLS-1$
			actionBars.setGlobalActionHandler(
					LibraryViewFindElementAction.ACTION_ID, //$NON-NLS-1$
					libraryViewFindElementAction);
		}

		/**
		 * @see org.eclipse.epf.authoring.ui.actions.LibraryActionBarContributor#activate()
		 */
		public void activate() {
			activeViewPart.addPropertyListener(this);
			ISelectionProvider selectionProvider = activeViewPart instanceof ISelectionProvider ? (ISelectionProvider) activeViewPart
					: activeViewPart.getSite().getSelectionProvider();
			selectionProvider.addSelectionChangedListener(configViewEditAction);
			selectionProvider
					.addSelectionChangedListener(libraryViewFindElementAction);
			update();
		}

		/**
		 * @see LibraryActionBarContributor#deactivate()
		 */
		public void deactivate() {
			activeViewPart.removePropertyListener(this);
			ISelectionProvider selectionProvider = activeViewPart instanceof ISelectionProvider ? (ISelectionProvider) activeViewPart
					: activeViewPart.getSite().getSelectionProvider();
			selectionProvider
					.removeSelectionChangedListener(configViewEditAction);
			selectionProvider
					.removeSelectionChangedListener(libraryViewFindElementAction);
		}

		/**
		 * @see LibraryActionBarContributor#update()
		 */
		public void update() {
			ISelectionProvider selectionProvider = activeViewPart instanceof ISelectionProvider ? (ISelectionProvider) activeViewPart
					: activeViewPart.getSite().getSelectionProvider();
			ISelection selection = selectionProvider.getSelection();
			IStructuredSelection structuredSelection = selection instanceof IStructuredSelection ? (IStructuredSelection) selection
					: StructuredSelection.EMPTY;
			configViewEditAction.selectionChanged(structuredSelection);
			libraryViewFindElementAction.selectionChanged(structuredSelection);
		}

		/**
		 * @see LibraryActionBarContributor#selectionChanged(SelectionChangedEvent)
		 */
		public void selectionChanged(SelectionChangedEvent event) {
		}

		/**
		 * @see LibraryActionBarContributor#disableGlobalEditMenu()
		 */
		public void disableGlobalEditMenu() {
			configViewEditAction.setEnabled(false);
		}

		/**
		 * @see LibraryActionBarContributor#enableGlobalEditMenu()
		 */
		public void enableGlobalEditMenu() {
			configViewEditAction.setEnabled(true);
		}

		/**
		 * @see LibraryActionBarContributor#menuAboutToShow(IMenuManager)
		 */
		public void menuAboutToShow(IMenuManager menuManager) {
			// Add our standard markers.
			menuManager.add(new Separator("additions")); //$NON-NLS-1$
			menuManager.add(new Separator("edit")); //$NON-NLS-1$

			// Add our other standard marker.
			menuManager.add(new Separator("additions-end")); //$NON-NLS-1$

			addGlobalActions(menuManager);

			menuManager.add(new Separator());
			menuManager.add(new ActionContributionItem(configViewEditAction));
			if (PerspectiveListUtil.isAuthoringPerspective()) {
				menuManager.add(new ActionContributionItem(
						libraryViewFindElementAction));
			}
		}
	}

	/**
	 * @see org.eclipse.epf.authoring.ui.views.AbstractBaseView#createActionBarContributor()
	 */
	public ILibraryActionBarContributor createActionBarContributor() {
		return new ConfigurationViewActionBarContributor(editingDomain);
	}

	/**
	 * @see org.eclipse.epf.authoring.ui.views.AbstractBaseView#setInputForViewer(Object)
	 */
	public void setInputForViewer(Object model) {
		if ((model == null) || (model instanceof MethodConfiguration)) {
			setMethodConfiguration((MethodConfiguration) model);
		} else {
			reset();
		}
	}
	
}
