package com.asreader.asr033w.asr033w.util;

public class ASR033WByteQueue {

	private static final String TAG = ASR033WByteQueue.class.getSimpleName();

	private static final int DEFAULT_QUEUE_SIZE = 1024 * 8;

	private byte[] mData;
	private int mFront;
	private int mTail;

	public ASR033WByteQueue() {
		mData = new byte[DEFAULT_QUEUE_SIZE];
		mFront = mTail = 0;
	}

	public synchronized int getSize() {
		return mData.length;
	}

	public synchronized int getFront() {
		return mFront;
	}

	public synchronized int getTail() {
		return mTail;
	}

	public synchronized int getCount() {
		return getCount(mFront, mTail);
	}

	public synchronized int getCount(int start, int end) {
		return start <= end ? end - start : mData.length + end - start;
	}

	public synchronized int getRemainSize() {
		return mData.length - getCount();
	}
	
	public int next(int pos) {
		return pos % mData.length;
	}

	public synchronized boolean isRange(int pos) {
		return mFront <= mTail ? mFront <= pos && pos < mTail
				: (0 <= pos && pos < mTail) || (mFront <= pos && pos < mData.length);
	}

	public synchronized boolean isLength(int pos) {
		return mFront <= mTail ? mFront < pos && pos <= mTail
				: (0 <= pos && pos <= mTail) || (mFront < pos && pos < mData.length);
	}

	public synchronized void enqueue(byte[] data) {
		enqueue(data, 0, data.length);
	}

	public synchronized void enqueue(byte[] data, int offset, int length) {
		if (getRemainSize() < length) {
			resize(length);
		}
		
		int pos = mTail + length;
		
		if (mFront <= mTail) {
			if (pos < getSize()) {
				System.arraycopy(data, offset, mData, mTail, length);
			} else {
				int copyLen = mData.length - mTail;
				System.arraycopy(data, offset,  mData, mTail, copyLen);
				System.arraycopy(data, offset + copyLen, mData, 0, length - copyLen);
			}
		} else {
			System.arraycopy(data, offset, mData, mTail, length);
		}
		
		mTail = (mTail + length) % mData.length;
	}

	public synchronized byte[] dequeue(int end) {
		return dequeue(mFront, end);
	}

	public synchronized byte[] dequeue(int start, int end) {

		if (!isRange(start)) {
			return null;
		}
		if (!isLength(end)) {
			return null;
		}
		int length = getCount(start, end);
		byte[] data = new byte[length];
		if (mFront <= mTail) {
			System.arraycopy(mData, start, data, 0, length);
		} else {
			if (start >= mFront && start < mData.length && end >= mFront && end < mData.length) {
				System.arraycopy(mData, start, data, 0, length);
			} else if (start >= mFront && start < mData.length && end >= 0 && end <= mTail) {
				int copyLen = mData.length - start;
				System.arraycopy(mData, start, data, 0, copyLen);
				System.arraycopy(mData, 0, data, copyLen, data.length - copyLen);
			} else if (start >= 0 && start < mTail && end >= 0 && end <= mTail) {
				System.arraycopy(mData, start, data, 0, length);
			} else {
				return null;
			}
			// */
		}
		mFront = (start + length) % mData.length;
		return data;
	}

	public synchronized boolean remove(int pos) {
		if (!isLength(pos)) {
			return false;
		}

		mFront = (pos) % mData.length;

		return true;
	}

	public synchronized void clear() {
		mFront = mTail = 0;
	}

	public synchronized int findPattern(byte[] pattern) {
		return findPattern(pattern, mFront);
	}

	public synchronized int findPattern(byte[] pattern, int pos) {
		int refPos = -1;
		int index = -1;
		boolean isFound = false;

		while (isRange(pos)) {
			index = indexOf(mData, pattern[0], pos);
			if (isRange(index)) {
				isFound = true;
				for (int i = 0; i < pattern.length; i++) {
					refPos = (index + i) % mData.length;
					if (refPos == mTail || pattern[i] != mData[refPos]) {
						isFound = false;
						break;
					}
				}
				if (isFound)
					return index;
			}
			pos = (pos + 1) % mData.length;
			if (!isRange(pos)) {
				index = -1;
				break;
			}
		}
		return index;
	}

	public synchronized int peekFront() {
		return peek(mFront);
	}

	public synchronized int peek(int pos) {
		if (!isRange(pos))
			return 0;
		return mData[pos] & 0xFF;
	}

	public synchronized int findByte(byte data) {
		return findByte(data, mFront);
	}

	public synchronized int findByte(byte data, int pos) {
		int index = -1;

		if (isRange(pos)) {
			index = indexOf(mData, data, pos);
			if (isRange(index)) {
				return index;
			}
		}
		return -1;
	}

	private synchronized int indexOf(byte[] array, byte data, int pos) {
		for (int i = pos; i < array.length; i++) {
			if (array[i] == data)
				return i;
		}
		return -1;
	}

	private synchronized void resize(int size) {
		byte[] newBuf = null;
		int newSize = ((size / DEFAULT_QUEUE_SIZE) + 1) * DEFAULT_QUEUE_SIZE;
		int copySize = 0;

		newBuf = new byte[mData.length + newSize];

		if (mFront <= mTail) {
			if ((copySize = mTail - mFront) > 0)
				System.arraycopy(mData, mFront, newBuf, mFront, copySize);
		} else {
			System.arraycopy(mData, 0, newBuf, 0, mTail);
			System.arraycopy(mData, mFront, newBuf, mFront + newSize, mData.length - mFront);
			mFront += newSize;
		}
		mData = newBuf;
	}
}
