/* 
 
Copyright 2006 Rene Grothmann, modified by Eric Hakenholz

This file is part of C.a.R. software.

    C.a.R. is a free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3 of the License.

    C.a.R. is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 
 
 package rene.util;

import java.util.Enumeration;

/**
 * This is a more effective replacement of the Vector class. It is based on a
 * growing array. If an object is removed, it is replaced by null. The class
 * knows about the first null object (the gap). Searching for elements or other
 * operations automatically compress the array by copying the elements upwards,
 * but only as far as they need to go anyway.
 * 
 * Accessing an element is very effective, at least the second time. If you want
 * to make sure, it is always effective, compress first. The most effective way
 * is to get the object array itself.
 * 
 * The objects can be enumerated. The object returned by nextElement() is found
 * very rapidly. E.g. it can be deleted at once.
 * 
 * Enumeration is not reentrant. Do it only once each time.
 * 
 * Nothing in this class is synchronized!
 **/

public class MyVector implements Enumeration {
	Object O[];
	int OSize, ON, OLast, Gap;
	int EN = 0;

	public MyVector(final int initsize) {
		O = new Object[initsize];
		OSize = initsize;
		OLast = ON = 0;
		Gap = -1;
	}

	public MyVector() {
		this(8);
	}

	/**
	 * Add an element. Extend the array, if necessary.
	 */
	public void addElement(final Object o) {
		if (OLast >= OSize)
			extend();
		O[OLast++] = o;
		ON++;
	}

	/**
	 * Extend the array, or get space by compressing it.
	 */
	public void extend() {
		if (ON < OLast / 2) {
			compress();
			return;
		}
		final Object o[] = new Object[2 * OSize];
		System.arraycopy(O, 0, o, 0, OLast);
		OSize *= 2;
		O = o;
	}

	/**
	 * Compress the array.
	 */
	public void compress() {
		if (Gap < 0)
			return;
		int k = Gap;
		for (int i = Gap; i < OLast; i++) {
			if (O[i] == null)
				continue;
			O[k++] = O[i];
		}
		ON = k;
		for (int i = k; i < OLast; i++)
			O[i] = null;
		Gap = -1;
		OLast = ON;
	}

	/**
	 * Get an enumeration of this array.
	 */
	public Enumeration elements() {
		compress();
		EN = 0;
		return this;
	}

	/**
	 * Method for Enumeration.
	 */
	public boolean hasMoreElements() {
		while (EN < OLast && O[EN] == null)
			EN++;
		return EN < OLast;
	}

	/**
	 * Method for Enumeration.
	 */
	public Object nextElement() {
		if (!hasMoreElements())
			throw new ArrayIndexOutOfBoundsException(OLast);
		return O[EN++];
	}

	/**
	 * Clear this array, but keep its memory!
	 */
	public void removeAllElements() {
		for (int i = 0; i < OLast; i++)
			O[i] = null;
		ON = OLast = 0;
		Gap = -1;
	}

	/**
	 * Remove a single element. This will also compress the part below the
	 * element, or all, if it is not found.
	 */
	public void removeElement(final Object o) {
		final int i = indexOf(o);
		if (i < 0)
			return;
		O[i] = null;
		ON--;
		if (Gap < 0 || Gap > i)
			Gap = i;
		if (i == OLast - 1)
			OLast--;
		while (OLast > 0 && O[OLast - 1] == null)
			OLast--;
		if (Gap >= OLast)
			Gap = -1;
	}

