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

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

import java.util.*;

/**
 * Array-based list of short numbers.
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */
public class ShortArrayList extends AbstractShortCollection implements ShortList {

    short[] buffer;
    int size;

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

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

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

    public int size() {
        return size;
    }

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

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

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

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

    public short[] toArray(short[] a) {
        if (a.length < size) {
            a = new short[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;
            short[] newbuf = new short[newlen];
            System.arraycopy(buffer, 0, newbuf, 0, size);
            this.buffer = newbuf;
        }
    }

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

    public boolean remove(short 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(ShortCollection c) {
        ensureCapacity(size + c.size());
        for (ShortIterator itr=c.iterator(); itr.hasNext();) {
            buffer[size++] = itr.next();
        }
        return true;
    }

    public boolean addAll(int index, ShortCollection c) {
        int csize = c.size();
        ensureCapacity(size + csize);
        System.arraycopy(buffer, index, buffer, index+csize, size-index);
        for (ShortIterator 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 ShortList)) return false;
        ShortList that = (ShortList)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 short getAt(int index) {
        if (index >= size) throw new IndexOutOfBoundsException("" + index);
        return buffer[index];
    }

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

    public void addAt(int index, short 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 short removeAt(int index) {
        if (index >= size) throw new IndexOutOfBoundsException("" + index);
        short e = buffer[index];
        System.arraycopy(buffer, index+1, buffer, index, size-index-1);
        size--;
        return e;
    }

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

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

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

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

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

    class Itr implements ShortListIterator {
        int cursor = 0;
        int last   = -1;
        Itr(int idx) {
            cursor = idx;
        }
        public int nextIndex() {
            return cursor;
        }
        public boolean hasNext() {
            return cursor < size();
        }
        public short next() {
            try {
                short 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 short previous() {
            try {
                short e = getAt(cursor-1);
                last = --cursor;
                return e;
            }
            catch (IndexOutOfBoundsException e) {
                throw new NoSuchElementException();
            }
        }
        public void set(short e) {
            if (last < 0) throw new IllegalStateException();
            ShortArrayList.this.setAt(last, e);
        }
        public void add(short e) {
            ShortArrayList.this.addAt(cursor++, e);
            last = -1;
        }
        public void remove() {
            if (last < 0) throw new IllegalStateException();
            ShortArrayList.this.removeAt(last);
            if (last < cursor) cursor--;
            last = -1;
        }
    }
}
