//------------------------------------------------------------------------------
// 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.library.edit.process.command;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.edit.command.CreateChildCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.epf.common.serviceability.MsgBox;
import org.eclipse.epf.library.edit.LibraryEditPlugin;
import org.eclipse.epf.library.edit.LibraryEditResources;
import org.eclipse.epf.library.edit.command.MethodElementAddCommand;
import org.eclipse.epf.library.edit.navigator.ProcessPackageItemProvider;
import org.eclipse.epf.library.edit.ui.NewProcessComponentDialog;
import org.eclipse.epf.library.edit.ui.UserInteractionHelper;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.library.edit.validation.IValidatorFactory;
import org.eclipse.epf.uma.CapabilityPattern;
import org.eclipse.epf.uma.DeliveryProcess;
import org.eclipse.epf.uma.Element;
import org.eclipse.epf.uma.MethodConfiguration;
import org.eclipse.epf.uma.MethodLibrary;
import org.eclipse.epf.uma.MethodPackage;
import org.eclipse.epf.uma.MethodPlugin;
import org.eclipse.epf.uma.Process;
import org.eclipse.epf.uma.ProcessComponent;
import org.eclipse.epf.uma.ProcessFamily;
import org.eclipse.epf.uma.ProcessPackage;
import org.eclipse.epf.uma.UmaFactory;
import org.eclipse.epf.uma.UmaPackage;
import org.eclipse.epf.uma.util.ContentDescriptionFactory;
import org.eclipse.epf.uma.util.IMethodLibraryPersister;
import org.eclipse.epf.uma.util.UmaUtil;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;


/**
 * Executes the Create Process Component command.
 * 
 * @author Phong Nguyen Le
 * @author Kelvin Low
 * @since 1.0
 */
public class CreateProcessComponentCommand extends CreateChildCommand {

	private static final Collection ECLASSES = new HashSet();

	private Process process;

	private IStatus status;

	static {
		ECLASSES.add(UmaPackage.eINSTANCE.getMethodPackage());
	}

	/**
	 * Creates a new instance.
	 */
	public CreateProcessComponentCommand(EditingDomain domain, EObject owner,
			EStructuralFeature feature, Object child, int index,
			Collection selection, Helper helper) {
		super(domain, owner, feature, child, index, selection, helper);
	}

	public boolean canUndo() {
		return false;
	}

