/*
 * Written by Dawid Kurzyniec and released to the public domain, as explained
 * at http://creativecommons.org/licenses/publicdomain
 */

package edu.emory.mathcs.util.collections.ints;

import java.util.*;

/**
 * Array-based list of int numbers.
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */
public class IntArrayList extends AbstractIntCollection implements IntList {

    int[] buffer;
    int size;

    /**
     * Creates a IntArrayList with initial capacity of 10.
     */
    public IntArrayList() {
        this(10);
    }

    /**
     * Creates a IntArrayList with the specified initial capacity.
     */
    public IntArrayList(int initialCapacity) {
        if (initialCapacity < 0) throw new IllegalArgumentException();
        this.buffer = new int[initialCapacity];
    }

    /**
     * Creates a IntArrayList, copying to it all elements from the specified
     * int collection, in the order as returned by that collection's iterator.
     */
    public IntArrayList(IntCollection c) {
        int cap = c.size();
        cap += cap/10;
        if (cap < 0) cap = Integer.MAX_VALUE;
        this.buffer = new int[cap];
        addAll(c);
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public boolean contains(int e) {
        for (int i=0; i<size; i++) {
            if (buffer[i] == e) return true;
        }
        return false;
    }

    public IntIterator iterator() {
        return new Itr(0);
    }

    public int[] toArray() {
        int[] a = new int[size];
        System.arraycopy(buffer, 0, a, 0, size);
        return a;
    }

    public int[] toArray(int[] a) {
        if (a.length < size) {
            a = new int[size];
        }
        System.arraycopy(buffer, 0, a, 0, size);
        return a;
    }

    private void ensureCapacity(int capacity) {
        if (capacity < 0) throw new IllegalArgumentException();
        if (capacity > buffer.length) {
            int newlen = 2*buffer.length;
            if (newlen < buffer.length) newlen = Integer.MAX_VALUE; // overflow
            if (newlen < capacity) newlen = capacity;
            int[] newbuf = new int[newlen];
            System.arraycopy(buffer, 0, newbuf, 0, size);
            this.buffer = newbuf;
        }
    }

    public boolean add(int e) {
        ensureCapacity(size+1);
        buffer[size++] = e;
        return true;
    }

    public boolean remove(int e) {
        for (int i=0; i<size; i++) {
            if (buffer[i] == e) {
                System.arraycopy(buffer, i+1, buffer, i, size-i-1);
                size--;
                return true;
            }
        }
        return false;
    }

    public boolean addAll(IntCollection c) {
        ensureCapacity(size + c.size());
        for (IntIterator itr=c.iterator(); itr.hasNext();) {
            buffer[size++] = itr.next();
        }
        return true;
    }

    public boolean addAll(int index, IntCollection c) {
        int csize = c.size();
        ensureCapacity(size + csize);
        System.arraycopy(buffer, index, buffer, index+csize, size-index);
        for (IntIterator itr=c.iterator(); itr.hasNext();) {
            buffer[index++] = itr.next();
        }
        return true;
    }

    public void clear() {
        size = 0;
    }

    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof IntList)) return false;
        IntList that = (IntList)o;
        if (this.size() != that.size()) return false;
        for (int i=0; i<size; i++) {
            if (buffer[i] != that.getAt(i)) return false;
        }
        return true;
    }

    public int hashCode() {
        int hashCode = 1;
        for (int i=0; i<size; i++) {
            hashCode = 31*hashCode + (int)buffer[i];
        }
        return hashCode;
    }

    public int getAt(int index) {
        if (index >= size) throw new IndexOutOfBoundsException("" + index);
        return buffer[index];
    }

    public int setAt(int index, int e) {
        if (index >= size) throw new IndexOutOfBoundsException("" + index);
        int old = buffer[index];
        buffer[index] = e;
        return old;
    }

    public void addAt(int index, int e) {
        if (index >= size) throw new IndexOutOfBoundsException("" + index);
        ensureCapacity(size+1);
        System.arraycopy(buffer, index, buffer, index+1, size-index);
        buffer[index] = e;
        size++;
    }

    public int removeAt(int index) {
        if (index >= size) throw new IndexOutOfBoundsException("" + index);
        int e = buffer[index];
        System.arraycopy(buffer, index+1, buffer, index, size-index-1);
        size--;
        return e;
    }

    public int indexOf(int e) {
        for (int i=0; i<size; i++) {
            if (buffer[i] == e) return i;
        }
        return -1;
    }

    public int lastIndexOf(int e) {
        for (int i=size-1; i>=0; --i) {
            if (buffer[i] == e) return i;
        }
        return -1;
    }

    public IntListIterator listIterator() {
        return new Itr(0);
    }

    public IntListIterator listIterator(int index) {
        return new Itr(index);
    }

    public IntList subList(int fromIndex, int toIndex) {
        throw new UnsupportedOperationException();
    }

    public int size64() {  
        return size;        
    }                       
                            
    class Itr implements IntListIterator {
        int cursor = 0;
        int last   = -1;
        Itr(int idx) {
            cursor = idx;
        }
        public int nextIndex() {
            return cursor;
        }
        public boolean hasNext() {
            return cursor < size();
        }
        public int next() {
            try {
                int e = getAt(cursor);
                last = ++cursor;
                return e;
            }
            catch (IndexOutOfBoundsException e) {
                throw new NoSuchElementException();
            }
        }
        public int previousIndex() {
            return cursor-1;
        }
        public boolean hasPrevious() {
            return cursor > 0;
        }
        public int previous() {
            try {
                int e = getAt(cursor-1);
                last = --cursor;
                return e;
            }
            catch (IndexOutOfBoundsException e) {
                throw new NoSuchElementException();
            }
        }
        public void set(int e) {
            if (last < 0) throw new IllegalStateException();
            IntArrayList.this.setAt(last, e);
        }
        public void add(int e) {
            IntArrayList.this.addAt(cursor++, e);
            last = -1;
        }
        public void remove() {
            if (last < 0) throw new IllegalStateException();
            IntArrayList.this.removeAt(last);
            if (last < cursor) cursor--;
            last = -1;
        }
    }
}
