/* 2010-03-10
 */
package com.cwasa.javacv;

import java.util.concurrent.LinkedBlockingQueue;


public class FrameQueue {

	private static final int		STD_CAPACITY = 16;

	private final int				FRAME_SIZE;
	private final int				CAPACITY;
	private final LinkedBlockingQueue<byte[]>
									PIXEL_DATA_QUEUE;
	private final LinkedBlockingQueue<byte[]>
									FREE_QUEUE;
	private final byte[]			Q_TERMINATOR;
	private boolean					queueIsClosed;

	public FrameQueue(int fsz) { this(fsz, STD_CAPACITY); }

	public FrameQueue(int fsz, int cpcty) {

		this.FRAME_SIZE = fsz;
		this.CAPACITY = cpcty;
		this.FREE_QUEUE = new LinkedBlockingQueue<byte[]>();
		// Non-positive capacity value means unlimited capacity.
		this.PIXEL_DATA_QUEUE =
			0 < this.CAPACITY ?
				new LinkedBlockingQueue<byte[]>(this.CAPACITY) :
				new LinkedBlockingQueue<byte[]>();
		this.Q_TERMINATOR = new byte[0];
		this.queueIsClosed = false;
	}

	public byte[] allocItem() {

		byte[] item = this.FREE_QUEUE.poll();
		if (item == null) { item = new byte[this.FRAME_SIZE]; }
		return item;
	}

	public void freeItem(byte[] item) throws InterruptedException {

		this.FREE_QUEUE.put(item);
	}

/** This and #closeQueue() are to be used by a single producer thread. */
	public void addFrame(byte[] fpixels) throws InterruptedException {

		if (!this.queueIsClosed) {
			this.PIXEL_DATA_QUEUE.put(fpixels);
		}
		else {
			System.out.println(
				"FrameQueue: ignore addFrame() after queue is closed.");
		}
	}

/** This and #addFrame(byte[]) are to be used by a single producer thread. */
	public void closeQueue() throws InterruptedException {

		this.addFrame(this.Q_TERMINATOR);
		this.queueIsClosed = true;
	}

/** To be used by a single consumer thread. */
	public byte[] getNextFrame() throws InterruptedException {

		byte[] fpixels = null;

		final byte[] PIXELS = this.PIXEL_DATA_QUEUE.take();
		// Return null to our caller if queue is exhausted.
		if (PIXELS != this.Q_TERMINATOR) { fpixels = PIXELS; }

		return fpixels;
	}
}
