/*******************************************************************************
 * Copyright (c) 2007, 2008 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 ******************************************************************************/

package org.eclipse.ui.internal.statushandlers;

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

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.dynamichelpers.ExtensionTracker;
import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler;
import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.WorkbenchPlugin;

/**
 * The registry of status handlers extensions.
 * 
 * @since 3.3
 * 
 */
public class StatusHandlerRegistry implements IExtensionChangeHandler {

	private static final String STATUSHANDLERS_POINT_NAME = "statusHandlers"; //$NON-NLS-1$

	private static final String TAG_STATUSHANDLER = "statusHandler"; //$NON-NLS-1$

	private static final String TAG_STATUSHANDLER_PRODUCTBINDING = "statusHandlerProductBinding"; //$NON-NLS-1$
	
	private static final String STATUSHANDLER_ARG = "-statushandler"; //$NON-NLS-1$

	private ArrayList statusHandlerDescriptors = new ArrayList();

	private ArrayList productBindingDescriptors = new ArrayList();

	private StatusHandlerDescriptorsMap statusHandlerDescriptorsMap;

	private StatusHandlerDescriptor defaultHandlerDescriptor;

	private static StatusHandlerRegistry instance;

	/**
	 * Creates an instance of the class.
	 */
	private StatusHandlerRegistry() {
		IExtensionTracker tracker = PlatformUI.getWorkbench()
				.getExtensionTracker();
		IExtensionPoint handlersPoint = Platform.getExtensionRegistry()
				.getExtensionPoint(WorkbenchPlugin.PI_WORKBENCH,
						STATUSHANDLERS_POINT_NAME);
		IExtension[] extensions = handlersPoint.getExtensions();

		statusHandlerDescriptorsMap = new StatusHandlerDescriptorsMap();

		// initial population
		for (int i = 0; i < extensions.length; i++) {
			addExtension(tracker, extensions[i]);
		}

		tracker.registerHandler(this, ExtensionTracker
				.createExtensionPointFilter(handlersPoint));

		// registers on products ext. point to, needed
		// for changing the default handler if product is changed
		IExtensionPoint productsPoint = Platform.getExtensionRegistry()
				.getExtensionPoint(Platform.PI_RUNTIME, Platform.PT_PRODUCT);

		tracker.registerHandler(this, ExtensionTracker
				.createExtensionPointFilter(productsPoint));
	}

	/**
	 * Returns StatusHandlerRegistry singleton instance.
	 * 
	 * @return StatusHandlerRegistry instance
	 */
	public static StatusHandlerRegistry getDefault() {
		if (instance == null) {
			instance = new StatusHandlerRegistry();
		}
		return instance;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler#addExtension(org.eclipse.core.runtime.dynamichelpers.IExtensionTracker,
	 *      org.eclipse.core.runtime.IExtension)
	 */
	public void addExtension(IExtensionTracker tracker, IExtension extension) {
		IConfigurationElement[] configElements = extension
				.getConfigurationElements();
		for (int j = 0; j < configElements.length; j++) {
			if (configElements[j].getName().equals(TAG_STATUSHANDLER)) {
				StatusHandlerDescriptor descriptor = new StatusHandlerDescriptor(
						configElements[j]);
				tracker.registerObject(extension, descriptor,
						IExtensionTracker.REF_STRONG);
				statusHandlerDescriptors.add(descriptor);
			} else if (configElements[j].getName().equals(
					TAG_STATUSHANDLER_PRODUCTBINDING)) {
				StatusHandlerProductBindingDescriptor descriptor = new StatusHandlerProductBindingDescriptor(
						configElements[j]);
				tracker.registerObject(extension, descriptor,
						IExtensionTracker.REF_STRONG);
				productBindingDescriptors.add(descriptor);
			}
		}
		buildHandlersStructure();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler#removeExtension(org.eclipse.core.runtime.IExtension,
	 *      java.lang.Object[])
	 */
	public void removeExtension(IExtension extension, Object[] objects) {
		for (int i = 0; i < objects.length; i++) {
			if (objects[i] instanceof StatusHandlerDescriptor) {
				statusHandlerDescriptors.remove(objects[i]);
			} else if (objects[i] instanceof StatusHandlerProductBindingDescriptor) {
				productBindingDescriptors.remove(objects[i]);
			}
		}
		buildHandlersStructure();
	}

