//------------------------------------------------------------------------------
// 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.editors;

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

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
import org.eclipse.epf.authoring.ui.AuthoringUIPlugin;
import org.eclipse.epf.authoring.ui.AuthoringUIResources;
import org.eclipse.epf.authoring.ui.views.ViewHelper;
import org.eclipse.epf.common.serviceability.MsgDialog;
import org.eclipse.epf.library.LibraryPlugin;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.library.util.LibraryUtil;
import org.eclipse.epf.uma.ContentPackage;
import org.eclipse.epf.uma.CustomCategory;
import org.eclipse.epf.uma.DescribableElement;
import org.eclipse.epf.uma.Discipline;
import org.eclipse.epf.uma.DisciplineGrouping;
import org.eclipse.epf.uma.Domain;
import org.eclipse.epf.uma.Guidance;
import org.eclipse.epf.uma.MethodConfiguration;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.MethodLibrary;
import org.eclipse.epf.uma.MethodPlugin;
import org.eclipse.epf.uma.Process;
import org.eclipse.epf.uma.ProcessComponent;
import org.eclipse.epf.uma.Role;
import org.eclipse.epf.uma.RoleSet;
import org.eclipse.epf.uma.RoleSetGrouping;
import org.eclipse.epf.uma.Task;
import org.eclipse.epf.uma.Tool;
import org.eclipse.epf.uma.WorkProduct;
import org.eclipse.epf.uma.WorkProductType;
import org.eclipse.epf.uma.util.UmaUtil;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;

/**
 * Launches a Method editor that is appropriate for a given method element.
 * 
 * @author Shilpa Toraskar
 * @author Kelvin Low
 * @since 1.0
 */
public class EditorChooser implements IEditorKeeper {

	private static EditorChooser singleton = null;

	public static EditorChooser getInstance() {
		if (singleton == null) {
			synchronized (EditorChooser.class) {
				if (singleton == null) {
					singleton = new EditorChooser();
					IEditorKeeper.REFERENCE.setEditorKeeper(singleton);
				}
			}
		}
		return singleton;
	}

