/**
 * File:    ManageActivatedProfilesUI.java
 * Created: 26.02.2008
 *
 *
/*******************************************************************************
 * Copyright (c) 2008-2009 Open Wide SA <www.openwide.fr>
 *
 * 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:
 *    Marc Dutoo, Open Wide, Lyon, France
 *      - creation and implementation
 *    Mickael Istria, Open Wide, Lyon, France
 *      - Use NLS Strings
 *******************************************************************************/
package org.eclipse.jwt.we.conf.edit.ui;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jwt.we.conf.edit.provider.ConfItemProviderAdapterFactory;
import org.eclipse.jwt.we.conf.edit.ui.internal.Messages;
import org.eclipse.jwt.we.conf.edit.ui.internal.StatusDialog;
import org.eclipse.jwt.we.conf.edit.ui.internal.UIUtils;
import org.eclipse.jwt.we.conf.edit.ui.provider.AdapterFactoryColumnLabelProvider;
import org.eclipse.jwt.we.conf.edit.ui.provider.AdapterFactoryContentProviderWithList;
import org.eclipse.jwt.we.conf.edit.ui.provider.MissingProfileLabelProvider;
import org.eclipse.jwt.we.conf.edit.ui.viewer.ClassViewFilter;
import org.eclipse.jwt.we.conf.model.AspectInstance;
import org.eclipse.jwt.we.conf.model.ConfModel;
import org.eclipse.jwt.we.conf.model.Profile;
import org.eclipse.jwt.we.conf.model.aspects.AspectManager;
import org.eclipse.jwt.we.conf.model.aspects.ConfRegistry;
import org.eclipse.jwt.we.conf.model.resource.ConfModelResourceManager;
import org.eclipse.jwt.we.conf.model.resource.ConfModelStatus;
import org.eclipse.jwt.we.conf.model.resource.ConfResourceException;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;

/**
 * Manages activated profiles.
 * 
 * If the conf model contains profiles depending on missing EMF models and
 * plugins, proposes to patch them so at least other profiles will be available.
 * 
 * TODO if has no ConfModel, propose to migrate ?
 * 
 * By default it manages profiles for the constructor-given weEditor's model. To
 * make it work for different weEditors, override getWeEditor(), and optionally
 * listen to selection changes, ex :
 * Plugin.getInstance().getWorkbench().getActiveWorkbenchWindow
 * ().getSelectionService().addSelectionListener(this);
 * 
 * TODO migrate warning messages to resource markers (using a specific
 * Builder...) : error or warning markers about missing profiles or
 * non-installed, embedded conf model
 * 
 * @version $Id: ManageActivatedProfilesUI.java,v 1.2 2010/05/27 15:42:49 mdutoo
 *          Exp $
 * @author Marc Dutoo, Open Wide SA, France <www.openwide.fr>
 * @since 0.6
 */
public class ManageActivatedProfilesUI
{

	private static final List<Profile> EMPTY_PROFILE_LIST = new ArrayList<Profile>(0);

	private AdapterFactory adapterFactory;

	private String importFilePath = null;
	private EObject selectedModel = null;
	private ConfModelStatus selectedModelStatus = null;

	private int checkMissingProfilesStatus;
	private String checkMissingProfilesTitleText;
	private String checkMissingProfilesInfoText;
	private Label warningMissingProfilesLabel;
	private Label adviceProfilesLabel;

	private Button showStatusDetailsButton;
	private Text exportTargetFileText;
	private Button chooseExportTargetFileButton;
	private Button exportToFileButton;
	private Combo usingEmbeddedProfilesCombo;
	private TreeViewer missingProfilesTreeViewer;
	private TreeViewer installedProfilesTreeViewer;
	private TreeViewer embeddedProfilesTreeViewer;

	private StackLayout enableProfileStackLayout;

	private Group enableProfileGroup;

	private Composite profileUiComposite;

	private Composite stackComposite;

	private Label enableProfileLabel;


