/*
 * Created on 30.12.2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package de.farafin.snEADy.communication;


/**
 * this class is a implemention of a ring buffer. it is based on a array that contains
 * the data. if the size of the array is too small, it will be automatically resized.
 * 
 * (my first intention was to extend from java.util.Vector, but because i can not
 * ovewride the resize methode, so it was not possible to implement a ring vector)
 *  
 * 
 * @author lars, roland
 *
 * @see java.util.Vector
 */ 
public final class RingVector
{
	/** the index of the head of the data */
	private int headIndex = 0;
	
	/** the length of this part of the array that contains data. */
	private int definedLength = 0;

	/** the array that conatins the data */
	private Object dataArray[] = null;
	
	/** default constructor 
	 * creats a new, empty RingVector with a capacity of 10
	 *  */
	public RingVector()
	{
		this.headIndex = 0;
		this.definedLength = 0;
		this.dataArray = new Object[10];
	
	}
	
	/** creats a new, empty RingVector with a capacity of <code>size</code>
	 * @param size the size of the RingVector
	 */
	public RingVector(int size)
	{
		this.headIndex = 0;
		this.definedLength = 0;
		this.dataArray = new Object[size];
	}
	
	/** doubles the size of the array */
	private void doubleSize()
	{
		Object[] newArray = new Object[this.dataArray.length<<1];
		
		for(int i=0; i<this.definedLength; i++)
			newArray[i] = this.dataArray[(i + this.headIndex)%this.definedLength];
		
		this.headIndex = 0;
		this.dataArray = newArray;
	}
	
		
	/** adds a element at the end
	 * @param obj
	 */
	public void addLast(Object obj)
	{
		if(this.definedLength == this.dataArray.length) doubleSize();
	
		this.dataArray[(this.headIndex + this.definedLength)%this.dataArray.length] = obj;
		
		this.definedLength++;
	}
	
	/** adds a element at the end
	 * @param obj
	 */
	public void addFirst(Object obj)
	{
		if(this.definedLength == this.dataArray.length) doubleSize();
		this.headIndex--;
		if(this.headIndex<0)this.headIndex+=this.dataArray.length;
		this.dataArray[headIndex] = obj;
		this.definedLength++;
	}
	
	/** remove the last Object. sets the reference to null.
	 * @return the removed Object
	 */
	public Object remLast()
	{
		if(this.definedLength <= 0) return null;
		this.definedLength--;
		Object obj = this.dataArray[(this.headIndex + this.definedLength)%this.dataArray.length];
		this.dataArray[(this.headIndex + this.definedLength)%this.dataArray.length] = null;
		return obj;
	}
	

	/** remove the first Object. sets the reference to null.
	 * @return the removed Object
	 */
	public Object remFirst()
	{
		if(this.definedLength <= 0) return null;
		Object obj = this.dataArray[this.headIndex];
		this.dataArray[(this.headIndex + this.definedLength)%this.dataArray.length] = null;

		this.headIndex = (this.headIndex + 1)%this.dataArray.length;
		this.definedLength--;
		return obj;
	}
	
	/** replaces the <code>i</code>'th element with <code>obj</code>
	 * @param i the index of the element that should be replaced
	 * @param obj the new object
	 * @return the old object at the place of <code>i</code>
	 */
	public Object replaceAt(int i, Object obj)
	{
		if(this.definedLength <= 0) return null;
		Object old = this.dataArray[(this.headIndex+i)%this.dataArray.length];
		this.dataArray[(this.headIndex+i)%this.dataArray.length] = obj;
		return old;
	}
	
	/** move the last Element and put it to the front.
	 * @return the moved element
	 */
	public Object moveLastToFirst()
	{
		// TODO #roland: compute this more effective!
		if(this.definedLength <= 0) return null;
		Object obj = this.remLast();
		this.addFirst(obj);
		return obj;
	}

	/** move the last Element and put it to the front.
	 * @return the moved element
	 */
	public Object moveFirstToLast()
	{
		// TODO #roland: compute this more effective!
		if(this.definedLength <= 0) return null;
		Object obj = this.remFirst();
		this.addLast(obj);
		return obj;	
	}
	
	/** 
	 * @param i index of the element
	 * @return returns the element at i or if there is no element or the index is out
	 * of range, it returns 0
	 */
	public Object getElementAt(int i)
	{
		if(this.definedLength <= 0 || definedLength < i) return null;
		return this.dataArray[(headIndex + i)%this.dataArray.length];
	}
	
	/** @return the first object of the array */
	public Object getFirst() {return this.getElementAt(0);}

	/** @return the first object of the array */
	public Object getLast() {return this.getElementAt(this.definedLength-1);}
	
	/** @return Returns the length of the with data defined part. */
	public int size(){return definedLength;}
	
	/** sets all Elements to null and clear the RingVector so that there is no data defined any more. */
	public void clear()
	{
		for(int i = 0; i < this.dataArray.length; i++)
		{
			this.dataArray[i] = null;
		}
		this.definedLength = 0;
		this.headIndex = 0;
	}
	
	/**
	 * @return the ringVector as array
	 */
	public Object[] toArray()
	{
		Object[] newArray = new Object[this.dataArray.length];
		
		for(int i=0; i<this.definedLength; i++)
			newArray[i] = this.dataArray[(i + this.headIndex)%this.definedLength];
		
		return newArray;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */
	public Object clone()
	{
		RingVector ring = new RingVector(this.dataArray.length);

		for(int i=0; i<this.definedLength; i++)
			ring.dataArray[i] = this.dataArray[(i + this.headIndex)%this.definedLength];
		
		ring.definedLength = this.definedLength;
		
		return ring;
	}
}
