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

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

import java.util.*;

/**
 * Array-based list of long numbers.
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */
public class LongArrayList extends AbstractLongCollection implements LongList {

    long[] buffer;
    int size;

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

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

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

    public int size() {
        return size;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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