package jp.crestmuse.cmx.misc;
import java.util.*;
import java.util.concurrent.*;

public class QueueWrapper<E> {
  private java.util.Queue<E> queue;
  private E[] list;
  private int size;
  private int head, tail;
  private byte nReaders = (byte)0;
  private byte[] alreadyRead;

  private static final int DEFAULT_BUFFER_SIZE = 512;

  public QueueWrapper(java.util.Queue<E> q) {
    this(q, DEFAULT_BUFFER_SIZE);
  }

  public QueueWrapper(java.util.Queue<E> q, int size) {
    if (size <= 0) size = DEFAULT_BUFFER_SIZE;
    queue = q;
    this.size = size;
    list = (E[])new Object[size];
    alreadyRead = new byte[size];
    head = 0;
    tail = 0;
  }

  public QueueReader createReader() {
    nReaders++;
    return new QueueReaderImpl();
  }

  private class QueueReaderImpl implements QueueReader<E> {
    private int next;
    private QueueReaderImpl() {
      next = 0;
    }

    public synchronized E take() throws InterruptedException {
      E e;
      if (tail < head && next < head && next > tail
          || tail > head && (next < head || next > tail)) {
        e = list[next];
      } else {
        if (queue instanceof BlockingQueue)
          e = ((BlockingQueue<E>)queue).take();
        else
          e = queue.poll();
        list[head] = e;
        head++;
        if (head >= size) head = 0;
      }
      alreadyRead[next]++;
      if (alreadyRead[next] >= nReaders) {
        list[next] = null;
        alreadyRead[next] = 0;
        tail = next;
      }
      next++;
      if (next >= size) next = 0;
//      if (tail >= size) tail = 0;
      return e;
    }


/*
    public E get(int index) {
      if (index < next) {
        return list.get(index);
      } else {
        throw new IndexOutOfBoundsException();
      }
    }
*/

//    public boolean isAvailable(int index) {
//      return index < next;
//    }

    public Iterator<E> iterator() {
      return new Iterator() {
          public boolean hasNext() {
            return next < size;
          }
          public E next() {
            try {
              return take();
            } catch (InterruptedException e) {
              throw new RuntimeException();
            }
          }
          public void remove() {
            throw new UnsupportedOperationException();
          }
        };
    }

  }
}
