/******************************************************************************
 * Copyright (c) 2006, 2010 VMware Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution. 
 * The Eclipse Public License is available at 
 * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
 * is available at http://www.opensource.org/licenses/apache2.0.php.
 * You may elect to redistribute this code under either of these licenses. 
 * 
 * Contributors:
 *   VMware Inc.
 *****************************************************************************/

package org.eclipse.gemini.blueprint.service.importer.support.internal.collection;

import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;

/**
 * Subclass offering a List extension for a DynamicCollection. This allows not
 * just forward, but also backwards iteration through the
 * <code>ListIterator</list>.
 * 
 * @author Costin Leau
 * 
 */
public class DynamicList<E> extends DynamicCollection<E> implements List<E>, RandomAccess {

	/**
	 * List iterator.
	 * 
	 * @author Costin Leau
	 * 
	 */
	private class DynamicListIterator extends DynamicIterator implements ListIterator<E> {

		/**
		 * Similar to {@link DynamicIterator#tailGhost} in functionality but
		 * representing the last seen object in the head of the collection.
		 */
		protected volatile E headGhost = null;

		// flag used for enforcing the iterator consistency:
		// null - do not enforce anything
		// true - should not throw exception
		// false - should throw exception
		/**
		 * Iterator variable - not thread-safe/synchronized since only one
		 * thread should use the iterator.
		 */
		protected Boolean hasPrevious = null;

		/**
		 * Boolean field used by the {@link #set(Object)} and {@link #remove()}
		 * operation. True indicates next() was called, and false previous().
		 */
		private boolean previousOperationCalled = true;


		private DynamicListIterator(int index) {
			super.cursor = index;
		}

		public void add(E o) {
			removalAllowed = false;
			synchronized (storage) {
				synchronized (lock) {
					DynamicList.this.add(cursor, o);
				}
			}
		}

		/**
		 * Updates the hasPrevious field.
		 * 
		 * Internal unprotected method to avoid nested synchronization blocks.
		 * To execute this code, one needs the storage, iteratorsLock and
		 * iterator lock.
		 * 
		 * @return
		 */
		private boolean unsafeHasPrevious() {
			hasPrevious = (cursor - 1 >= 0 ? Boolean.TRUE : Boolean.FALSE);
			return hasPrevious.booleanValue();
		}

		public boolean hasPrevious() {
			synchronized (lock) {
				headGhost = null;
				return unsafeHasPrevious();
			}
		}

		public int nextIndex() {
			synchronized (lock) {
				return cursor;
			}
		}

		public E next() {
			previousOperationCalled = true;
			return super.next();
		}

		public E previous() {
			try {
				removalAllowed = true;
				previousOperationCalled = false;
				// no enforcement
				if (hasPrevious == null) {
					synchronized (storage) {
						synchronized (lock) {
							if (unsafeHasPrevious())
								return storage.get(--cursor);
							else
								throw new NoSuchElementException();
						}
					}
				}
				// need to return an object no matter what
				else if (hasPrevious.booleanValue()) {
					synchronized (storage) {
						synchronized (lock) {
							// if there is an element available, return it
							if (unsafeHasPrevious()) {
								return storage.get(--cursor);
							}
							else {
								// otherwise return the last one seen
								return headGhost;
							}
						}
					}
				}
				// should throw exception no matter what
				else {
					throw new NoSuchElementException();
				}
			}
			finally {
				// no matter what, reset hasPrevious
				hasPrevious = null;
				// remove ghost object
				synchronized (lock) {
					headGhost = null;
				}
			}
		}

		public int previousIndex() {
			synchronized (lock) {
				return (cursor - 1);
			}
		}

		public void set(E o) {
			if (!removalAllowed)
				throw new IllegalStateException();
			synchronized (storage) {
				synchronized (lock) {
					int index = (previousOperationCalled ? cursor - 1 : cursor);
					if (index < 0) {
						index = 0;
					}
					else {
						int length = storage.size();
						if (index > length) {
							index = length;
						}
					}
					storage.set(index, o);
				}
			}
		}

		protected int removalIndex(int cursor) {
			int index = (previousOperationCalled ? cursor - 1 : cursor);
			if (index < 0) {
				index = 0;
			}
			else {
				int length;
				synchronized (storage) {
					length = storage.size();
				}
				if (index > length) {
					index = length;
				}
			}
			return index;
		}
	}


	public DynamicList() {
		super();
	}

	public DynamicList(Collection<? extends E> c) {
		super(c);
	}

	public DynamicList(int size) {
		super(size);
	}

	public void add(int index, E o) {
		super.add(index, o);
	}

	public boolean addAll(int index, Collection<? extends E> c) {
		synchronized (storage) {
			return storage.addAll(index, c);
		}
	}

	public E get(int index) {
		synchronized (storage) {
			return storage.get(index);
		}
	}

	public int indexOf(Object o) {
		synchronized (storage) {
			return storage.indexOf(o);
		}
	}

	public int lastIndexOf(Object o) {
		synchronized (storage) {
			return storage.lastIndexOf(o);
		}
	}

	public ListIterator<E> listIterator() {
		DynamicListIterator iter = new DynamicListIterator(0);

		synchronized (iterators) {
			iterators.put(iter, null);
		}

		return iter;
	}

	public ListIterator<E> listIterator(int index) {
		return new DynamicListIterator(index);
	}

	public E remove(int index) {
		return super.remove(index);
	}

	public E set(int index, E o) {
		synchronized (storage) {
			return storage.set(index, o);
		}
	}

	// TODO: test behavior to see if the returned list properly behaves under
	// dynamic circumstances
	public List<E> subList(int fromIndex, int toIndex) {
		synchronized (storage) {
			return storage.subList(fromIndex, toIndex);
		}
	}
}