/**
 * File:    ExportTemplateWizardPage.java
 * Created: 10.02.2007
 *
 *
/*******************************************************************************
 * 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 
 *    	- initial API and implementation
 *******************************************************************************/

package org.eclipse.jwt.we.misc.wizards.template.exp;

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

import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.jwt.meta.model.core.GraphicalElement;
import org.eclipse.jwt.meta.model.core.impl.CorePackageImpl;
import org.eclipse.jwt.meta.model.processes.Activity;
import org.eclipse.jwt.meta.model.processes.ActivityLinkNode;
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.actions.handlers.TemplateHandler;
import org.eclipse.jwt.we.misc.logging.Logger;
import org.eclipse.jwt.we.misc.util.EMFHelper;
import org.eclipse.jwt.we.misc.util.GeneralHelper;
import org.eclipse.jwt.we.misc.views.LayoutDataManager;
import org.eclipse.jwt.we.misc.views.Views;
import org.eclipse.jwt.we.misc.wizards.WizardHelper;
import org.eclipse.jwt.we.misc.wizards.template.TemplateFilter;
import org.eclipse.jwt.we.model.view.Reference;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.dialogs.ContainerCheckedTreeViewer;

/**
 * This is a wizard page where parts of a model can be selected for export.
 * 
 * @version $Id: ExportTemplateWizardPage.java,v 1.8 2009/11/04 17:18:47 chsaad
 *          Exp $
 * @author Christian Saad (csaad@gmx.net), Programming distributed Systems Lab,
 *         University of Augsburg, Germany, www.ds-lab.org
 */
public class ExportTemplateWizardPage extends WizardPage
{

	/**
	 * A Logger.
	 */
	static final Logger logger = Logger.getLogger(ExportTemplateWizardPage.class);

	/**
	 * The TextField for the path of the template file.
	 */
	protected Text templateField;

	/**
	 * The Checkbox for the comments.
	 */
	private Button commentCheckbox;

	/**
	 * The treeviewer for the package selection
	 */
	protected CheckboxTreeViewer packageViewer;


	/**
	 * Pass in the selection.
	 * 
	 * @param pageId
	 *            The name of the page.
	 * @param workbench
	 *            The workbench.
	 */
	public ExportTemplateWizardPage(String pageId, IWorkbench workbench)
	{
		super(pageId);
	}


	/**
	 * @return The EMF EditingDomain.
	 */
	public EditingDomain getEditingDomain()
	{
		return (EditingDomain) GeneralHelper.getActiveInstance().getAdapter(
				EditingDomain.class);
	}


