/*
 * This file has been developed at the University of Munich, Chair for Programming & Software Engineering.
 * 
 * This file is licensed under the Eclipse Public License (EPL) 1.0
 * 
 */
package eu.uml4soa.utbm.ui.eclipse.actions;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
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.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.ISelectionStatusValidator;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.uml2.uml.Activity;
import org.eclipse.uml2.uml.Behavior;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.ProtocolStateMachine;

import eu.mdd4soa.trans.uml2smm.ui.wizard.DirectoryLoadDialog;
import eu.uml4soa.utbm.ui.eclipse.UTBMEclipseUIPlugin;

/**
 * Wizard page displaying the set of available activities, output file naming
 * pattern, and the output directory.
 * 
 * @author Philip Mayer
 * 
 */
public class SemanticsWizardPage extends WizardPage {

	private static final int WIDTH_HINT_DESCRIPTIONS= 100;

	private final class ActivityContentProvider implements ITreeContentProvider {
		@Override
		public void dispose() {
		}

		@Override
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		}

		@Override
		public Object[] getChildren(Object arg0) {
			List<Behavior> behaviors= fAvailableBehaviours.get(arg0);
			if (behaviors != null) {
				return behaviors.toArray();
			}
			return null;
		}

		@Override
		public Object getParent(Object arg0) {
			return getParentFor(arg0);
		}

		@Override
		public boolean hasChildren(Object arg0) {
			return (fAvailableBehaviours.get(arg0) != null && fAvailableBehaviours.get(arg0).size() > 0);
		}

