package freenet.support;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * DoublyLinkedList implementation. See DoublyLinkedList for an explanation
 * when to use this.
 * 
 * @author tavin
 *
 * TODO: there are still some unimplemented methods
 *       -- it remains to be seen if they are needed at all
 */
public class DoublyLinkedListImpl<T extends DoublyLinkedList.Item<? extends T>> implements DoublyLinkedList<T> {

    protected int size;
	protected T _firstItem, _lastItem;

	//@Override
	//public final DoublyLinkedListImpl<T> clone() {
	//    return new DoublyLinkedListImpl<T>(this);
	//}
    
    /**
     * A new list with no items.
     */
    public DoublyLinkedListImpl() {
        clear();
    }

	protected DoublyLinkedListImpl(T _h, T _t, int size) {
		_firstItem = _h;
		_lastItem = _t;
        
        T i = _firstItem;
        while (i != null ) {
        	i.setParent(this);
			i = i.getNext();
        }
        
        this.size = size;
    }

	//	/**
	//	 * 
	//	 * XXX: FIXME this doesn't really work. it use .clone() method
	//	 * 
	//	 * @param impl
	//	 */
	//    protected DoublyLinkedListImpl(DoublyLinkedListImpl<T> impl) {
	//        this();
	//        Enumeration<T> e = impl.forwardElements();
	//        boolean checked = false;
	//        for(;e.hasMoreElements();) {
	//			DoublyLinkedListImpl.Item oi = (DoublyLinkedListImpl.Item) e.nextElement();
	//			T i = (T) oi.clone();
	//            if(!checked) {
	//                checked = true;
	//                if(!i.getClass().getName().equals(oi.getClass().getName())) {
	//                    System.err.println("Clone constructor failed for "+oi+": "+i);
	//                    new Exception("error").printStackTrace();
	//                }
	//            }
	//            this.push(i);
	//        }
	//    }

    //=== DoublyLinkedList implementation ======================================