	/**
	 * 
	 * @param adapterFactory
	 *            better to use composed adapterfactory, so it can handle all
	 *            EObjects (rather than default ConfItemProviderAdapterFactory)
	 */
	public ManageActivatedProfilesUI(AdapterFactory adapterFactory)
	{
		if (adapterFactory != null)
		{
			this.adapterFactory = adapterFactory;
		}
		else
		{
			this.adapterFactory = new ConfItemProviderAdapterFactory();
		}
	}


	public EObject getSelectedModel()
	{
		return selectedModel;
	}


	public void setSelectedModel(EObject selectedModel)
	{
		this.selectedModel = selectedModel;
		this.importFilePath = ""; // default //$NON-NLS-1$

		if (selectedModel != null)
		{
			// get selected model resource URI
			Resource selectedModelResource = selectedModel.eResource();

			if (selectedModelResource != null)
			{
				// else happens ex. when selectedModel is actually a
				// modelElement that has just been deleted

				URI selectedModelUri = selectedModelResource.getURI();
				if (selectedModelUri != null)
				{
					// else happens when error loading model (ex. embeddedconf
					// feature not found)
					// NB. no other way to detect it (isLoaded() or getErrors())

					String modelFileName = selectedModelUri.lastSegment();
					String confFileName = modelFileName.substring(0,
							modelFileName.lastIndexOf('.'));
					// TODO better : give real conf/ location
					this.importFilePath = "conf/imported/" + confFileName + ".conf"; //$NON-NLS-1$ //$NON-NLS-2$
				}
			}
		}
	}


	/**
	 * Helper depending on the current selected model
	 * 
	 * @return
	 */
	protected List<Profile> getMissingProfiles()
	{
		if (selectedModel == null)
		{
			return EMPTY_PROFILE_LIST;
		}
		List<Profile> missingProfiles = AspectManager.INSTANCE
				.getMissingInstalledProfiles(selectedModel);
		return missingProfiles;
	}


	/**
	 * Helper depending on the current selected model
	 * 
	 * @return
	 */
	protected boolean useEmbeddedConf()
	{
		ConfModel selectedConfModel = getSelectedModelConfModel();
		return (selectedConfModel == null) ? false : selectedConfModel
				.getUseEmbeddedConf();
	}


	/**
	 * Helper depending on the current selected model
	 * 
	 * @return
	 */
	protected ConfModel getSelectedModelConfModel()
	{
		return (selectedModel == null) ? null : AspectManager.INSTANCE
				.getConfModel(selectedModel);
	}


	private boolean importToFileHasAllRequiredInfo()
	{
		// TODO warn if importToFile.exists()
		if (importFilePath != null)
		{
			File importToFile = new File(importFilePath);
			return (importToFile.canRead() && importToFile.isFile())
					|| (!importToFile.exists() && importToFile.getParentFile() != null && importToFile
							.getParentFile().exists());
		}
		return false;
	}


	/**
	 * Does nothing, to be overriden by extenders. Must be called by wrapping
	 * UI.
	 */
	public void dispose()
	{

	}


