package kr.util;

/**
 * A ring buffer represents a continous block along a larger logical array.
 */
public class RingBuffer<T extends Object> {
	private int pos, size;
	private int ringBufferPos = 0;
	private Object[] data;
	
	/**
	 * Creates a ringbuffer with a given maximum length
	 */
	public RingBuffer(int maxLength)
	{
		this.data = new Object[maxLength];
	}

	/**
	 * Returns the logical position of the last index
	 */
	public int getLastIndex() {
		return pos + size -1;
	}

	/**
	 * Shifts the last position in the buffer to the given value. The 
	 * amount the buffer shifted is returned
	 */
	public int shiftEndTo(int index) {
		int shiftAmount = index - (pos + size - 1);
		pos += shiftAmount;
		ringBufferPos = (ringBufferPos + shiftAmount) % data.length;
		
		return shiftAmount;
	}

	/**
	 * Gets the value of a given index position
	 */
	public T get(int i) {
		//the extra modulation is to deal with java's idea that -15 % 16 = -15 
		return (T)data[((i - pos + ringBufferPos) % data.length + data.length) % data.length];
	}

	/**
	 * Sets the size of the continous block held. This cannot be larger
	 * then the maximum length specified in the constructor
	 */
	public void setSize(int size) {
		this.size = size;
	}

	/**
	 * Sets the object without concern for the logical position
	 * @param i internal position of the array
	 */
	public void setInternal(int i, T obj) {
		data[i] = obj;
	}

	public int getFirstIndex() {
		return pos;
	}

	public int getSize() {
		return size;
	}
}

class RingBufferTest
{
	public static void main(String [] argv)
	{
		int rsize = 100;
		RingBuffer<Integer> rb = new RingBuffer<Integer>(rsize);
		
		for(int i = 0; i < rsize; i++)
		{
			rb.setInternal(i, i);
		}
		
		rb.setSize(rsize);
		
		for(int i = 0; i < 100; i++)
		{
			System.out.println("rb "+i+" is "+rb.get(i));
		}
		
		for(int shift = -150; shift < 150; shift += 50)
		{
			System.out.println("Shift: "+shift);
			rb.shiftEndTo(shift);
			for(int i = shift; i < 100+shift; i++)
			{
				System.out.println("rb "+i+" is "+rb.get(i));
			}
		}
		
	}
}