	public void execute() {
		MethodLibrary lib = UmaUtil.getMethodLibrary(owner);
		
		Shell shell = MsgBox.getDefaultShell();
		
		// The owner must be updatable.
		//
		status = UserInteractionHelper.checkModify(owner, shell);
		if (!status.isOK()) {
			return;
		}

		List configs = lib.getPredefinedConfigurations();
		List methodConfigs = new ArrayList();
		for (Iterator iter = configs.iterator(); iter.hasNext();) {
			Object element = iter.next();
			if (!(element instanceof ProcessFamily)) {
				methodConfigs.add(element);
			}
		}

		if (methodConfigs.isEmpty()) {
			// TODO: Return an IStatus object.
			LibraryEditPlugin.getDefault().getMsgDialog().displayError(
					LibraryEditResources.createProcess_text, //$NON-NLS-1$
					LibraryEditResources.noConfigError_msg); //$NON-NLS-1$
			return;
		}

		ProcessPackageItemProvider adapter = (ProcessPackageItemProvider) helper;

		List procClasses = new ArrayList();
		if (adapter.getProcessType() == UmaPackage.eINSTANCE
				.getProcessPlanningTemplate()) {
			procClasses.add(DeliveryProcess.class);
			procClasses.add(CapabilityPattern.class);
		}
		MethodPlugin plugin = UmaUtil.getMethodPlugin((Element) owner);		
		List baseProcList = TngUtil.getAvailableBaseProcesses(plugin,
				procClasses);

		MethodConfiguration[] procCtxs = new MethodConfiguration[methodConfigs
				.size()];
		methodConfigs.toArray(procCtxs);

		Process[] baseProcesses = new Process[baseProcList.size()];
		baseProcList.toArray(baseProcesses);

		final ProcessComponent procComp = (ProcessComponent) child;
		procComp.setProcess((Process) UmaFactory.eINSTANCE.create(adapter
				.getProcessType()));

		NewProcessComponentDialog dialog = new NewProcessComponentDialog(
				Display.getCurrent().getActiveShell(), procCtxs, procComp,
				baseProcesses, plugin, (ProcessPackage) owner);
		boolean canExecute = true;
		while (true) {
			int ret = dialog.open();
			if (ret == IDialogConstants.CANCEL_ID) {
				canExecute = false;
				break;
			}

			if (procComp.getProcess().getDefaultContext() == null) {
				LibraryEditPlugin
						.getDefault()
						.getMsgDialog()
						.displayError(
								LibraryEditResources.createProcess_text, //$NON-NLS-1$
								LibraryEditResources.noDefaultConfigError_msg); //$NON-NLS-1$
			} else {
				String msg = IValidatorFactory.INSTANCE.createNameValidator(
						owner, procComp).isValid(procComp.getName());
				if (msg == null) {
					process = procComp.getProcess();
					break;
				} else {
					LibraryEditPlugin
							.getDefault()
							.getMsgDialog()
							.displayError(
									LibraryEditResources.createProcess_text, //$NON-NLS-1$
									msg);
				}
			}
		}

		if (canExecute) {
			// create process component need to update the configuration that has been selected as
			// default context of its process. Check if the configuration file is updatable
			//
			status = UserInteractionHelper.checkModify(procComp.getProcess().getDefaultContext(), shell);
			if (!status.isOK()) {
				return;
			}

			super.execute();

			Command cmd = getCommand();
			if (cmd instanceof MethodElementAddCommand) {
				IStatus status = ((MethodElementAddCommand) cmd).getStatus();
				if (status != null && !status.isOK()) {
					this.status = status;
					return;
				}
			}

			final MethodConfiguration procCtx = process.getDefaultContext();
			
			// need to add the parent packages and plugin into the configuration as well
			// RATLC00385049 - New process in new plug-in not automatically visible in configuration view
			List pkgs = procCtx.getMethodPackageSelection();
			for (EObject obj = procComp; obj != null; obj = obj.eContainer()) {
				if (obj instanceof MethodPackage) {
					pkgs.add(obj);
				}
			}
			procCtx.getMethodPluginSelection().add(plugin);
			
			process.getValidContext().add(procCtx);
			process.setPresentation(ContentDescriptionFactory
					.createContentDescription(process));

			Runnable runnable = new Runnable() {
				public void run() {
					// save the resource of parent ProcessPackage
					Resource resource = ((EObject) owner).eResource();
					if (resource != null) {
						IMethodLibraryPersister.FailSafeMethodLibraryPersister persister = ContentDescriptionFactory
								.getMethodLibraryPersister()
								.getFailSafePersister();
						try {
							// save the resource of newly created
							// ProcessComponent again after creating process's
							// presentation
							persister.save(procComp.eResource());

							persister.save(resource);

							// save the resource of the process's default
							// context
							persister.save(procCtx.eResource());

							persister.commit();
						} catch (Exception e) {
							try {
								persister.rollback();
							} catch (Exception ex) {
								LibraryEditPlugin.INSTANCE.log(ex);
								LibraryEditPlugin.INSTANCE.log(e);
							}
							LibraryEditPlugin
									.getDefault()
									.getMsgDialog()
									.displayError(
											LibraryEditResources.createProcess_text, //$NON-NLS-1$
											LibraryEditResources.createProcessError_msg, //$NON-NLS-1$
											NLS.bind(LibraryEditResources.saveProcessError_reason, procComp.getName()), 
											e);
						}
					}
				}
			};

			UserInteractionHelper
					.runWithProgress(
							runnable,
							MessageFormat
									.format(
											LibraryEditResources.creatingProcessComponentTask_name, new Object[] { procComp.getName() })); //$NON-NLS-1$
		}
	}

	public void redo() {
		super.redo();
		ProcessComponent procComp = (ProcessComponent) child;
		MethodConfiguration procCtx = process.getDefaultContext();
		if (procCtx instanceof MethodConfiguration) {
			((MethodConfiguration) procCtx).getMethodPackageSelection().add(
					procComp);
		}
	}

	/**
	 * @see org.eclipse.emf.edit.command.CreateChildCommand#undo()
	 */
	public void undo() {
		MethodConfiguration procCtx = process.getDefaultContext();
		if (procCtx instanceof MethodConfiguration) {
			((MethodConfiguration) procCtx).getMethodPackageSelection().remove(
					child);
		}
		super.undo();
	}

	public IStatus getStatus() {
		return status;
	}
}
