/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.badlogic.gdx.utils;

/** A sorted double linked list which uses ints for indexing
 * 
 * @param <E> */
public class SortedIntList<E> implements Iterable<SortedIntList.Node<E>> {
	private NodePool<E> nodePool = new NodePool<E>(); // avoid allocating nodes
	private Iterator iterator;
	int size = 0;

	Node<E> first;

	/** Creates an ascending list */
	public SortedIntList () {
	}

	/** Inserts an element into the list at the given index
	 * 
	 * @param index Index of the element
	 * @param value Element to insert
	 * @return Element replaced by newly inserted element, null if nothing was replaced */
	public E insert (int index, E value) {
		if (first != null) {
			Node<E> c = first;
			// iterate to the right until we can't move any further because the next number is bigger than index
			while (c.n != null && c.n.index <= index) {
				c = c.n;
			}
			// add one to the right
			if (index > c.index) {
				c.n = nodePool.obtain(c, c.n, value, index);
				if (c.n.n != null) {
					c.n.n.p = c.n;
				}
				size++;
			}
			// the new element is smaller than every other element
			else if (index < c.index) {
				Node<E> newFirst = nodePool.obtain(null, first, value, index);
				first.p = newFirst;
				first = newFirst;
				size++;
			}
			// that element already exists so swap the value
			else {
				c.value = value;
			}
		} else {
			first = nodePool.obtain(null, null, value, index);
			size++;
		}
		return null;
	}

	/** Retrieves an element at a given index
	 * 
	 * @param index Index of the element to retrieve
	 * @return Matching element, null otherwise */
	public E get (int index) {
		E match = null;
		if (first != null) {
			Node<E> c = first;
			while (c.n != null && c.index < index) {
				c = c.n;
			}
			if (c.index == index) {
				match = c.value;
			}
		}
		return match;
	}

	/** Clears list */
	public void clear () {
		for (; first != null; first = first.n) {
			nodePool.free(first);
		}
		size = 0;
	}

	/** @return size of list equal to elements contained in it */
	public int size () {
		return size;
	}

	/** Returns an iterator to traverse the list.<br/>
	 * Only one iterator can be active per list at any given time.
	 * 
	 * @return Iterator to traverse list */
	public java.util.Iterator<Node<E>> iterator () {
		if (iterator == null) {
			iterator = new Iterator();
		}
		return iterator.reset();
	}

	class Iterator implements java.util.Iterator<Node<E>> {
		private Node<E> position;
		private Node<E> previousPosition;

		@Override
		public boolean hasNext () {
			return position != null;
		}

		@Override
		public Node<E> next () {
			previousPosition = position;
			position = position.n;
			return previousPosition;
		}

		@Override
		public void remove () {
			// the contract specifies to remove the last returned element, if nothing was returned yet assumably do nothing
			if (previousPosition != null) {
				// if we are at the second element set it as the first element
				if (previousPosition == first) {
					first = position;
				}
				// else remove last returned element by changing the chain
				else {
					previousPosition.p.n = position;
					if (position != null) {
						position.p = previousPosition.p;
					}
				}
				size--;
			}
		}

		public Iterator reset () {
			position = first;
			previousPosition = null;
			return this;
		}
	}

	public static class Node<E> {
		/** Node previous to this */
		protected Node<E> p;
		/** Node next to this */
		protected Node<E> n;
		/** Value held */
		public E value;
		/** Index value in list */
		public int index;
	}

	static class NodePool<E> extends Pool<Node<E>> {
		@Override
		protected Node<E> newObject () {
			return new Node<E>();
		}

		public Node<E> obtain (Node<E> p, Node<E> n, E value, int index) {
			Node<E> newNode = super.obtain();
			newNode.p = p;
			newNode.n = n;
			newNode.value = value;
			newNode.index = index;
			return newNode;
		}
	}
}
