/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.collection.lists;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
import net.ranides.assira.collection.AIntCollection;
import net.ranides.assira.collection.IntCollection;
import net.ranides.assira.collection.arrays.NativeArrayAllocator;
import net.ranides.assira.collection.arrays.ArrayAllocator;
import net.ranides.assira.collection.arrays.ArrayUtils;
import net.ranides.assira.collection.iterators.IntIterator;
import net.ranides.assira.collection.iterators.IntListIterator;
import net.ranides.assira.collection.iterators.RandomAccessIntIterator;
import net.ranides.assira.generic.CompareUtils;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public abstract class AIntList extends AIntCollection implements IntList, Comparable<List<? extends Integer>>, RandomAccess, Serializable {
    
    /**
     * Ensures that the given index is nonnegative and not greater than the list
     * size.
     *
     * @param index an index.
     * @throws IndexOutOfBoundsException if the given index is negative or
     * greater than the list size.
     */
    protected void checkIndex(int index) {
        if (index < 0) {
            throw new IndexOutOfBoundsException("Index (" + index + ") is negative");
        }
        if (index > size()) {
            throw new IndexOutOfBoundsException("Index (" + index + ") is greater than list size (" + (size()) + ")");
        }
    }
    
    /**
     * Ensures that the given index is nonnegative and smaller than the list
     * size.
     *
     * @param index an index.
     * @throws IndexOutOfBoundsException if the given index is negative or not
     * smaller than the list size.
     */
    protected void checkIndexElement(int index) {
        if (index < 0) {
            throw new IndexOutOfBoundsException("Index (" + index + ") is negative");
        }
        if (index >= size()) {
            throw new IndexOutOfBoundsException("Index (" + index + ") is greater than or equal to list size (" + (size()) + ")");
        }
    }
	
	@Override
	public boolean add(int k) {
		add(size(), k);
		return true;
	}
    
    @Override
    public boolean addAll(int index, Collection<? extends Integer> values) {
        checkIndex(index);
        if( values.isEmpty()) {
            return false;
        }
        Iterator<? extends Integer> iterator = values.iterator();
        while (iterator.hasNext()) {
            add(index++, iterator.next());
        }
        return true;
    }

    @Override
    public boolean addAll(Collection<? extends Integer> values) {
        return addAll(size(), values);
    }

    @Override
    public IntListIterator iterator() {
        return listIterator();
    }

    @Override
    public IntListIterator listIterator() {
        return listIterator(0);
    }
    
    @Override
    public IntListIterator listIterator(int index) {
        checkIndex(index);
        return new IIterator(index);
    }

    @Override
    public boolean contains(int k) {
        return indexOf(k) >= 0;
    }

    @Override
    public void addElements(int index, int a[]) {
        addElements(index, a, 0, a.length);
    }

    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        }
        if (!(other instanceof List)) {
            return false;
        }
        List<?> list = (List<?>) other;
        int s = size();
        if (s != list.size()) {
            return false;
        }
        ListIterator<?> iterator1 = listIterator(), iterator2 = list.listIterator();
        while (s-- != 0) {
            if (!CompareUtils.equals(iterator1.next(), iterator2.next())) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * Compares this list to another object. If the argument is a
     * {@link java.util.List}, this method performs a lexicographical
     * comparison; otherwise, it throws a
     * <code>ClassCastException</code>.
     *
     * @param list
     * @return if the argument is a {@link java.util.List}, a negative integer,
     * zero, or a positive integer as this list is lexicographically less than,
     * equal to, or greater than the argument.
     * @throws ClassCastException if the argument is not a list.
     */
    @Override
    public int compareTo(List<? extends Integer> list) {
        if (list == this) {
            return 0;
        }
        if (list instanceof IntList) {
            return compareTo(listIterator(), ((IntList)list).listIterator());
        } else {
            return compareTo(listIterator(), list.listIterator());
        }
    }
    
    private static int compareTo(ListIterator<? extends Integer> it1, ListIterator<? extends Integer> it2) {
        while (it1.hasNext() && it2.hasNext()) {
            int r = it1.next().compareTo(it2.next());
            if(r != 0) {
                return r;
            }
        }
        return it2.hasNext() ? -1 : (it1.hasNext() ? 1 : 0);
    }

    private static int compareTo(IntListIterator it1, IntListIterator it2) {
        while (it1.hasNext() && it2.hasNext()) {
            int r = CompareUtils.cmp(it1.nextInt(), it2.nextInt());
            if (r != 0) {
                return r;
            }
        }
        return it2.hasNext() ? -1 : (it1.hasNext() ? 1 : 0);
    }

    /**
     * Returns the hash code for this list, which is identical to
     * {@link java.util.List#hashCode()}.
     *
     * @return the hash code for this list.
     */
    @Override
    public int hashCode() {
        IntIterator itr = iterator();
        int h = 1, s = size();
        while (s-- != 0) {
            int k = itr.nextInt();
            h = 31 * h + (k);
        }
        return h;
    }
    
    @Override
    public void push(int o) {
        add(o);
    }

    @Override
    public int pop() {
        if (isEmpty()) {
            throw new NoSuchElementException();
        }
        return removeInt(size() - 1);
    }

    @Override
    public int top() {
        if (isEmpty()) {
            throw new NoSuchElementException();
        }
        return getInt(size() - 1);
    }

    @Override
    public int peek(int i) {
		int index = size() - 1 - i;
		if (index<0) {
            throw new NoSuchElementException();
        }
        return getInt(index);
    }

    @Override
    public boolean remove(Object value) {
        return rem(((Integer) value).intValue());
    }

    @Override
    public boolean addAll(IntCollection values) {
        return addAll(size(), values);
    }

    @Override
    public boolean addAll(IntList list) {
        return addAll(size(), list);
    }
    
    @Override
    public void addElements(int index, int array[], int offset, int length) {
        checkIndex(index);
        addAll(index, NativeArrayList.wrap(array, offset, length));
    }

    @Override
    public void add(int index, Integer value) {
        add(index, value.intValue());
    }

    @Override
    public Integer set(int index, Integer value) {
        return set(index, value.intValue());
    }

    @Override
    public Integer get(int index) {
        return getInt(index);
    }

    @Override
    public int indexOf(Object value) {
        return indexOf( ((Integer) value).intValue() );
    }

    @Override
    public int lastIndexOf(Object value) {
        return lastIndexOf( ((Integer)value).intValue() );
    }

    @Override
    public Integer remove(int index) {
        return removeInt(index);
    }

    @Override
    public int indexOf(int k) {
        IntListIterator iterator = listIterator();
        for (int i = 0, n = size(); i < n; i++) {
            if (k == iterator.nextInt()) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public int lastIndexOf(int k) {
        int n = size();
        IntListIterator iterator = listIterator(n);
        for (int i = n; i-- != 0;) {
            if (k == iterator.previousInt()) {
                return i;
            }
        }
        return -1;
    }
    
    @Override
    public boolean rem(int k) {
        int index = indexOf(k);
        if (index == -1) {
            return false;
        }
        removeInt(index);
        return true;
    }
    
    @Override
    public void clear() {
        IntIterator iterator = listIterator();
        while(iterator.hasNext()) {
            iterator.nextInt();
            iterator.remove();
        }
    }
    
    @Override
    public boolean isEmpty() {
        return size() == 0;
    }
    
    /**
     * Removes elements of this type-specific list using optimized system calls.
     *
     * @param from the start index (inclusive).
     * @param to the end index (exclusive).
     */
    @Override
    public void removeElements(int from, int to) {
        NativeArrayAllocator.ensureFromTo(size(), from, to);
        IntIterator iterator = listIterator(from);
        for(int i=0, n=to-from; i<n; i++) {
            iterator.nextInt();
            iterator.remove();
        }
    }
    
    @Override
    public int[] toIntArray(int target[]) {
        target = ArrayAllocator.ensureCapacity(target, size());
        ArrayUtils.collect(listIterator(), target);
        return target;
    }
    
    @Override
    public void getElements(int from, int[] target, int offset, int length) {
        ArrayUtils.collect(listIterator(from), target, offset, length);
    }
    
    @Override
    public boolean addAll(int index, IntCollection values) {
        checkIndex(index);
        int n = values.size();
        if (n == 0) {
            return false;
        }
        
        IntIterator source = values.iterator();
        for(int i=index; source.hasNext(); i++) {
            add(i, source.nextInt());
        }
        return true;
    }
    
    @Override
    public boolean addAll(int index, IntList values) {
        return addAll(index, (IntCollection)values);
    }
    
    @Override
    public IntList subList(int from, int to) {
        checkIndex(from);
        checkIndex(to);
        if (from > to) {
            throw new IllegalArgumentException("Start index (" + from + ") is greater than end index (" + to + ")");
        }
        return new SubList(this, from, to);
    }
    
    protected static class SubList extends AIntList implements Serializable {

        private static final long serialVersionUID = 2;

        protected final IntList data;
        protected final int begin;
        protected int end;

        public SubList(IntList list, int begin, int end) {
            this.data = list;
            this.begin = begin;
            this.end = end;
        }

        @Override
        public void add(int index, int k) {
            data.add(begin + index, k);
            end++;
        }

        @Override
        public boolean addAll(int index, Collection<? extends Integer> values) {
            if(data.addAll(begin + index, values)) {
                end += values.size();
                return true;
            }
            return false;
        }

        @Override
        public int getInt(int index) {
            return data.getInt(begin + index);
        }

        @Override
        public int removeInt(int index) {
            int prev = data.removeInt(begin + index);
            end--;
            return prev;
        }

        @Override
        public int set(int index, int k) {
            return data.set(begin + index, k);
        }

        @Override
        public void clear() {
            removeElements(0, size());
            end = begin;
        }

        @Override
        public int size() {
            return end - begin;
        }

        @Override
        public void getElements(int from, int[] target, int offset, int length) {
            data.getElements(this.begin + from, target, offset, length);
        }

        @Override
        public void removeElements(int begin, int end) {
            data.removeElements(this.begin + begin, this.begin + end);
            this.end -= (end - begin);
        }

        @Override
        public void addElements(int index, int a[], int offset, int length) {
            data.addElements(this.begin + index, a, offset, length);
            this.end += length;
        }

        @Override
        public IntListIterator listIterator(int index) {
            return new SIterator(index);
        }

        @Override
        public IntList subList(int from, int to) {
            NativeArrayAllocator.ensureFromTo(size(), from, to);
            return new SubList(this, from, to);
        }

        @Override
        public boolean addAll(int index, IntCollection values) {
            if(data.addAll(begin + index, values)) {
                end += values.size();
                return true;
            }
            return false;
        }

        @Override
        public boolean addAll(int index, IntList values) {
            if(data.addAll(begin + index, values)) {
                end += values.size();
                return true;
            }
            return false;
        }
        
        protected final class SIterator implements IntListIterator {
            
            private final IntListIterator delegate;

			public SIterator(int index) {
				this.delegate = data.listIterator(begin + index);
			}

			@Override
			public boolean hasNext() {
				return delegate.nextIndex() < end;
			}

			@Override
			public int nextInt() {
				if(!hasNext()) {
					throw new NoSuchElementException();
				}
				return delegate.nextInt();
			}

			@Override
			public boolean hasPrevious() {
				return delegate.previousIndex() >= begin;
			}

			@Override
			public int previousInt() {
				if(!hasPrevious()) {
					throw new NoSuchElementException();
				}
				return delegate.previousInt();
			}

			@Override
			public int nextIndex() {
				return delegate.nextIndex() - begin;
			}

			@Override
			public int previousIndex() {
				return delegate.previousIndex() - begin;
			}

			@Override
			public void remove() {
				delegate.remove();
				end--;
			}

			@Override
			public void set(int e) {
				delegate.set(e);
			}

			@Override
			public void add(int e) {
				delegate.add(e);
				end++;
			}
        }
    }

    protected class IIterator extends RandomAccessIntIterator {

        public IIterator(int pos) {
            super(pos);
        }

        @Override
        protected int size() {
            return AIntList.this.size();
        }

        @Override
        protected int get(int index) {
            return AIntList.this.get(index);
        }

        @Override
        protected void set(int index, int value) {
            AIntList.this.set(index, value);
        }

        @Override
        protected void add(int index, int value) {
            AIntList.this.add(index, value);
        }

        @Override
        protected void remove(int index) {
            AIntList.this.remove(index);
        }

    }
    
}