	/**
	 * Refreshes the UI and first its model
	 */
	public void refresh()
	{
		if (selectedModel == null)
		{
			if (enableProfileStackLayout.topControl != enableProfileLabel)
			{
				enableProfileStackLayout.topControl = enableProfileLabel;
				stackComposite.layout();
			}
			return;
		}

		this.selectedModelStatus = new ConfModelStatus();
		this.selectedModelStatus.setBypassFlaggedUnavailable(true);
		ConfModel mySelectedModelConfModel = AspectManager.INSTANCE.getConfModel(
				selectedModel, this.selectedModelStatus);
		if (this.selectedModelStatus.getSeverity() == IStatus.ERROR)
		{
			// can this error be handled gracefully ?
			IStatus resourceDoesNotExistErrorStatus = null;
			IStatus patchableMissingEmfErrorStatus = null;
			for (IStatus childStatus : this.selectedModelStatus.getChildren())
			{
				if (childStatus.getCode() == ConfModelStatus.RESOURCE_DOES_NOT_EXIST)
				{
					// is it the common "no aspects" case ?
					resourceDoesNotExistErrorStatus = childStatus;
					break;
				}
				else if (childStatus.getCode() == ConfModelStatus.RESOURCE_PATCHABLE_MISSING_EMF)
				{
					// is it patchable ?
					patchableMissingEmfErrorStatus = childStatus;
					break;
				}
			}

			if (resourceDoesNotExistErrorStatus != null)
			{
				// common "no aspects" case, do nothing
				// TODO log or put in warnings ??

			}
			else if (patchableMissingEmfErrorStatus != null)
			{
				// asking whether to try patching
				String msg = (patchableMissingEmfErrorStatus.getException() == null) ? patchableMissingEmfErrorStatus
						.getMessage() : patchableMissingEmfErrorStatus.getException()
						.getMessage();
				int tryPatchingMissingEmfError = ErrorDialog.openError(
						stackComposite.getShell(), Messages.confirmTryPatching_title,
						Messages.bind(Messages.confirmTryPatching_body, msg),
						selectedModelStatus);
				if (tryPatchingMissingEmfError == Dialog.OK)
				{
					this.selectedModelStatus = new ConfModelStatus();
					this.selectedModelStatus.setTryPatchingMissingEmf(true);
					this.selectedModelStatus.setBypassFlaggedUnavailable(true);
					mySelectedModelConfModel = AspectManager.INSTANCE.getConfModel(
							selectedModel, this.selectedModelStatus);
					if (this.selectedModelStatus.getSeverity() == IStatus.ERROR)
					{
						String retryMsg = (this.selectedModelStatus.getException() == null) ? this.selectedModelStatus
								.getMessage() : this.selectedModelStatus.getException()
								.getMessage();
						ErrorDialog.openError(stackComposite.getShell(),
								Messages.errorPatching_title,
								Messages.bind(Messages.errorPatching_body, retryMsg),
								this.selectedModelStatus);
					}
					else
					{
						MessageDialog.openInformation(stackComposite.getShell(),
								Messages.successfullyPatched_title,
								Messages.successfullyPatched_body);
					}
				}
				else
				{
					ErrorDialog.openError(stackComposite.getShell(),
							Messages.noPatchApplied_title, Messages.noPatchApplied_body,
							this.selectedModelStatus);
				}
			}
			else
			{
				ErrorDialog.openError(stackComposite.getShell(),
						Messages.errorLoadingConfModel_title,
						Messages.errorLoadingConfModel_body, this.selectedModelStatus);
			}
		}

		if (mySelectedModelConfModel != null)
		{
			if (enableProfileStackLayout.topControl != profileUiComposite)
			{
				enableProfileStackLayout.topControl = profileUiComposite;
			}

			updateWarnings();
			updateImportToFile();
			updateAvailableProfiles();
			stackComposite.layout(); // shows the right stack layer
			// stackComposite.pack(); //resize TODO doesn't work
			// to resize a wrapping dialog, in its code recompute its shell's
			// size, like this :
			// stackComposite.getShell().setSize(stackComposite.getShell().computeSize(SWT.DEFAULT,
			// SWT.DEFAULT));
			// stackComposite.setSize(stackComposite.computeSize(SWT.DEFAULT,
			// SWT.DEFAULT)); // resize TODO doesn't work

		}
		else
		{
			if (enableProfileStackLayout.topControl != enableProfileGroup)
			{
				enableProfileStackLayout.topControl = enableProfileGroup;
				stackComposite.layout(); // shows the right stack layer
			}
		}
	}


	protected void updateWarnings()
	{
		// run validation code
		checkMissingProfilesTitleText = ""; //$NON-NLS-1$
		checkMissingProfilesInfoText = ""; //$NON-NLS-1$
		checkMissingProfilesStatus = Status.OK;
		if (this.getMissingProfiles().isEmpty())
		{
			checkMissingProfilesTitleText = Messages.noMissingProfilesTitle;
			if (this.useEmbeddedConf())
			{
				checkMissingProfilesInfoText = Messages.advertUseEmbeddedProfiles;
			}
		}
		else if (this.useEmbeddedConf())
		{ // BOLD
			checkMissingProfilesTitleText = Messages.missingProfilesWarningTitle;
			checkMissingProfilesStatus = Status.WARNING;
			checkMissingProfilesInfoText = Messages.missingProfilesEmbeddedConfText;
		}
		else
		{ // BOLD
			checkMissingProfilesTitleText = Messages.missingProfilesWarningTitle;
			checkMissingProfilesStatus = Status.ERROR;
			checkMissingProfilesInfoText = Messages.missingProfilesNotEmbeddedConfText;
		}

		// update UI
		warningMissingProfilesLabel.setText(checkMissingProfilesTitleText);
		adviceProfilesLabel.setText(checkMissingProfilesInfoText);
		switch (checkMissingProfilesStatus)
		{
			case Status.WARNING:
				addFontStyle(warningMissingProfilesLabel, SWT.BOLD);
				break;
			case Status.ERROR:
				addFontStyle(warningMissingProfilesLabel, SWT.BOLD);
				break;
			default:
				removeFontStyle(warningMissingProfilesLabel, SWT.BOLD);
		}
	}


