/*******************************************************************************
 * Copyright (c) 2010 Tasktop Technologies 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:
 *     Tasktop Technologies - initial API and implementation
 *     Itema AS - Added support for build service messages 
 *     Itema AS - Automatic refresh when a new repo has been added; bug 330910
 *******************************************************************************/

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

import java.io.IOException;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.mylyn.builds.ui.BuildsUiStartup;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.statushandlers.StatusManager;
import org.osgi.framework.BundleContext;

/**
 * @author Steffen Pingel
 * @author Torkild U. Resheim
 */
public class BuildsUiPlugin extends AbstractUIPlugin {

	public static final String ID_PLUGIN = "org.eclipse.mylyn.builds.ui"; //$NON-NLS-1$

	private static BuildsUiPlugin instance;

	public static BuildsUiPlugin getDefault() {
		return instance;
	}

	private BuildRefresher refresher;

	private BuildNotifier notifier;

	private boolean startupExtensionsInitialized;

	@Override
	public void start(BundleContext context) throws Exception {
		instance = this;
		super.start(context);
	}

	@Override
	public void stop(BundleContext context) throws Exception {
		if (refresher != null) {
			getPreferenceStore().removePropertyChangeListener(refresher);
			refresher.stop();
			refresher = null;
		}
		try {
			BuildsUiInternal.save();
		} catch (IOException e) {
			StatusManager.getManager().handle(
					new Status(IStatus.ERROR, BuildsUiPlugin.ID_PLUGIN, "Unexpected error while saving builds", e));
		}
		super.stop(context);
		instance = null;
	}

	protected IPath getBuildsFile() {
		IPath stateLocation = Platform.getStateLocation(getBundle());
		IPath cacheFile = stateLocation.append("builds.xmi"); //$NON-NLS-1$
		return cacheFile;
	}

	public void initializeRefresh() {
		if (notifier == null) {
			notifier = new BuildNotifier();
			notifier.register(BuildsUiInternal.getModel());
		}

		if (refresher == null) {
			refresher = new BuildRefresher();
			getPreferenceStore().addPropertyChangeListener(refresher);
			refresher.start();
		}

		if (!startupExtensionsInitialized) {
			// start automatic discovery services etc.
			UiStartupExtensionPointReader.runStartupExtensions();
			startupExtensionsInitialized = true;
		}
	}

	/**
	 * Performs a one-shot refresh of build server data regardless of the automatic refresh preference setting. This
	 * method should be called when build service settings has been changed in a way that require update of the data.
	 * For instance when a new repository has been added.
	 */
	public void refreshBuilds() {
		initializeRefresh();
		refresher.refresh();

		// delay the save until other async tasks complete to ensure that the model is updated before it's persisted 
		BuildsUiInternal.getModel().getLoader().getRealm().asyncExec(new Runnable() {
			public void run() {
				// trigger a save in case Eclipse crashes and stop() is not executed
				try {
					BuildsUiInternal.save();
				} catch (IOException e) {
					StatusManager.getManager().handle(
							new Status(IStatus.ERROR, BuildsUiPlugin.ID_PLUGIN, "Unexpected error while saving builds",
									e));
				}
			}
		});
	}

	static class UiStartupExtensionPointReader {

		private static final String EXTENSION_ID_STARTUP = "org.eclipse.mylyn.builds.ui.startup"; //$NON-NLS-1$

		private static final String ELEMENT_STARTUP = "startup"; //$NON-NLS-1$

		private static final String ELEMENT_CLASS = "class"; //$NON-NLS-1$

		public static void runStartupExtensions() {
			IExtensionRegistry registry = Platform.getExtensionRegistry();
			IExtensionPoint extensionPoint = registry.getExtensionPoint(EXTENSION_ID_STARTUP);
			IExtension[] extensions = extensionPoint.getExtensions();
			for (IExtension extension : extensions) {
				IConfigurationElement[] elements = extension.getConfigurationElements();
				for (IConfigurationElement element : elements) {
					if (element.getName().compareTo(ELEMENT_STARTUP) == 0) {
						runStartupExtension(element);
					}
				}
			}
		}

		private static void runStartupExtension(IConfigurationElement configurationElement) {
			try {
				Object object = configurationElement.createExecutableExtension(ELEMENT_CLASS);
				if (!(object instanceof BuildsUiStartup)) {
					StatusHandler.log(new Status(IStatus.ERROR, BuildsUiPlugin.ID_PLUGIN, NLS.bind(
							"Startup extension failed: {0} does notimplement {1}", //$NON-NLS-1$
							object.getClass().getCanonicalName(), BuildsUiStartup.class.getCanonicalName())));
					return;
				}
				final BuildsUiStartup startup = (BuildsUiStartup) object;
				SafeRunner.run(new ISafeRunnable() {
					public void run() throws Exception {
						startup.lazyStartup();
					}

					public void handleException(Throwable exception) {
						// ignore, handled by SafeRunner						
					}
				});
			} catch (Throwable e) {
				StatusHandler.log(new Status(IStatus.ERROR, BuildsUiPlugin.ID_PLUGIN, "Startup extension failed", e)); //$NON-NLS-1$
			}
		}

	}

}
