package org.bucksch.zeipis.recorder;

import java.io.IOException;
import java.io.InputStream;

public class InputOutputStream extends InputStream
{

  // API for creator of this stream, for writing

  public InputOutputStream(CloseCallback closeCB)
  {
    myBuffer = new byte[4096];
    myCloseCB = closeCB;
  }
  public InputOutputStream(int bufferSize, CloseCallback closeCB)
  {
    myBuffer = new byte[bufferSize];
    myCloseCB = closeCB;
  }

  public synchronized void write(byte b)
  {
    if (myLength >= myBuffer.length)
      throw new RuntimeException("Buffer full");
    myBuffer[myLength++] = b;
  }
  public synchronized void write(byte[] buf, int offset, int length)
  {
    if (myLength + length > myBuffer.length)
      throw new RuntimeException("Buffer full or too small");
    for (int i = offset, stop = offset + length; i < stop; i++)
      myBuffer[myLength++] = buf[i];
  }
  // you must call this when you're done writing and have no more data
  public synchronized void eof()
  {
    close();
  }


  // Standard API for reading this stream

  @Override
  public synchronized int available()
  {
    return myLength - myCur;
  }

  @Override
  public int read() throws IOException
  {
    if ( ! waitForData())
      return -1;
    synchronized (this)
    {
      byte result = myBuffer[myCur++];

      if (myCur == myLength)
        clearBuffer();
      return result;
    }
  }

  @Override
  public int read(byte[] buf) throws IOException
  {
    return read(buf, 0, buf.length);
  }
  @Override
  public int read(byte[] buf, int offset, int length) throws IOException
  {
    if ( ! waitForData())
      return -1;
    synchronized (this)
    {
      int avail = myLength - myCur;
      if (avail > length)
        avail = length;

      for (int i = myCur, stop = myCur + avail; i < stop; i++)
        buf[offset++] = myBuffer[myCur++];

      if (myCur == myLength)
        clearBuffer();
      return avail;      
    }
  }

  @Override
  public synchronized long skip(long n) throws IOException
  {
    if (n > myLength - myCur)
      n = myLength - myCur;
    myCur += n;
    return n;
  }

  @Override
  public boolean markSupported()
  {
    return false;
  }
  @Override
  public synchronized void mark(int readlimit)
  {
  }
  @Override
  public synchronized void reset() throws IOException
  {
    myCur = 0;
  }

  @Override
  public synchronized void close()
  {
    myOpen = false;
    myLength = 0;
    myCur = 0;
    myBuffer = null;

    if (myCloseCB != null)
      myCloseCB.streamClosed(this);
  }


  // Internal

  // returns false, if EOF
  protected boolean waitForData()
  {
    if (myCur < myLength)
      return true;
    while (myCur >= myLength && myOpen)
    {
      Thread.yield();
      try {
        Thread.sleep(3); // ms - low value, because we'll get a PAT or PMT from DVB card within 10-30ms
      } catch (InterruptedException e) {}
    }
    return myCur < myLength;
  }
  // restart from 0 in buffer, to avoid fill
  protected void clearBuffer()
  {
    assert (myCur == myLength);
    myCur = 0;
    myLength = 0;
  }

  // Data
  protected byte[] myBuffer;
  protected int myLength = 0; // position until which the buffer is filled. Not the total length of data.
  protected int myCur = 0; // current read position in buffer.
  protected boolean myOpen = true; // stream will get more data
  protected CloseCallback myCloseCB;
}

interface CloseCallback
{
  public void streamClosed(InputOutputStream s);
}