	/**
	 * Find an element. Compress on the way. Check for the last element,
	 * returned by nextElement() first. Equality is checked with the equal()
	 * function.
	 * 
	 * @return -1, if not found.
	 */
	public int indexOf(final Object o) {
		if (EN > 0 && EN <= OLast && O[EN - 1].equals(o))
			return EN - 1;
		if (Gap < 0) {
			for (int i = 0; i < OLast; i++) {
				if (O[i].equals(o))
					return i;
			}
			return -1;
		}
		for (int i = 0; i < Gap; i++) {
			if (O[i].equals(o))
				return i;
		}
		int k = Gap;
		for (int i = Gap; i < OLast; i++) {
			if (O[i] == null)
				continue;
			if (O[i].equals(o)) {
				Gap = k;
				return i;
			}
			O[k++] = O[i];
			O[i] = null;
		}
		ON = k;
		for (int i = k; i < OLast; i++)
			O[i] = null;
		Gap = -1;
		OLast = ON;
		return -1;
	}

	/**
	 * @return the number of objects in the vector.
	 */
	public int size() {
		return ON;
	}

	/**
	 * Get the element at a given position. Second access will always be
	 * effective. First access compresses. Throws an exception, if the index is
	 * invalid.
	 */
	public Object elementAt(final int n) {
		if (n < 0 || n >= ON)
			throw new ArrayIndexOutOfBoundsException(n);
		if (Gap < 0 || n < Gap)
			return O[n];
		int k = Gap;
		for (int i = Gap; i < OLast; i++) {
			if (O[i] == null)
				continue;
			O[k] = O[i];
			O[i] = null;
			if (k == n) {
				final Object ret = O[k];
				k++;
				Gap = k;
				if (Gap >= ON) {
					for (int j = Gap; j < OLast; j++)
						O[j] = null;
					OLast = ON;
					Gap = -1;
				}
				return ret;
			}
			k++;
		}
		// never happens
		throw new ArrayIndexOutOfBoundsException(n);
	}

	/**
	 * Get the array itself (compressed). Make sure, you also use size() to
	 * determine the true length of the array. Do not change objects beyond the
	 * size! Do not set objects to null!
	 */
	public Object[] getArray() {
		compress();
		return O;
	}

	/**
	 * Copy the array into an object array of at least the same size.
	 */
	public void copyInto(final Object o[]) {
		compress();
		System.arraycopy(O, 0, o, 0, ON);
	}

	/**
	 * Test for equality with another vector, using equals.
	 */
	public boolean equals(final MyVector V) {
		if (V.ON != ON)
			return false;
		V.compress();
		compress();
		for (int i = 0; i < ON; i++) {
			if (!V.O[i].equals(O[i]))
				return false;
		}
		return true;
	}

	/**
	 * Test for equality with another vector, using object equality.
	 */
	public boolean equalsIdentical(final MyVector V) {
		if (V.ON != ON)
			return false;
		V.compress();
		compress();
		for (int i = 0; i < ON; i++) {
			if (V.O[i] != O[i])
				return false;
		}
		return true;
	}

	/**
	 * Trancate the vector to n elements, if it has more.
	 */
	public void truncate(final int n) {
		if (n >= ON)
			return;
		compress();
		for (int i = n; i < OLast; i++)
			O[i] = null;
		OLast = ON = n;
	}

	public static void main(final String args[]) {
		final MyVector V = new MyVector();
		for (int i = 1; i <= 10; i++)
			V.addElement("Element " + i);
		for (int i = 4; i <= 9; i++)
			V.removeElement("Element " + i);
		System.out.println("--> " + V.elementAt(3));
		System.out.println(V.ON + " elements, " + V.OLast + " used, " + V.Gap
				+ " gap.");
		System.out.println("--> " + V.elementAt(3));
		System.out.println(V.ON + " elements, " + V.OLast + " used, " + V.Gap
				+ " gap.");
		for (int i = 11; i <= 20; i++)
			V.addElement("Element " + i);
		System.out.println(V.ON + " elements, " + V.OLast + " used ," + V.Gap
				+ " gap.");
		final Enumeration E = V.elements();
		while (E.hasMoreElements()) {
			System.out.println((String) E.nextElement());
		}
		System.out.println(V.ON + " elements, " + V.OLast + " used, " + V.Gap
				+ " gap.");
	}
}