	private void updateImportToFile()
	{
		importFilePath = (importFilePath == null) ? "" : importFilePath; //$NON-NLS-1$
		exportTargetFileText.setText(importFilePath);
		exportToFileButton.setEnabled(importToFileHasAllRequiredInfo());
		chooseExportTargetFileButton.setEnabled(!this.getMissingProfiles().isEmpty());
		exportToFileButton.setEnabled(!this.getMissingProfiles().isEmpty()
				&& importToFileHasAllRequiredInfo());
		usingEmbeddedProfilesCombo.setText("" + this.useEmbeddedConf()); //$NON-NLS-1$
	}


	private void updateAvailableProfiles()
	{
		usingEmbeddedProfilesCombo.setText("" + this.useEmbeddedConf()); //$NON-NLS-1$
		missingProfilesTreeViewer.setInput(this.getMissingProfiles()); // TODO
																		// copy
																		// ? or
																		// selectionListener
																		// enough
																		// ?
		// TODO not required each time ??
		installedProfilesTreeViewer
				.setInput(ConfRegistry.INSTANCE.getInstalledProfiles());
		embeddedProfilesTreeViewer.setInput(this.getSelectedModelConfModel());
	}


	@SuppressWarnings("serial")
	public Composite createControl(Composite parent)
	{
		enableProfileStackLayout = new StackLayout();
		stackComposite = new Composite(parent, SWT.None);
		stackComposite.setLayout(enableProfileStackLayout);

		enableProfileLabel = new Label(stackComposite, SWT.None);
		enableProfileLabel.setText(Messages.noWorkflowModelSelected);

		enableProfileGroup = new Group(stackComposite, SWT.None);
		enableProfileGroup.setText(Messages.enableProfile);
		enableProfileGroup.setLayout(new GridLayout(1, false));
		Label enableProfileLabel = new Label(enableProfileGroup, SWT.None);
		enableProfileLabel.setText(Messages.profileDisabled);
		Button enableProfileButton = new Button(enableProfileGroup, SWT.RIGHT);
		enableProfileButton.setText(Messages.enable);

		profileUiComposite = new Composite(stackComposite, SWT.None);
		profileUiComposite.setLayout(new GridLayout(1, false));

		// ////////////////
		// Warning Group
		Group warningGroup = new Group(profileUiComposite, SWT.None);
		warningGroup.setText(Messages.warningAndAdvice);
		GridLayout gL = new GridLayout(2, false);
		gL.makeColumnsEqualWidth = true;
		warningGroup.setLayout(gL);
		warningGroup.setLayoutData(new GridData(GridData.FILL_BOTH));

		warningMissingProfilesLabel = new Label(warningGroup, SWT.None);

		Label missingProfilesLabel = new Label(warningGroup, SWT.None);
		missingProfilesLabel
				.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
		missingProfilesLabel.setText(Messages.missingProfiles);
		
		adviceProfilesLabel = new Label(warningGroup, SWT.None);

		missingProfilesTreeViewer = createProfileTreeViewer(warningGroup, null);
		GridData gD = new GridData(GridData.FILL_BOTH);
		gD.minimumHeight = 100;
		gD.verticalSpan = 2;
		missingProfilesTreeViewer.getTree().setLayoutData(gD);
		missingProfilesTreeViewer.getTree().setHeaderVisible(true);
		missingProfilesTreeViewer.getTree().setLinesVisible(true);
		
		showStatusDetailsButton = new Button(warningGroup, SWT.None);
		showStatusDetailsButton.setText(Messages.showStatusDetails_button);

		// ////////////////
		// Available profiles group
		Group availableProfilesGroup = new Group(profileUiComposite, SWT.None);
		availableProfilesGroup.setText(Messages.detailAvailableProfiles);
		availableProfilesGroup.setLayout(new GridLayout(1, false));
		availableProfilesGroup.setLayoutData(new GridData(GridData.FILL_BOTH));

		Label installedProfilesLabel = new Label(availableProfilesGroup, SWT.None);
		installedProfilesLabel.setLayoutData(new GridData(
				GridData.VERTICAL_ALIGN_BEGINNING));
		installedProfilesLabel.setText(Messages.installedProfiles);
		addFontStyle(installedProfilesLabel, SWT.BOLD);
		installedProfilesTreeViewer = createProfileTreeViewer(availableProfilesGroup,
				null);
		GridData treeLayoutData = new GridData(GridData.FILL_BOTH);
		treeLayoutData.minimumHeight = 150;
		installedProfilesTreeViewer.getTree().setLayoutData(treeLayoutData);
		installedProfilesTreeViewer.getTree().setHeaderVisible(true);
		installedProfilesTreeViewer.getTree().setLinesVisible(true);

		Label embeddedProfilesLabel = new Label(availableProfilesGroup, SWT.None);
		embeddedProfilesLabel.setLayoutData(new GridData(
				GridData.VERTICAL_ALIGN_BEGINNING));
		embeddedProfilesLabel.setText(Messages.embeddedProfiles); // TODO or
																	// Activated
																	// profiles
																	// ?
		addFontStyle(embeddedProfilesLabel, SWT.BOLD);
		embeddedProfilesTreeViewer = createProfileTreeViewer(availableProfilesGroup,
				new MissingProfileLabelProvider(this.adapterFactory,
						new HashMap<Integer, String>()
						{

							{
								put(1, Messages.version);
								put(2, Messages.description);
							}
						}));
		GridData embeddedProfilesTreeViewerLayoutData = new GridData(GridData.FILL_BOTH);
		embeddedProfilesTreeViewerLayoutData.minimumHeight = 150;
		embeddedProfilesTreeViewer.getTree().setLayoutData(
				embeddedProfilesTreeViewerLayoutData);
		embeddedProfilesTreeViewer.getTree().setHeaderVisible(true);
		embeddedProfilesTreeViewer.getTree().setLinesVisible(true);
		// embeddedProfilesTreeViewer.setInput(this.getSelectedModel().getEmbeddedconf());

		// ////////////////
		// Export Missing Profiles Group
		Group exportMissingProfileGroup = new Group(profileUiComposite, SWT.None);
		exportMissingProfileGroup.setText(Messages.exportMissingProfiles);
		exportMissingProfileGroup.setLayout(new GridLayout(3, false));
		exportMissingProfileGroup.setLayoutData(new GridData(GridData.FILL_BOTH));

		chooseExportTargetFileButton = new Button(exportMissingProfileGroup, SWT.NULL);
		chooseExportTargetFileButton.setText(Messages.choose);

		exportTargetFileText = new Text(exportMissingProfileGroup, SWT.SINGLE
				| SWT.BORDER);
		exportTargetFileText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL
				| GridData.HORIZONTAL_ALIGN_FILL));
		exportTargetFileText.setEnabled(false);

		exportToFileButton = new Button(exportMissingProfileGroup, SWT.NULL);
		exportToFileButton.setText(Messages.exportToConfFile);

		// ////////////////
		// Development tools group
		Group developmentToolsGroup = new Group(profileUiComposite, SWT.None);
		developmentToolsGroup.setText(Messages.developmentTools);
		developmentToolsGroup.setLayout(new GridLayout(4, false));
		developmentToolsGroup.setLayoutData(new GridData(GridData.FILL_BOTH));

		Label usingEmbeddedProfilesLabel = new Label(developmentToolsGroup, SWT.None);
		usingEmbeddedProfilesLabel.setText(Messages.usingEmbeddedProfiles);

		usingEmbeddedProfilesCombo = new Combo(developmentToolsGroup, SWT.DROP_DOWN);
		usingEmbeddedProfilesCombo.setItems(new String[]
		{ "true", "false" }); //$NON-NLS-1$ //$NON-NLS-2$
		usingEmbeddedProfilesCombo.addModifyListener(new ModifyListener()
		{

			public void modifyText(ModifyEvent e)
			{
				boolean newUseEmbeddedConf = Boolean.valueOf(usingEmbeddedProfilesCombo
						.getText());
				boolean oldUseEmbeddedConf = ManageActivatedProfilesUI.this
						.useEmbeddedConf();
				if (newUseEmbeddedConf != oldUseEmbeddedConf)
				{
					ManageActivatedProfilesUI.this.getSelectedModelConfModel()
							.setUseEmbeddedConf(newUseEmbeddedConf);
					ManageActivatedProfilesUI.this.refresh();
				}
			}
		});

		Button disableProfileButton = new Button(developmentToolsGroup, SWT.RIGHT);
		disableProfileButton.setText(Messages.disableProfiles);

		showStatusDetailsButton.addListener(SWT.Selection, new Listener()
		{

			public void handleEvent(Event e)
			{
				// opens a dialog with the status
				String msg = Messages.bind(
						Messages.statusDetails_body,
						new Object[]
						{
								selectedModel.eResource().getURI(),
								selectedModelStatus.getSeverity() == IStatus.OK
										|| selectedModelStatus.getSeverity() == IStatus.INFO ? "OK"
										: selectedModelStatus.getSeverity() == IStatus.WARNING ? "WARNING"
												: "ERROR" });
				msg += "\n"
						+ ((selectedModelStatus.getChildren() == null || selectedModelStatus
								.getChildren().length == 0) ? Messages.statusDetails_body_nodetails
								: Messages.statusDetails_body_details);
				StatusDialog.openStatus(stackComposite.getShell(),
						Messages.statusDetails_title, msg, selectedModelStatus);
			}
		});

		chooseExportTargetFileButton.addListener(SWT.Selection, new Listener()
		{

			public void handleEvent(Event e)
			{
				// opens a dialog to select the file
				FileDialog dialog = new FileDialog(stackComposite.getShell(), SWT.OPEN);
				dialog.setFileName(importFilePath);
				dialog.setFilterExtensions(new String[]
				{ "*.conf", "*.*" }); //$NON-NLS-1$ //$NON-NLS-2$
				importFilePath = dialog.open();
				ManageActivatedProfilesUI.this.refresh();
			}
		});

		exportToFileButton.addListener(SWT.Selection, new Listener()
		{

			public void handleEvent(Event event)
			{
				try
				{
					File importFile = new File(importFilePath);
					// TODO check canRead, warn override
					ConfModel exportConfModel = ConfModelResourceManager.INSTANCE
							.exportProfilesToFile(
									ManageActivatedProfilesUI.this.getMissingProfiles(),
									importFile);

					// import in conf registry so it gets used instead of the
					// embedded one
					// TODO check it is in the discovery file path, else won't
					// work at next load
					ConfRegistry.INSTANCE.addDiscoveredConf(exportConfModel); // TODO
																				// or
																				// trigger
																				// rediscovery

					ConfModel confModel = getSelectedModelConfModel(); // cast
																		// always
																		// ok
					if (confModel.getUseEmbeddedConf())
					{
						confModel.setUseEmbeddedConf(false);
						// TODO mark as dirty
					}

					MessageDialog.openInformation(stackComposite.getShell(),
							Messages.Success,
							Messages.bind(Messages.importSuccess, exportToFileButton));
				}
				catch (Exception e)
				{ // IOException or ConfResourceException
					ErrorDialog.openError(
							stackComposite.getShell(),
							Messages.importError,
							"" //$NON-NLS-1$
									+ e.getMessage(),
							new Status(
									Status.ERROR,
									"org.eclipse.jwt.we.conf.model.edit", Status.OK, e.getMessage(), e)); //$NON-NLS-1$
				}

				ManageActivatedProfilesUI.this.refresh();
			}
		});

		enableProfileButton.addListener(SWT.Selection, new Listener()
		{

			public void handleEvent(Event e)
			{
				if (selectedModel == null)
				{
					return;
				}
				try
				{
					// loading with createIfDoesn't exist (also unflags
					// unavailability)
					ConfModel newlyLoadedConfModel = ConfModelResourceManager.INSTANCE
							.loadConfModelOfModel(selectedModel, true);

					if (newlyLoadedConfModel == null)
					{
						String msg = Messages.enablementError;
						ErrorDialog.openError(
								stackComposite.getShell(),
								Messages.errorCreatingOrReloadingConfModel,
								msg,
								new Status(
										Status.ERROR,
										"org.eclipse.jwt.we.conf.model.edit", Status.OK, msg, null)); //$NON-NLS-1$
					}

				}
				catch (ConfResourceException crex)
				{
					ErrorDialog.openError(stackComposite.getShell(),
							Messages.enablementError, "" //$NON-NLS-1$
									+ crex.getMessage(), new Status(Status.ERROR,
									"org.eclipse.jwt.we.conf.model.edit", //$NON-NLS-1$
									Status.OK, crex.getMessage(), crex));
				}

				refresh();
			}
		});

		disableProfileButton.addListener(SWT.Selection, new Listener()
		{

			public void handleEvent(Event e)
			{
				boolean confirm = MessageDialog.openConfirm(stackComposite.getShell(),
						Messages.confirmConfRemoval_Title,
						Messages.confirmConfRemoval_Body);
				if (!confirm)
				{
					return;
				}
				// deleting conf model
				if (!ConfModelResourceManager.INSTANCE
						.deleteConfModel(AspectManager.INSTANCE
								.getConfModel(selectedModel)))
				{
					String msg = Messages.confModelNotDeleted;
					ErrorDialog.openError(
							stackComposite.getShell(),
							Messages.errorDisablingProfiles,
							msg,
							new Status(
									Status.ERROR,
									"org.eclipse.jwt.we.conf.model.edit", Status.OK, msg, null)); //$NON-NLS-1$
				}

				refresh();
			}
		});

		// adding tree context menus and actions
		makeActions();
		hookContextMenu(installedProfilesTreeViewer, new Action[]
		{ activateInstalledProfileAction });
		hookContextMenu(embeddedProfilesTreeViewer, new Action[]
		{ disableEmbeddedProfileAction });

		return stackComposite;
	}


	/**
	 * Helper TODO move
	 * 
	 * @param textControl
	 * @param swtFontStyle
	 *            ex. SWT.BOLD
	 */
	private void addFontStyle(Control textControl, int swtFontStyle)
	{
		Font font = textControl.getFont();
		FontData fontData = (FontData) font.getFontData()[0];
		fontData.setStyle(fontData.getStyle() | swtFontStyle);
		textControl.setFont(new Font(font.getDevice(), fontData));
	}


	/**
	 * Helper TODO move
	 * 
	 * @param textControl
	 * @param swtFontStyle
	 *            ex. SWT.BOLD
	 */
	private void removeFontStyle(Control textControl, int swtFontStyle)
	{
		Font font = textControl.getFont();
		FontData fontData = (FontData) font.getFontData()[0];
		fontData.setStyle(fontData.getStyle() & swtFontStyle);
		textControl.setFont(new Font(font.getDevice(), fontData));
	}


	@SuppressWarnings("serial")
	private TreeViewer createProfileTreeViewer(Composite parentComposite,
			IBaseLabelProvider optCustomLabelProvider)
	{
		TreeViewer profilesTreeViewer = new TreeViewer(parentComposite, SWT.BORDER
				| SWT.H_SCROLL | SWT.V_SCROLL); // MULTI ?
		// NB. if no column there is a default one since EObject tree
		Tree profilesTree = profilesTreeViewer.getTree();

		// build columns
		TreeColumn column0 = new TreeColumn(profilesTree, SWT.LEFT);
		column0.setText(""); //$NON-NLS-1$
		column0.setWidth(300);
		TreeColumn column1 = new TreeColumn(profilesTree, SWT.LEFT);
		column1.setText(Messages.version);
		column1.setWidth(100);
		TreeColumn column2 = new TreeColumn(profilesTree, SWT.LEFT);
		column2.setText(Messages.description);
		column2.setWidth(300);
		// profilesTree.setHeaderVisible(true); // nice to have if custom
		// columns, or to let them be resized

		// set providers
		profilesTreeViewer.setContentProvider(new AdapterFactoryContentProviderWithList(
				this.adapterFactory));
		// filter out asoectInstances
		profilesTreeViewer.addFilter(new ClassViewFilter(AspectInstance.class));
		// NB. either wrap with an impl of ITreeContentProvider or impl
		// AdapterFactory on top of list ? benefit no copy : listen to external
		// ex. property changes...
		if (optCustomLabelProvider == null)
		{
			optCustomLabelProvider = new AdapterFactoryColumnLabelProvider(
					this.adapterFactory, new HashMap<Integer, String>()
					{

						{
							put(1, Messages.version);
							put(2, Messages.description);
						}
					});
		}
		profilesTreeViewer.setLabelProvider(optCustomLabelProvider);

		return profilesTreeViewer;
	}


	private void hookContextMenu(TreeViewer viewer, final Action[] actions)
	{
		MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener()
		{

			public void menuAboutToShow(IMenuManager manager)
			{
				fillContextMenu(manager, actions);
			}
		});
		Menu menu = menuMgr.createContextMenu(viewer.getControl());
		viewer.getControl().setMenu(menu);
		if (UIUtils.getActiveEditor() != null)
		{
			// TODO else register it afterwards ? seems to work...
			UIUtils.getActiveEditor().getSite().registerContextMenu(menuMgr, viewer);
		}
	}

	private Action activateInstalledProfileAction = null;
	private Action disableEmbeddedProfileAction = null;


	private void fillContextMenu(IMenuManager manager, Action[] actions)
	{
		for (Action action : actions)
		{
			manager.add(action);
		}
	}


	private void makeActions()
	{
		activateInstalledProfileAction = new Action()
		{

			public void run()
			{
				ISelection selection = installedProfilesTreeViewer.getSelection();
				if (!(selection instanceof IStructuredSelection))
				{
					return;
				}
				// TODO ?
				/*
				 * boolean disableExecuteBehaviours =
				 * MessageDialog.openQuestion(stackComposite.getShell(),
				 * "Disable Play Behaviours",
				 * "You're about to activate a set of profiles." +
				 * "Do you want to disable playing their behaviours ?");
				 */
				for (Object selectedObject : ((IStructuredSelection) selection).toList())
				{
					if (selectedObject instanceof Profile)
					{
						Profile installedProfile = (Profile) selectedObject;
						AspectManager.INSTANCE.activateProfile(getSelectedModel(),
								installedProfile); // TODO
													// ddisableExecuteBehaviours
													// ?
					}
				}
				ManageActivatedProfilesUI.this.refresh();
			}
		};
		activateInstalledProfileAction.setText(Messages.activate);
		activateInstalledProfileAction.setImageDescriptor(PlatformUI.getWorkbench()
				.getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD));

		disableEmbeddedProfileAction = new Action()
		{

			public void run()
			{
				ISelection selection = embeddedProfilesTreeViewer.getSelection();
				if (!(selection instanceof IStructuredSelection))
				{
					return;
				}
				boolean confirm = MessageDialog.openConfirm(stackComposite.getShell(),
						Messages.disableProfiles_Title, Messages.disableProfiles_Body);
				if (!confirm)
				{
					return;
				}
				for (Object selectedObject : ((IStructuredSelection) selection).toList())
				{
					if (selectedObject instanceof Profile)
					{
						Profile embeddedProfile = (Profile) selectedObject;
						AspectManager.INSTANCE.disableProfile(getSelectedModel(),
								embeddedProfile);
					}
				}
				ManageActivatedProfilesUI.this.refresh();
			}
		};
		disableEmbeddedProfileAction.setText(Messages.disable);
		disableEmbeddedProfileAction.setImageDescriptor(PlatformUI.getWorkbench()
				.getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_DELETE));
	}

}