	/**
	 * @return The {@link AdapterFactory} from an
	 *         {@link AdapterFactoryEditingDomain} if available.
	 */
	public AdapterFactory getAdapterFactory()
	{
		return (AdapterFactory) GeneralHelper.getActiveInstance().getAdapter(
				AdapterFactory.class);
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
	 * .Composite )
	 */
	public void createControl(Composite parent)
	{
		// Composite
		Composite composite = new Composite(parent, SWT.NONE);
		composite.setLayout(new GridLayout(5, false));
		composite.setLayoutData(WizardHelper.createData(GridData.FILL, GridData.FILL,
				true, false, -1, -1));

		// PackageTree
		Tree packageTree = new Tree(composite, SWT.CHECK);
		packageTree.setLayoutData(WizardHelper.createData(GridData.FILL, GridData.FILL,
				true, true, -1, 5));
		// The XMI element
		final XMIResourceImpl xmires;
		if (getEditingDomain().getResourceSet().getResources().get(0) instanceof XMIResourceImpl)
		{
			xmires = (XMIResourceImpl) getEditingDomain().getResourceSet().getResources()
					.get(0);
		}
		else
		{
			xmires = null;
		}

		// PackageViewer
		final ContainerCheckedTreeViewer packageViewer = new ContainerCheckedTreeViewer(
				packageTree);
		packageViewer.setContentProvider(new AdapterFactoryContentProvider(
				getAdapterFactory()));
		packageViewer.setLabelProvider(new AdapterFactoryLabelProvider(
				getAdapterFactory()));
		packageViewer.setInput(getEditingDomain().getResourceSet().getResources().get(0)
				.getContents().get(0));

		if (xmires != null)
		{
			packageViewer.setGrayed(xmires, true);
		}

		packageViewer.addFilter(new TemplateFilter());
		this.packageViewer = packageViewer;

		// (Un)check sub/supertree
		packageViewer.addCheckStateListener(new ICheckStateListener()
		{

			public void checkStateChanged(CheckStateChangedEvent event)
			{
				// Set the state of the top element
				checkXMI(event.getElement(), xmires);

				setPageComplete(validatePage());
			}
		});

		// Comment Checkbox
		commentCheckbox = new Button(composite, SWT.CHECK);
		commentCheckbox.setText(PluginProperties.wizards_ExportComments_label);
		commentCheckbox.setLayoutData(WizardHelper.createData(-1, GridData.BEGINNING,
				false, false, -1, 3));
		commentCheckbox.setSelection(true);

		// Expand All Button
		Button expandButton = new Button(composite, SWT.PUSH);
		expandButton.setLayoutData(WizardHelper.createData(-1, GridData.END, false,
				false, -1, 1));
		expandButton.setImage(Plugin.getDefault().getFactoryRegistry().getImageFactory()
				.getImage(
						Plugin.getInstance().getFactoryRegistry().getImageFactory(
								Views.getInstance().getSelectedView())
								.createImageDescriptor("menu_expandall.gif") //$NON-NLS-1$
				));
		expandButton.addSelectionListener(new SelectionAdapter()
		{

			@Override
			public void widgetSelected(SelectionEvent event)
			{
				packageViewer.expandAll();
			}

		});

		// Collapse All Button
		Button collapseButton = new Button(composite, SWT.PUSH);
		collapseButton.setLayoutData(WizardHelper.createData(-1, GridData.BEGINNING,
				false, false, -1, 1));

		collapseButton.setImage(Plugin.getDefault().getFactoryRegistry()
				.getImageFactory().getImage(
						Plugin.getInstance().getFactoryRegistry().getImageFactory(
								Views.getInstance().getSelectedView())
								.createImageDescriptor("menu_collapseall.gif"))); //$NON-NLS-1$
		collapseButton.addSelectionListener(new SelectionAdapter()
		{

			@Override
			public void widgetSelected(SelectionEvent event)
			{
				packageViewer.expandAll();
				packageViewer.collapseAll();
			}

		});

		// Dummy Label
		Label dummyLabel = new Label(composite, SWT.LEFT);
		dummyLabel.setLayoutData(WizardHelper.createData(-1, -1, false, false, -1, 5));

		// Filename Textfield
		Label templateLabel = new Label(composite, SWT.RIGHT);
		templateLabel.setLayoutData(WizardHelper.createData(-1, GridData.BEGINNING,
				false, false, -1, 1));
		templateLabel.setText(PluginProperties.wizards_ExportTemplate_label);
		templateField = new Text(composite, SWT.BORDER);
		templateField.setLayoutData(WizardHelper.createData(-1, GridData.FILL, false,
				true, -1, 2));
		templateField.addModifyListener(new ModifyListener()
		{

			public void modifyText(ModifyEvent e)
			{
				setPageComplete(validatePage());
			}
		});

		// Browse Button
		Button browseButton = new Button(composite, SWT.PUSH);
		browseButton.setLayoutData(WizardHelper.createData(-1, GridData.END, false,
				false, -1, 2));
		browseButton.setText(PluginProperties.wizards_Browse_label);
		browseButton.addSelectionListener(new SelectionAdapter()
		{

			@Override
			public void widgetSelected(SelectionEvent event)
			{
				String fileExtension = WEEditor.getTemplateExtension();
				String fileExtensionName = PluginProperties.wizards_Files_templatefiles;
				String filePath = TemplateHandler.saveFilePathDialog(getShell(),
						fileExtension, fileExtensionName, SWT.SAVE);
				if (filePath != null)
				{
					if (!filePath.endsWith("." + fileExtension)) //$NON-NLS-1$
					{
						filePath = filePath + "." + fileExtension; //$NON-NLS-1$
					}
					templateField.setText(filePath);
				}
			}
		});

		// Validate
		setPageComplete(validatePage());
		setMessage(null);
		setControl(composite);

		// refresh packageviewer display
		getShell().pack();
		packageViewer.refresh();
		packageViewer.expandAll();
		packageViewer.collapseAll();
		packageViewer.expandToLevel(2);

		// make dialog bigger and center
		WizardHelper.resizeDialog(getShell());
	}