	/**
	 * Opens the respective editor depending on the given Method element object.
	 * 
	 * @param obj
	 *            A Method element.
	 */
	public void openEditor(Object obj) {
		try {
			obj = ViewHelper.handleDangling(obj);
			if (obj == null)
				return;

			obj = LibraryUtil.unwrap(obj);

			ArrayList errors = new ArrayList();
			if (obj instanceof EObject) {
				Resource res = ((EObject) obj).eResource();
				if (res != null && !res.getErrors().isEmpty()) {
					errors.addAll(res.getErrors());
				}
				if (obj instanceof DescribableElement) {
					Resource presRes = ((DescribableElement) obj)
							.getPresentation().eResource();
					if (presRes != null && !presRes.getErrors().isEmpty()) {
						errors.addAll(presRes.getErrors());
					}
				}
				if (!errors.isEmpty()) {
					final MultiStatus multiStatus = new MultiStatus(
							AuthoringUIPlugin.getDefault().getId(),
							0,
							AuthoringUIResources.EditorChooser_ResourcesError, null); //$NON-NLS-1$
					for (Iterator iter = errors.iterator(); iter.hasNext();) {
						Diagnostic e = (Diagnostic) iter.next();
						IStatus status = new Status(IStatus.WARNING,
								LibraryPlugin.getDefault().getId(), 0, e
										.getMessage(), null);
						multiStatus.add(status);
					}
					try {
						if (AuthoringUIPlugin
								.getDefault()
								.getMsgDialog()
								.displayConfirmation(
										AuthoringUIResources.EditorChooser_loaddErrorDlgTitle, //$NON-NLS-1$
										AuthoringUIResources.EditorChooser_ResourcesError, //$NON-NLS-1$
										multiStatus) == Dialog.CANCEL) {
							return;
						}

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

			if ((obj instanceof MethodPlugin)
					|| (obj instanceof ContentPackage) || (obj instanceof Role)
					|| (obj instanceof Task) || (obj instanceof WorkProduct)
					|| (obj instanceof Guidance) || (obj instanceof Discipline)
					|| (obj instanceof DisciplineGrouping)
					|| (obj instanceof Domain)
					|| (obj instanceof WorkProductType)
					|| (obj instanceof RoleSet) || (obj instanceof Tool)
					|| (obj instanceof RoleSetGrouping)
					|| (obj instanceof MethodLibrary)) {
				openEditor((MethodElement) obj, MethodElementEditor.EDITOR_ID);
			} else if (obj instanceof CustomCategory) {
				CustomCategory custCat = (CustomCategory) obj;
				if(TngUtil.isRootCustomCategory(custCat)) {
					return;
				}
				openEditor((MethodElement) obj, MethodElementEditor.EDITOR_ID);
			} else if (obj instanceof ProcessComponent) {
				openEditor((MethodElement) obj, ProcessEditor.EDITOR_ID);

				// open properties view by default when we open Process Editor
				try {
					PlatformUI.getWorkbench().getActiveWorkbenchWindow()
							.getActivePage().showView(
									"org.eclipse.ui.views.PropertySheet", null, IWorkbenchPage.VIEW_VISIBLE); //$NON-NLS-1$
				} catch (PartInitException exception) {
					AuthoringUIPlugin.getDefault().getLogger().logError(
							exception);
				}
			} else if (obj instanceof Process) {
				Object container = ((Process) obj).eContainer();
				if (container instanceof ProcessComponent) {
					openEditor((MethodElement) container,
							ProcessEditor.EDITOR_ID);
				}
			} else if (obj instanceof MethodConfiguration) {
				openEditor((MethodConfiguration) obj,
						ConfigurationEditor.EDITOR_ID);
			}
		} catch (Throwable t) {
			t.printStackTrace();
			String title = AuthoringUIResources.editors_EditorChooser_systemErrorDialog_title; //$NON-NLS-1$
			String message = AuthoringUIResources.editors_EditorChooser_systemErrorDialog_message; //$NON-NLS-1$
			String reason = AuthoringUIResources.editors_EditorChooser_systemErrorDialog_reason; //$NON-NLS-1$
			MsgDialog dialog = AuthoringUIPlugin.getDefault().getMsgDialog();
			dialog.displayError(title, message, reason, t);
		}
	}

	/**
	 * Opens the Configuration editor.
	 * 
	 * @param config
	 * @param editorId
	 */
	private void openEditor(MethodConfiguration config, String editorId)
			throws PartInitException {
		ConfigurationEditorInput editorInput = new ConfigurationEditorInput(
				config);
		AuthoringUIPlugin.getDefault().getWorkbench()
				.getActiveWorkbenchWindow().getActivePage().openEditor(
						editorInput, editorId);
	}

	/**
	 * Opens the Method editor for the given method element.
	 * 
	 * @param element
	 *            A Method element.
	 * @param editorId
	 *            The editor ID.
	 */
	private void openEditor(MethodElement element, String editorId)
			throws PartInitException {
		MethodElementEditorInput editorInput = new MethodElementEditorInput(
				element);
		AuthoringUIPlugin.getDefault().getWorkbench()
				.getActiveWorkbenchWindow().getActivePage().openEditor(
						editorInput, editorId);
	}

	/**
	 * Selects (brings to front, but does not give focus) the Method editor for
	 * the given object. If there is no open editor for the object, does
	 * nothing.
	 * 
	 * @param obj
	 */
	public void selectEditor(Object obj) {
		try {
			obj = ViewHelper.handleDangling(obj);
			if (obj == null)
				return;
			obj = LibraryUtil.unwrap(obj);
		} catch (Throwable t) {
			t.printStackTrace();
		}
		IEditorInput editorInput = null;
		if (obj instanceof MethodConfiguration)
			editorInput = new ConfigurationEditorInput(
					(MethodConfiguration) obj);
		else if (obj instanceof MethodElement)
			editorInput = new MethodElementEditorInput((MethodElement) obj);
		if (editorInput != null) {
			IWorkbenchPage page = AuthoringUIPlugin.getDefault().getWorkbench()
					.getActiveWorkbenchWindow().getActivePage();
			IEditorPart editorPart = page.findEditor(editorInput);
			if (editorPart != null)
				page.bringToTop(editorPart);
		}
	}

	/**
	 * 
	 * @return The object which the currently selected editor is editing (or
	 *         null).
	 */
	public Object getSelectedEditorInput() {
		IEditorPart editorPart = AuthoringUIPlugin.getDefault().getWorkbench()
				.getActiveWorkbenchWindow().getActivePage().getActiveEditor();
		if (editorPart != null) {
			IEditorInput editorInput = editorPart.getEditorInput();
			if (editorInput instanceof ConfigurationEditorInput)
				return ((ConfigurationEditorInput) editorInput)
						.getConfiguration();
			if (editorInput instanceof MethodElementEditorInput)
				return ((MethodElementEditorInput) editorInput)
						.getMethodElement();
		}
		return null;
	}

	/**
	 * Closes the Method editor for the given Method element.
	 * 
	 * @param obj
	 *            A Method element.
	 */
	public void closeEditor(Object obj) {
		try {
			MethodElementEditorInput editorInput = new MethodElementEditorInput(
					(MethodElement) obj);
			IEditorPart editor = AuthoringUIPlugin.getDefault().getWorkbench()
					.getActiveWorkbenchWindow().getActivePage().findEditor(
							editorInput);
			if (editor != null) {
				AuthoringUIPlugin.getDefault().getWorkbench()
						.getActiveWorkbenchWindow().getActivePage()
						.closeEditor(editor, false);
			}
		} catch (RuntimeException e) {
			e.printStackTrace();
		}
	}

	public Collection getElementsWithDirtyEditor(Collection elements) {
		ArrayList result = new ArrayList();

		IWorkbenchPage workbenchPage = AuthoringUIPlugin.getDefault()
				.getWorkbench().getActiveWorkbenchWindow().getActivePage();
		IEditorReference[] editorReferences = workbenchPage
				.getEditorReferences();
		// ArrayList closeEditorRefs = new ArrayList();
		for (int i = 0; i < editorReferences.length; i++) {
			IEditorReference reference = editorReferences[i];
			IEditorPart editor = reference.getEditor(true);
			if (editor != null && editor.isDirty()) {
				IEditorInput input = editor.getEditorInput();
				MethodElement element = null;
				if (input instanceof MethodElementEditorInput) {
					element = ((MethodElementEditorInput) input)
							.getMethodElement();
				} else if (input instanceof ConfigurationEditorInput) {
					element = ((ConfigurationEditorInput) input)
							.getConfiguration();
				}
				if (element != null) {
					for (Iterator iter = elements.iterator(); iter.hasNext();) {
						Object e = iter.next();
						if (e == element || UmaUtil.isContainedBy(element, e)) {
							result.add(element);
							iter.remove();
						}
					}
				}
			}
		}

		return result;
	}

	/**
	 * Closes the all open Method editors ofor the given (deleted) element and
	 * all of its children and grand children.
	 * 
	 * @param e
	 *            the deleted MethodElement
	 */
	public void closeEditorsOnDeletion(Object e) {
		IWorkbenchPage workbenchPage = AuthoringUIPlugin.getDefault()
				.getWorkbench().getActiveWorkbenchWindow().getActivePage();
		IEditorReference[] editorReferences = workbenchPage
				.getEditorReferences();
		ArrayList closeEditorRefs = new ArrayList();
		for (int i = 0; i < editorReferences.length; i++) {
			IEditorReference reference = editorReferences[i];
			IEditorPart editor = reference.getEditor(true);
			if (editor != null) {
				IEditorInput input = editor.getEditorInput();
				MethodElement element = null;
				if (input instanceof MethodElementEditorInput) {
					element = ((MethodElementEditorInput) input)
							.getMethodElement();
				} else if (input instanceof ConfigurationEditorInput) {
					element = ((ConfigurationEditorInput) input)
							.getConfiguration();
				}
				if (element != null
						&& (element.eContainer() == null || UmaUtil
								.isContainedBy(element, e))) {
					closeEditorRefs.add(reference);
				}
			}
		}
		int size = closeEditorRefs.size();
		IEditorReference[] references = new IEditorReference[size];
		for (int i = 0; i < size; i++) {
			references[i] = (IEditorReference) closeEditorRefs.get(i);
		}
		workbenchPage.closeEditors(references, false);
	}

}