/*******************************************************************************
 * Copyright (c) 2010 Itema AS 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:
 *     Itema AS - initial API and implementation
 *******************************************************************************/

package org.eclipse.mylyn.internal.builds.ui.notifications;

import java.util.HashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.wizard.IWizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.mylyn.builds.core.IBuildServer;
import org.eclipse.mylyn.builds.ui.BuildsUi;
import org.eclipse.mylyn.builds.ui.spi.BuildServerWizard;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.commons.notifications.core.AbstractNotification;
import org.eclipse.mylyn.commons.notifications.core.NotificationSinkEvent;
import org.eclipse.mylyn.commons.notifications.ui.AbstractUiNotification;
import org.eclipse.mylyn.commons.notifications.ui.NotificationControl;
import org.eclipse.mylyn.commons.repositories.core.RepositoryLocation;
import org.eclipse.mylyn.internal.builds.ui.BuildsUiPlugin;
import org.eclipse.mylyn.internal.builds.ui.view.NewBuildServerAction;
import org.eclipse.mylyn.internal.commons.repositories.ui.wizards.NewRepositoryWizardRegistry;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.wizards.IWizardDescriptor;

/**
 * GUI control designed to display build service messages. These messages can contain links that may be clicked by the
 * user and handled here. The following link formats are allowed:
 * <ul>
 * <li><code>create</code> - Calls the create wizard selection dialog.</li>
 * <li><code>discover:&lt;id&gt;?&lt;arguments&gt;</code> - Calls the wizard with the specified id and passes along the
 * given arguments.</li>
 * </ul>
 * <p>
 * An exact list of which properties are supported can be found in {@link RepositoryLocation}.
 * </p>
 * <p>
 * A stack of messages is maintained. When a new message arrives it is placed on top of the stack and displayed.
 * </p>
 * <p>
 * The code was lifted from org.eclipse.mylyn.internal.tasks.ui.notifications and modified to be suitable for this use.
 * It also has been simplified a bit.
 * </p>
 * 
 * @author Torkild U. Resheim
 */
public class BuildsServiceMessageControl extends NotificationControl {

	private final CopyOnWriteArrayList<AbstractNotification> messages;

	//private final Stack<AbstractNotification> messages;

	public BuildsServiceMessageControl(Composite parent) {
		super(parent);
		messages = new CopyOnWriteArrayList<AbstractNotification>();
	}

	protected void closeMessage() {
		if (messages.isEmpty()) {
			close();
		} else {
			nextMessage();
		}
	}

	private void nextMessage() {
		if (!messages.isEmpty()) {
			AbstractNotification message = messages.remove(0);
			if (ensureControl()) {
				setTitle(message.getLabel());
				setDescription(message.getDescription());
				if (message instanceof AbstractUiNotification) {
					setTitleImage(((AbstractUiNotification) message).getNotificationKindImage());
				}
				setEventId(message.getEventId());
			}
		}
	}

	/**
	 * Opens a new repository wizard using the given data.
	 * 
	 * @param data
	 */
	private void openRepositoryWizard(String data) {
		String connectorKind = data;
		if (data.indexOf('?') > -1) {
			connectorKind = data.substring(0, data.indexOf('?'));
		}
		// Get the Mylyn repository wizard
		IWizardDescriptor descriptor = NewRepositoryWizardRegistry.getInstance().findWizard(connectorKind);
		try {
			// Then if we have a wizard, open it.
			if (descriptor != null) {
				// This will use the default constructor so we need to convey some data.
				IWizard wizard = descriptor.createWizard();

				if (wizard instanceof BuildServerWizard) {
					try {

						// Set data we got from the discovery mechanism. This 
						// comes as a list of properties with keys and values.
						HashMap<String, String> properties = new HashMap<String, String>();
						int i = data.indexOf('?');
						if (i > -1) {
							String[] props = data.substring(i + 1).split("&"); //$NON-NLS-1$
							for (String set : props) {
								String[] kv = set.split("="); //$NON-NLS-1$
								properties.put(kv[0], kv[1]);
							}
						}
						IBuildServer bs = BuildsUi.createServer(connectorKind);
						RepositoryLocation rl = new RepositoryLocation(properties);
						bs.getLocation().apply(rl);
						((BuildServerWizard) wizard).setBuildServer(bs);
					} catch (Exception e) {
						StatusHandler.log(new Status(IStatus.ERROR, BuildsUiPlugin.ID_PLUGIN,
								Messages.BuildsServiceMessageControl_ErrorStartingWizard, e));
					}
				}
				WizardDialog wd = new WizardDialog(getShell(), wizard);
				wd.setTitle(wizard.getWindowTitle());
				wd.open();
			}
		} catch (CoreException e) {
			StatusHandler.log(new Status(IStatus.ERROR, BuildsUiPlugin.ID_PLUGIN,
					Messages.BuildsServiceMessageControl_ErrorStartingWizard, e));
		}
	}

	@Override
	protected SelectionListener getLinkListener() {
		return new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				if (e.text != null) {
					String cmd = null;
					String args = null;
					if (e.text.contains(":")) { //$NON-NLS-1$
						cmd = e.text.substring(0, e.text.indexOf(':')).toLowerCase();
						args = e.text.substring(e.text.indexOf(':') + 1);
					} else {
						cmd = e.text.toLowerCase();
					}
					if (cmd.equals("create")) { //$NON-NLS-1$
						closeMessage();
						new NewBuildServerAction().run();
					} else if (cmd.equals("discover")) { //$NON-NLS-1$
						closeMessage();
						if (args != null) {
							openRepositoryWizard(args);
						}
					}
				}
			}
		};
	}

	public void notify(final NotificationSinkEvent event) {
		for (final AbstractNotification message : event.getNotifications()) {
			messages.add(message);
		}
		// Show the next message but only if we're currently not
		// showing any messages.
		Display.getDefault().asyncExec(new Runnable() {
			public void run() {
				if (isClosed()) {
					nextMessage();
				}
			}
		});
	}
}