	/**
	 * Set the state of the XMI top element
	 * 
	 * @param element
	 *            The element
	 * @param xmires
	 *            The XMI Resource
	 */
	protected void checkXMI(Object element, XMIResourceImpl xmires)
	{
		// if xmi element was checked -> uncheck
		if (element instanceof XMIResourceImpl)
		{
			packageViewer.setGrayed(xmires, true);
		}

		// if any other element was checked -> also check xmi element
		else if (xmires != null)
		{
			packageViewer.setGrayed(xmires, true);
		}
	}


	/**
	 * Validate the Page (a filename must be selected)
	 * 
	 * @return Returns if the page is valid
	 */
	public boolean validatePage()
	{
		// disable "next" button if user has switched back to first page
		if (getContainer().getCurrentPage() != this)
		{
			return false;
		}

		URI fileURI = getFileURI();
		if (fileURI == null || fileURI.isEmpty())
		{
			setMessage(PluginProperties.wizards_ExportFilename_error);
			return false;
		}

		if (packageViewer.getCheckedElements().length < 1)
		{
			setMessage(PluginProperties.wizards_ExportChecked_error);
			return false;
		}

		setMessage(null);
		return true;
	}


	/**
	 * Changes the visibility and sets the focus on the filename textfield
	 * 
	 * @param visible
	 *            The Visibility
	 */
	@Override
	public void setVisible(boolean visible)
	{
		super.setVisible(visible);
		if (visible)
		{
			templateField.setFocus();
		}
	}


	/**
	 * Select the filename textfield
	 */
	public void selectFileField()
	{
		templateField.selectAll();
		templateField.setFocus();
	}


	/**
	 * @return The entered URI.
	 */
	public URI getFileURI()
	{
		try
		{
			return URI.createFileURI(templateField.getText());
		}
		catch (Exception exception)
		{
		}
		return null;
	}


