/*
 * CircularQueue.java
 * Implements a circular queue that may have more than
 * one slot.  Each slot may hold the data of an image frame.
 * Because setSamples() and putSamples() are not synchronized,
 * two threads can access the queue data simultaneously as
 * long as they are in different slots.
 * Assume an RGB model for the image.
 */

import java.io.*;
import javax.media.jai.*;

class CircularQueue
{
  private long head = 0;
  private long tail = 0;
  private int length;      //length of queue
  public int width;        //image width
  public int height;       //image height
  public byte buffer[][];  //the data queue
  public boolean quit = false; 

  public CircularQueue ( int queueLength, int w, int h )
  {
    if ( queueLength > 0 )
      length = queueLength;
    else
      length = 1;
    width = w;
    height = h;
    int frameSize = 3 * width * height;
    buffer = new byte[length][frameSize];
  }

  public synchronized void headInc()
  {  
    head++;
    notifyAll();   //wake up other threads
  }

  public synchronized void tailInc()
  {
    tail++;
    notifyAll();   //wake up other threads
  }     

  public synchronized void waitIfBufferFull()
  {
     if ( tail >= head + length ){
      try {
        System.out.println( "Buffer full, producer waits." );
        wait();
      } catch ( InterruptedException e ) {
          e.printStackTrace();
      }
    }
  }

  public synchronized void waitIfBufferEmpty()
  {
    if ( tail <= head ){
      try {
        System.out.println( "Buffer empty, consumer waits." );
        wait();
      } catch ( InterruptedException e ) {
          e.printStackTrace();
      }
    }
  }

  //consumes data
  public void setSamples ( TiledImage outImage )
  {
    int bands = 3;
    int k = 0;
    int h = (int) ( head % length );     //wrap-around
    for (int y = 0; y < height; y++)
      for (int x = 0; x < width; x++)
         for (int band=0; band < bands; band++)
           outImage.setSample(x, y, band, buffer[h][k++] );
  }

  //produces data
  public int putSamples ( InputStream in )
  {
    int t = (int) ( tail % length );     //wrap-around
    int size = 3 * width * height;
    int num = 0;
    try {
        if (  ( num = in.read( buffer[t], 0, size ) )  < 0 )
	  quit = true;
    } catch (IOException e) {
              e.printStackTrace();
              System.exit(0);
    }
    return num;  //return number of bytes read,-1 for end of data stream
  } 
}