    /**
     * {@inheritDoc}
     */
    @Override
    public void clear() {
    	// Help to detect removal after clear().
    	// The check in remove() is enough, strictly,
    	// as long as people don't add elements afterwards.
		if (_firstItem == null)
			return;

		T pos = _firstItem;
		T opos;

    	while(true) {
    		if(pos == null) break;
    		pos.setParent(null);
    		pos.setPrev(null);
    		opos = pos;
			pos = pos.getNext();
    		opos.setNext(null);
    	}

		_firstItem = _lastItem = null;
        size = 0;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final int size() {
		assert size != 0 || (_firstItem == null && _lastItem == null);
        return size;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean isEmpty() {
		assert size != 0 || (_firstItem == null && _lastItem == null);
        return size == 0;
    }

    /**
     * {@inheritDoc}
     * @see #forwardElements()
     */
    @Override
    public final Enumeration<T> elements() {
        return forwardElements();
    }
    
	@Override
	public boolean contains(T item) {
    	for(T i : this) {
    		if(i.equals(item))
    			return true;
    	}
    	return false;
    }
    
    /**
     * {@inheritDoc}
     */
	@Override
	public final T head() {
		return size == 0 ? null : _firstItem;
    }

    /**
     * {@inheritDoc}
     */
	@Override
	public final T tail() {
		return size == 0 ? null : _lastItem;
    }


    //=== methods that add/remove items at the head of the list ================
    /**
     * {@inheritDoc}
     */
	@Override
	public final void unshift(T i) {
		insertNext(null, i);
    }
    
    /**
     * {@inheritDoc}
     */
	@Override
	public final T shift() {
		return size == 0 ? null : remove(_firstItem);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public DoublyLinkedList<T> shift(int n) {
        if (n > size) n = size;
        if (n < 1) return new DoublyLinkedListImpl<T>();

		T i = _firstItem;
		for (int m = 0; m < n - 1; ++m)
			i = i.getNext();

		T newTailItem = i;
		T newFirstItem = newTailItem.getNext();
		newTailItem.setNext(null);

		DoublyLinkedList<T> newlist = new DoublyLinkedListImpl<T>(_firstItem, newTailItem, n);

		if (newFirstItem != null) {
			newFirstItem.setPrev(null);
			_firstItem = newFirstItem;
		} else {
			_firstItem = _lastItem = null;
		}
        size -= n;
        
        return newlist;
    }
    
    
    //=== methods that add/remove items at the tail of the list ================
    /**
     * {@inheritDoc}
     */
	@Override
	public final void push(T i) {
		insertPrev(null, i);
    }
    
    /**
     * {@inheritDoc}
     */
	@Override
	public final T pop() {
		return size == 0 ? null : remove(_lastItem);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DoublyLinkedList<T> pop(int n) {
        if (n > size) n = size;
        if (n < 1) return new DoublyLinkedListImpl<T>();

		T i = _lastItem;
		for (int m = 0; m < n - 1; ++m)
			i = i.getPrev();

		T newFirstItem = i;
		T newLastItem = newFirstItem.getPrev();
		newFirstItem.setPrev(null);

		DoublyLinkedList<T> newlist = new DoublyLinkedListImpl<T>(newFirstItem, _lastItem, n);

		if (newLastItem != null) {
			newLastItem.setNext(null);
			_lastItem = newLastItem;
		} else
			_firstItem = _lastItem = null;
        size -= n;
        
        return newlist;
    }

    
    //=== testing/looking at neighbor items ====================================
    /**
     * {@inheritDoc}
     */
	@Override
	public final boolean hasNext(T i) {
		T next = i.getNext();
		return next != null;
    }
    /**
     * {@inheritDoc}
     */
	@Override
	public final boolean hasPrev(T i) {
		T prev = i.getPrev();
		return prev != null;
    }
    /**
     * {@inheritDoc}
     */
	@Override
	public final T next(T i) {
		T next = i.getNext();
		return next;
    }
    /**
     * {@inheritDoc}
     */
	@Override
	public final T prev(T i) {
		T prev = i.getPrev();
		return prev;
    }


    //=== insertion and removal of items =======================================
    
    /**
     * {@inheritDoc}
     */
	@Override
	public T remove(T i) {
    	if (i.getParent() == null || isEmpty())
			return null; // not in list
    	if (i.getParent() != this)
    		throw new PromiscuousItemException(i, i.getParent());

		T next = i.getNext();
		T prev = i.getPrev();

		if ((next == null) && (prev == null)) // only item in list
			assert size == 1;

		if (next == null) { // last item
			assert _lastItem == i;
			_lastItem = prev;
		} else {
			assert next.getPrev() == i;
			next.setPrev(prev);
		}

		if (prev == null) { // first item
			assert _firstItem == i;
			_firstItem = next;
		} else {
			assert prev.getNext() == i;
			prev.setNext(next);
        }

        i.setNext(null);
        i.setPrev(null);
        --size;
        i.setParent(null);
        return i;
    }

    /**
     * {@inheritDoc}
     */
	@Override
	public void insertPrev(T i, T j) {
    	if (j.getParent() != null)
    		throw new PromiscuousItemException(j, j.getParent());
        if ((j.getNext() != null) || (j.getPrev() != null))
            throw new PromiscuousItemException(j);

		if (i == null) {
			// insert as tail
			j.setPrev(_lastItem);
			j.setNext(null);
			j.setParent(this);
			if (_lastItem != null) {
				_lastItem.setNext(j);
				_lastItem = j;
			} else {
				_firstItem = _lastItem = j;
			}

			++size;
		} else {
			// insert in middle
			if (i.getParent() == null)
				throw new PromiscuousItemException(i, i.getParent()); // different trace to make easier debugging
			if (i.getParent() != this)
				throw new PromiscuousItemException(i, i.getParent());
			T prev = i.getPrev();
			if (prev == null) {
				if (i != _firstItem)
					throw new VirginItemException(i);
				_firstItem = j;
			} else
				prev.setNext(j);
			j.setPrev(prev);
			i.setPrev(j);
			j.setNext(i);
			j.setParent(this);

			++size;
		}
    }

    /**
     * {@inheritDoc}
     */
	@Override
	public void insertNext(T i, T j) {
    	if (j.getParent() != null)
    		throw new PromiscuousItemException(j, i.getParent());
        if ((j.getNext() != null) || (j.getPrev() != null))
            throw new PromiscuousItemException(j);

		if (i == null) {
			// insert as head
			j.setPrev(null);
			j.setNext(_firstItem);
			j.setParent(this);

			if (_firstItem != null) {
				_firstItem.setPrev(j);
				_firstItem = j;
			} else {
				_firstItem = _lastItem = j;
			}

			++size;
		} else {
			if (i.getParent() != this)
				throw new PromiscuousItemException(i, i.getParent());
			T next = i.getNext();
			if (next == null) {
				if (i != _lastItem)
					throw new VirginItemException(i);
				_lastItem = j;
			} else
				next.setPrev(j);
			j.setNext(next);
			i.setNext(j);
			j.setPrev(i);
			j.setParent(this);

			++size;
		}
    }

    //=== Walkable implementation ==============================================
    
    /**
     * @return  an Enumeration of list elements from head to tail
     */
    private Enumeration<T> forwardElements() {
        return new ForwardWalker();
    }

    /**
     * @return  an Enumeration of list elements from tail to head
     */
    public Enumeration<T> reverseElements() {
        return new ReverseWalker();
    }

	private class ForwardWalker implements Enumeration<T> {
		protected T next;
        protected ForwardWalker() {
			next = _firstItem;
        }
        @Override
        public final boolean hasMoreElements() {
			return next != null;
        }

		@Override
		public T nextElement() {
			if (next == null)
                throw new NoSuchElementException();
			T result = next;
            next = next.getNext();
			return result;
        }
    }

	private class ReverseWalker implements Enumeration<T> {
		protected T next;
        protected ReverseWalker() {
			next = _lastItem;
        }
        @Override
        public final boolean hasMoreElements() {
			return next != null;
        }

		@Override
		public T nextElement() {
			if (next == null)
                throw new NoSuchElementException();
			T result = next;
	    if(next == null) throw new IllegalStateException("next==null");
            next = next.getPrev();
			return result;
        }
    }


    //=== list element ====================================================

	public static class Item<T extends DoublyLinkedListImpl.Item<?>> implements DoublyLinkedList.Item<T> {
		private T prev;
		private T next;
		private DoublyLinkedList<? super T> list;

		//		@Override
		//		public T clone() {
		//            if(getClass() != Item.class)
		//                throw new RuntimeException("Must implement clone() for "+getClass());
		//			return (T) new Item<T>();
		//        }

		@Override
		public final T getNext() {
            return next;
        }

		@Override
		@SuppressWarnings("unchecked")
		public final T setNext(DoublyLinkedList.Item<?> i) {
			T old = next;
			next = (T) i;
            return old;
        }

		@Override
		public final T getPrev() {
            return prev;
        }

		@Override
		@SuppressWarnings("unchecked")
		public final T setPrev(DoublyLinkedList.Item<?> i) {
			T old = prev;
			prev = (T) i;
            return old;
        }

		@Override
		public DoublyLinkedList<? super T> getParent() {
			return list;
		}

		@Override
		public DoublyLinkedList<? super T> setParent(DoublyLinkedList<? super T> l) {
			DoublyLinkedList<? super T> old = list;
			list = l;
			return old;
		}
    }

	@Override
	public Iterator<T> iterator() {
		return new Iterator<T>() {
			private Enumeration<T> e = forwardElements();

			@Override
			public boolean hasNext() {
				return e.hasMoreElements();
			}

			@Override
			public T next() {
				if(!hasNext())
					throw new NoSuchElementException();
				
				return e.nextElement();
			}

			@Override
			public void remove() {
				throw new UnsupportedOperationException();
			}
			
		};
	}
}