/*
 * Copyright 2009-2013 Yuichiro Moriguchi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.morilib.sh.misc;

import java.io.UnsupportedEncodingException;
import java.util.NoSuchElementException;

public class ByteRingBuffer implements java.io.Serializable {

	//
	private static final long serialVersionUID = 5015372387740172285L;

	//
	private byte[] buffer;
	private int ptrt;
	private int ptrb;

	/**
	 * 
	 * @param size
	 */
	public ByteRingBuffer(int size) {
		buffer = new byte[size];
		ptrt = 0;
		ptrb = -1;
	}

	private void incb() {
		ptrb = ptrb < buffer.length ? ptrb + 1 : 1;
	}

	private void inct() {
		ptrt = ptrt < buffer.length ? ptrt + 1 : 1;
	}

	private void dect() {
		ptrt = ptrt > 0 ? ptrt - 1 : buffer.length - 1;
	}

	private int index(int c) {
		return (ptrb + c) % buffer.length;
	}

	/**
	 * 
	 * @param c
	 * @return
	 */
	public boolean add(byte c) {
		if(ptrb < 0) {
			ptrb = 0;
		} else if(ptrt % buffer.length == ptrb % buffer.length) {
			incb();
		}
		buffer[ptrt < buffer.length ? ptrt : 0] = c;
		inct();
		return size() < buffer.length;
	}

	/**
	 * 
	 * @return
	 */
	public byte remove() {
		byte res;

		if(ptrb < 0)  throw new NoSuchElementException();
		dect();
		res = buffer[ptrt];
		if(ptrb == ptrt)  ptrb = -1;
		return res;
	}

	/**
	 * 
	 * @return
	 */
	public int size() {
		if(ptrb < 0)  return 0;
		return ptrt <= ptrb ?
				ptrt + buffer.length - ptrb : ptrt - ptrb;
	}

	/**
	 * 
	 * @param c
	 * @return
	 */
	public byte get(int c) {
		if(c >= buffer.length) {
			throw new IndexOutOfBoundsException("" + c);
		} else if(c >= size()) {
			throw new NoSuchElementException();
		}
		return buffer[index(c)];
	}

	/**
	 * 
	 * @return
	 */
	public boolean isEmpty() {
		return ptrb < 0;
	}

	/**
	 * 
	 */
	public void clear() {
		ptrt = 0;
		ptrb = -1;
	}

	/**
	 * 
	 * @param a
	 */
	public void add(byte[] a, int len) {
		byte[] b;
		int s, t;

		if(len == 0) {
			// do nothing
		} else if(len >= buffer.length) {
			s = len - buffer.length;
			System.arraycopy(a, s, buffer, 0, buffer.length);
			ptrb = ptrt = 0;
		} else {
			b = new byte[buffer.length];
			toArray(b);
			if(len > buffer.length - size()) {
				s = buffer.length - len;
				t = size() - s;
				System.arraycopy(b, t, buffer, 0, s);
				System.arraycopy(a, 0, buffer, s, len);
				ptrb = ptrt = 0;
			} else {
				s = size();
				System.arraycopy(a, 0, b, s, len);
				buffer = b;
				ptrb = 0;  ptrt = s + len;
			}
		}
	}

	/**
	 * 
	 * @param a
	 */
	public void add(byte[] a) {
		add(a, a.length);
	}

	/**
	 * 
	 * @param a
	 * @return
	 */
	public byte[] toArray(byte[] a) {
		int b = ptrb, e = ptrt, z, x;

		if(ptrb < 0)  return new byte[0];
		z = b >= e ? e + buffer.length : e;
		if(a.length < size())  a = new byte[z - b];
		if(z <= buffer.length) {
			System.arraycopy(buffer, b, a, 0, z);
		} else {
			x = buffer.length - b;
			System.arraycopy(buffer, b, a, 0, x);
			System.arraycopy(buffer, 0, a, x, a.length - x);
		}
		return a;
	}

	/**
	 * 
	 * @return
	 */
	public byte[] toArray() {
		return toArray(new byte[0]);
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		try {
			return new String(toArray(), "ISO-8859-1");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
	}

}
