/*******************************************************************************
 * Copyright (c) 2000, 2010 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.draw2d;

import java.util.Iterator;

/**
 * This class is intended for internal use only. TODO: If this is for internal
 * use only, we should move it to the internal package.
 */
public final class EventListenerList {

	private volatile Object array[];

	/**
	 * Adds a listener of type <i>c</i> to the list.
	 * 
	 * @param c
	 *            the class
	 * @param listener
	 *            the listener
	 */
	public synchronized void addListener(Class c, Object listener) {
		if (listener == null || c == null)
			throw new IllegalArgumentException();

		int oldSize = (array == null) ? 0 : array.length;
		Object[] newArray = new Object[oldSize + 2];
		if (oldSize != 0)
			System.arraycopy(array, 0, newArray, 0, oldSize);
		newArray[oldSize++] = c;
		newArray[oldSize] = listener;
		array = newArray;
	}

	/**
	 * Returns <code>true</code> if this list of listeners contains a listener
	 * of type <i>c</i>.
	 * 
	 * @param c
	 *            the type
	 * @return whether this list contains a listener of type <i>c</i>
	 */
	public synchronized boolean containsListener(Class c) {
		if (array == null)
			return false;
		for (int i = 0; i < array.length; i += 2)
			if (array[i] == c)
				return true;
		return false;
	}

	static class TypeIterator implements Iterator {
		private final Object[] items;
		private final Class type;
		private int index;

		TypeIterator(Object items[], Class type) {
			this.items = items;
			this.type = type;
		}

		public Object next() {
			Object result = items[index + 1];
			index += 2;
			return result;
		}

		public boolean hasNext() {
			if (items == null)
				return false;
			while (index < items.length && items[index] != type)
				index += 2;
			return index < items.length;
		}

		public void remove() {
			throw new UnsupportedOperationException(
					"Iterator removal not supported"); //$NON-NLS-1$
		}
	}

	/**
	 * Returns an Iterator of all the listeners of type <i>c</i>.
	 * 
	 * @param listenerType
	 *            the type
	 * @return an Iterator of all the listeners of type <i>c</i>
	 */
	public synchronized Iterator getListeners(final Class listenerType) {
		return new TypeIterator(array, listenerType);
	}

	/**
	 * Removes the first <i>listener</i> of the specified type by identity.
	 * 
	 * @param c
	 *            the type
	 * @param listener
	 *            the listener
	 */
	public synchronized void removeListener(Class c, Object listener) {
		if (array == null || array.length == 0)
			return;
		if (listener == null || c == null)
			throw new IllegalArgumentException();

		int index = 0;
		while (index < array.length) {
			if (array[index] == c && array[index + 1] == listener)
				break;
			index += 2;
		}
		if (index == array.length)
			return; // listener was not found

		Object newArray[] = new Object[array.length - 2];
		System.arraycopy(array, 0, newArray, 0, index);
		System.arraycopy(array, index + 2, newArray, index, array.length
				- index - 2);
		array = newArray;
	}

}