		@Override
		public Object[] getElements(Object arg0) {
			return fAvailableBehaviours.keySet().toArray();
		}
	}

	private final class ActivityLabelProvider implements ILabelProvider {
		@Override
		public Image getImage(Object element) {
			return null;
		}

		@Override
		public String getText(Object element) {
			String text= "";
			if (element instanceof Activity) {
				Activity a= (Activity) element;
				text= a.getName() + " (Orchestration)";
			} else if (element instanceof ProtocolStateMachine) {
				ProtocolStateMachine psm= (ProtocolStateMachine) element;
				text= psm.getName() + " (Protocol)";
			} else if (element instanceof Class) {
				Class participant= (Class) element;
				text= participant.getName() + " <<Participant>>";
				if (participant.getPackage() != null) {
					String packageName= participant.getPackage().getName();
					text+= " (package: " + packageName + ")";
				} else if (participant.getNearestPackage() != null) {
					text+= " (package: " + participant.getNearestPackage().getName() + ")";
				}
			}

			return text;
		}

		@Override
		public void addListener(ILabelProviderListener listener) {
		}

		@Override
		public void dispose() {
		}

		@Override
		public boolean isLabelProperty(Object element, String property) {
			return false;
		}

		@Override
		public void removeListener(ILabelProviderListener listener) {
		}
	}


	private final class ActivityViewerSorter extends ViewerSorter {

		@Override
		public int compare(Viewer viewer, Object e1, Object e2) {
			// sort alphabetically (luckily Activities are subclasses of Class
			// in UML)
			return ((Class) e1).getName().compareTo( ((Class) e2).getName());
		}

	}

	// Data

	private Map<org.eclipse.uml2.uml.Class, List<Behavior>> fAvailableBehaviours;

	private Map<Class, List<Behavior>> fSelectedBehaviours;

	private IContainer fOutputDirectory;

	private boolean fOpen;

	// UI

	private Text fOutputDirectoryText;

	private TreeViewer fParticipantBehaviourTree;

	private Button fOutputDirectorySelectButton;

	private Button fOpenCheckbox;

	private ISelectionStatusValidator fValidator;

	protected SemanticsWizardPage(String pageName) {
		super(pageName);
		fOpen= true;
	}

	public void setActivityList(Map<org.eclipse.uml2.uml.Class, List<Behavior>> activities) {
		fAvailableBehaviours= activities;
	}

	public void setOutputDirectory(IContainer directory) {
		fOutputDirectory= directory;
	}

	public void createControl(Composite parent) {

		Composite mainComposite= new Composite(parent, SWT.NONE);
		GridLayout lg= new GridLayout();
		mainComposite.setLayout(lg);

		Group activityListGroup= new Group(mainComposite, SWT.NONE);
		activityListGroup.setText("Available behaviours");
		GridLayout lay= new GridLayout();
		lay.numColumns= 1;
		activityListGroup.setLayout(lay);
		GridData d= new GridData(SWT.FILL, SWT.FILL, true, true);
		activityListGroup.setLayoutData(d);

		createActivityList(activityListGroup);

		Group outputGroup= new Group(mainComposite, SWT.NONE);
		outputGroup.setText("Output options");
		lay= new GridLayout();
		lay.numColumns= 2;
		outputGroup.setLayout(lay);
		d= new GridData(SWT.FILL, SWT.BEGINNING, true, false);
		outputGroup.setLayoutData(d);

		createOutputDirectory(outputGroup);
		createOpenCheckbox(outputGroup);

		setControl(mainComposite);

		fValidator= new ISelectionStatusValidator() {

			@Override
			public IStatus validate(Object[] selection) {
				if (selection.length == 0)
					return new Status(IStatus.ERROR, UTBMEclipseUIPlugin.PLUGIN_ID, "Selection must not be empty");
				else
					return Status.OK_STATUS;

			}
		};

		ImageDescriptor image= AbstractUIPlugin.imageDescriptorFromPlugin(UTBMEclipseUIPlugin.PLUGIN_ID, "icons/mio.gif");
		getShell().setImage(image.createImage());
	}

	private void createActivityList(Group composite) {

		fParticipantBehaviourTree= new TreeViewer(composite);

		fParticipantBehaviourTree.setContentProvider(new ActivityContentProvider());
		fParticipantBehaviourTree.setLabelProvider(new ActivityLabelProvider());
		fParticipantBehaviourTree.setSorter(new ActivityViewerSorter());
		fParticipantBehaviourTree.setInput(fAvailableBehaviours);

		fSelectedBehaviours= new HashMap<Class, List<Behavior>>();

		GridData d= new GridData(SWT.FILL, SWT.FILL, true, true);
		d.heightHint= 100;
		fParticipantBehaviourTree.getTree().setLayoutData(d);

		fParticipantBehaviourTree.addSelectionChangedListener(new ISelectionChangedListener() {

			@Override
			public void selectionChanged(SelectionChangedEvent event) {
				IStructuredSelection sel= (IStructuredSelection) fParticipantBehaviourTree.getSelection();
				fSelectedBehaviours.clear();
				Object nextSelection= null;
				for (Iterator<?> i= sel.iterator(); i.hasNext();) {
					nextSelection= i.next();
					// Note that a behaviour is also a class in UML...
					if (nextSelection instanceof Behavior) {
						Class parent= getParentFor(nextSelection);
						if (parent != null) {
							List<Behavior> list= fSelectedBehaviours.get(parent);
							if (list == null) {
								list= new ArrayList<Behavior>();
								fSelectedBehaviours.put(parent, list);
							}
							if (!list.contains(nextSelection))
								list.add((Behavior) nextSelection);
						}
					} else if (nextSelection instanceof Class) {
						fSelectedBehaviours.put((Class) nextSelection, fAvailableBehaviours.get(nextSelection));
					}
				}
				setPageComplete(true);
			}

		});
		fParticipantBehaviourTree.expandAll();
	}

	private void createOpenCheckbox(Composite composite) {

		createLeftLabel(composite, "");

		fOpenCheckbox= new Button(composite, SWT.CHECK | SWT.LEFT);
		GridData d= getFillGridData();
		fOpenCheckbox.setLayoutData(d);
		fOpenCheckbox.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
			}

			@Override
			public void widgetSelected(SelectionEvent e) {
				fOpen= fOpenCheckbox.getSelection();
			}

		});

		fOpenCheckbox.setSelection(fOpen);
		fOpenCheckbox.setText("Open result in editors");
	}

	private void createOutputDirectory(Composite composite) {

		createLeftLabel(composite, "Output Directory:");
		GridData gd= null;

		Composite rightComposite= new Composite(composite, SWT.NONE);
		GridLayout gridLayout= new GridLayout(2, false);
		gridLayout.marginWidth= 0;
		rightComposite.setLayout(gridLayout);

		gd= getFillGridData();
		rightComposite.setLayoutData(gd);

		fOutputDirectoryText= new Text(rightComposite, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
		gd= getFillGridData();
		gd.verticalAlignment= SWT.CENTER;
		fOutputDirectoryText.setLayoutData(gd);
		fOutputDirectoryText.setEditable(false);

		fOutputDirectorySelectButton= new Button(rightComposite, SWT.PUSH);
		gd= new GridData(SWT.TRAIL, SWT.TOP, false, false);
		fOutputDirectorySelectButton.setText("Choose...");
		fOutputDirectorySelectButton.setLayoutData(gd);
		fOutputDirectorySelectButton.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
			}

			public void widgetSelected(SelectionEvent e) {
				DirectoryLoadDialog d= new DirectoryLoadDialog(getShell(), "Directory Selection", "Select an output directory", fOutputDirectory,
						fValidator);
				if (d.open() == DirectoryLoadDialog.OK) {
					IContainer firstResult= d.getFirstResult();
					if (firstResult != null) {
						fOutputDirectory= firstResult;
						fOutputDirectoryText.setText(fOutputDirectory.getFullPath().toString());
					}
				}
			}

		});

		fOutputDirectoryText.setText(fOutputDirectory.getFullPath().toString());
	}

	private void createLeftLabel(Composite composite, String s) {

		Label l= new Label(composite, SWT.RIGHT);
		l.setText(s);
		GridData d= new GridData();
		d.verticalAlignment= SWT.CENTER;
		d.widthHint= WIDTH_HINT_DESCRIPTIONS;
		l.setLayoutData(d);
	}

	private GridData getFillGridData() {
		return new GridData(SWT.FILL, SWT.TOP, true, false);
	}

	public Map<Class, List<Behavior>> getSelectedBehaviours() {
		return fSelectedBehaviours;
	}

	public IContainer getOutputDirectory() {
		return fOutputDirectory;
	}

	public boolean getOpen() {
		return fOpen;
	}

	private Class getParentFor(Object arg0) {
		for (Class participant : fAvailableBehaviours.keySet()) {
			for (Behavior behavior : fAvailableBehaviours.get(participant)) {
				if (arg0.equals(behavior)) {
					return participant;
				}
			}
		}
		return null;
	}

}