	/**
	 * Returns the default product handler descriptor, or null if the product is
	 * not defined or there is no product binding
	 * 
	 * @return the default handler
	 */
	public StatusHandlerDescriptor getDefaultHandlerDescriptor() {
		return defaultHandlerDescriptor;
	}

	/**
	 * Returns a list of handler descriptors which should be used for statuses
	 * with given plugin id.
	 * 
	 * @param pluginId
	 * @return list of handler descriptors
	 */
	public List getHandlerDescriptors(String pluginId) {
		return statusHandlerDescriptorsMap.getHandlerDescriptors(pluginId);
	}

	/**
	 * Returns status handler descriptor for given id.
	 * 
	 * @param statusHandlerId
	 *            the id to get for
	 * @return the status handler descriptor
	 */
	public StatusHandlerDescriptor getHandlerDescriptor(String statusHandlerId) {
		StatusHandlerDescriptor descriptor = null;
		for (Iterator it = statusHandlerDescriptors.iterator(); it.hasNext();) {
			descriptor = (StatusHandlerDescriptor) it.next();
			if (descriptor.getId().equals(statusHandlerId)) {
				return descriptor;
			}
		}

		if (defaultHandlerDescriptor != null
				&& defaultHandlerDescriptor.getId().equals(statusHandlerId)) {
			return defaultHandlerDescriptor;
		}

		return null;
	}

	/**
	 * Disposes the registry.
	 */
	public void dispose() {
		PlatformUI.getWorkbench().getExtensionTracker().unregisterHandler(this);
	}

	/**
	 * It is possible since Eclipse 3.5 to configure custom status handling
	 * using the -statushandler parameter.
	 * 
	 * @return the id of the statushandler
	 * @since 3.5
	 */
	private String resolveUserStatusHandlerId(){
		String[] parameters = Platform.getCommandLineArgs();
		
		for(int i = 0; i < parameters.length - 1; i++){
			if(STATUSHANDLER_ARG.equals(parameters[i])){
				return parameters[i + 1];
			}
		}
		return null;
	}
	
	/**
	 * Sets the default product handler descriptor if product exists and binding
	 * is defined and creates handler descriptors tree due to the prefix policy.
	 */
	private void buildHandlersStructure() {
		statusHandlerDescriptorsMap.clear();
		defaultHandlerDescriptor = null;

		String productId = Platform.getProduct() != null ? Platform
				.getProduct().getId() : null;

		List allHandlers = new ArrayList();

		String defaultHandlerId = resolveUserStatusHandlerId();

		if (defaultHandlerId == null) {
			// we look for product related statushandler if it was not passed as
			// an argument to Eclipse
			for (Iterator it = productBindingDescriptors.iterator(); it
					.hasNext();) {
				StatusHandlerProductBindingDescriptor descriptor = ((StatusHandlerProductBindingDescriptor) it
						.next());

				if (descriptor.getProductId().equals(productId)) {
					defaultHandlerId = descriptor.getHandlerId();
				}
			}
		}

		for (Iterator it = statusHandlerDescriptors.iterator(); it.hasNext();) {
			StatusHandlerDescriptor descriptor = ((StatusHandlerDescriptor) it
					.next());

			allHandlers.add(descriptor);
		}

		StatusHandlerDescriptor handlerDescriptor = null;

		for (Iterator it = allHandlers.iterator(); it.hasNext();) {
			handlerDescriptor = (StatusHandlerDescriptor) it.next();

			if (handlerDescriptor.getId().equals(defaultHandlerId)) {
				defaultHandlerDescriptor = handlerDescriptor;
			} else {
				statusHandlerDescriptorsMap
						.addHandlerDescriptor(handlerDescriptor);
			}
		}
	}
}