	/**
	 * Returns a copy of the selected resources. The elements are copied and the
	 * necessary references in the copied list are set according to the original
	 * elements.
	 * 
	 * @return The selected resources
	 */
	@SuppressWarnings("unchecked")
	public Collection<EObject> getResources()
	{
		HashSet<EObject> originalElements = new HashSet();

		// (1a) the main model element
		originalElements.add((EObject) GeneralHelper.getActiveInstance().getModel());

		// (1b) the main diagram element
		originalElements
				.add((EObject) GeneralHelper.getActiveInstance().getDiagramData());

		// (2) + the selected elements
		Object[] checkedelements = packageViewer.getCheckedElements();
		for (int i = 0; i < checkedelements.length; i++)
		{
			if (checkedelements[i] instanceof EObject)
			{
				originalElements.add((EObject) checkedelements[i]);
			}
		}

		// (3) + the children of the selected activities (and their children,
		// and the
		// children's children...)
		HashSet<EObject> activityChildren = new HashSet();
		for (EObject eobject : originalElements)
		{
			if (eobject instanceof Activity)
			{
				for (Iterator iterator = EcoreUtil.getAllContents(eobject, true); iterator
						.hasNext();)
				{
					EObject child = (EObject) iterator.next();

					// add childs of the activity
					activityChildren.add(child);

					// add layoutdatas of the childs
					if (child instanceof GraphicalElement)
					{
						activityChildren.addAll(LayoutDataManager
								.getLayoutDataForGraphicalElement(GeneralHelper
										.getActiveInstance(), (GraphicalElement) child));
					}
				}

				// add referenceedges for this activity
				activityChildren.addAll(EMFHelper.getReferenceEdgesForScope(GeneralHelper
						.getActiveInstance(), (Activity) eobject));

				// add references for this activity
				Collection references = EMFHelper.getReferencesForScope(GeneralHelper
						.getActiveInstance(), (Activity) eobject);
				activityChildren.addAll(references);

				// add layoutdatas for the references
				for (Object reference : references)
				{
					activityChildren.addAll(LayoutDataManager
							.getLayoutDataForGraphicalElement(GeneralHelper
									.getActiveInstance(), (GraphicalElement) reference));
				}
			}
		}
		originalElements.addAll(activityChildren);

		// (4) - the elements which should be ignored (e.g. comments)
		ArrayList<EClass> ignoreElements = new ArrayList();
		if (!commentCheckbox.getSelection())
		{
			ignoreElements.add(CorePackageImpl.eINSTANCE.getComment());
		}
		HashSet<EObject> tempList = originalElements;
		originalElements = new HashSet();

		for (EObject eobject : tempList)
		{
			boolean keepElement = true;

			// find out if it should be ignored
			for (EClass eclass : ignoreElements)
			{
				if (eobject.eClass().equals(eclass))
				{
					keepElement = false;
					continue;
				}
			}

			// add only if it should not be ignored
			if (keepElement)
			{
				originalElements.add(eobject);
			}
		}

		// (5) + referenceableelements (and their containers) referenced in the
		// activities
		boolean refElemsAdded = false;
		for (EObject eobject : activityChildren)
		{
			if (eobject instanceof Reference
					&& ((Reference) eobject).getReference() != null
					&& !originalElements.contains(((Reference) eobject).getReference()))
			{
				// add refelement
				originalElements.add(((Reference) eobject).getReference());

				// recursively add refelement containers until the top
				EObject refElemContainer = ((Reference) eobject).getReference()
						.eContainer();
				while (refElemContainer != null
						&& !originalElements.contains(refElemContainer))
				{
					originalElements.add(refElemContainer);
					refElemContainer = refElemContainer.eContainer();
				}

				refElemsAdded = true;
			}
		}

		try
		{
			// copy the elements
			ExportTemplateCopier copier = new ExportTemplateCopier(true, false);
			Collection result = copier.copyAll(originalElements);
			copier.copyReferences();

			// unlink activitylinknodes that point to activities which are not
			// contained
			// in the workflow
			for (Object object : result)
			{
				EObject eobject = (EObject) object;
				if (eobject instanceof ActivityLinkNode
						&& !result.contains(((ActivityLinkNode) eobject).getLinksto()))
				{
					((ActivityLinkNode) eobject).setLinksto(null);
					((ActivityLinkNode) eobject)
							.setName("<" + ((ActivityLinkNode) eobject).getName() + ">"); //$NON-NLS-1$ //$NON-NLS-2$
				}
			}

			// if refelements were added, display message
			if (refElemsAdded)
			{
				MessageDialog.openInformation(GeneralHelper.getActiveShell(),
						PluginProperties.editor_WizardExportRefAdded_title,
						PluginProperties.editor_WizardExportRefAdded_description);
			}

			copier = null;

			return result;
		}
		catch (Exception e)
		{
			logger.warning("Could not create copy of selected elements.", e); //$NON-NLS-1$
			return null;
		}

	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
	 * .Composite )
	 */
	@Override
	public void dispose()
	{
		// dispose of everything
		packageViewer = null;
		templateField.dispose();
		commentCheckbox.dispose();

		super.dispose();
	}

}