package jocket.impl;

import java.nio.ByteBuffer;

public final class JocketWriter extends AbstractJocketBuffer {

  /** The sequence number of the next packet to write. */
  private int wseq;

  /** Current (not flushed) packet's start and end absolute position. */
  private int pstart, pend;

  public JocketWriter(ByteBuffer buf, int npackets) {
    super(buf, npackets);
  }

  public int write(final byte[] data, final int off, final int len) {
    readMemoryBarrier();
    final int rseq = rseq();
    final int wseq = this.wseq;

    if (rseq == wseq) {
      // reset position in buffer when reader is up to date
      if (rseq > 0 && pend == pstart) {
        this.pstart = this.pend = 0;
      }
    }

    // cannot write if all packets are written and the reader didn't read them
    else if (wseq - rseq >= npackets)
      return 0;

    else if (rseq < 0)
      close();

    if (isClosed())
      throw new ClosedException("Closed");

    // TODO: implement anti-truncation mechanism (write at 0 if remaining
    // space is too small)
    final int bytes = Math.min(getAvailableSpace(rseq, pend), len);
    if (bytes > 0) {
      buf.position(dataOffset + (pend & dataMask));
      buf.put(data, off, bytes);
      this.pend += bytes;

      // flush when reaching end of buffer, otherwise next write will
      // generate an inconsistent packet (overflowing the buffer)
      if ((this.pend & dataMask) == 0) {
        flush();
      }
    }
    return bytes;
  }

  public void flush() {
    final int pend = this.pend;
    final int pstart = this.pstart;
    final ByteBuffer buf = this.buf;
    if (pend > pstart) {
      int pkt = PACKET_INFO + (wseq & packetMask) * LEN_PACKET_INFO;
      buf.putInt(pkt, pstart);
      buf.putInt(pkt + 4, pend - pstart);
      buf.putInt(WSEQ, ++wseq);
      this.pstart = pend;
    }
  }

  /**
   * Returns the absolute position of the last read byte.
   * 
   * @param rseq reader sequence number.
   */
  private int head(int rseq) {
    // if all packets are read, the position has been or will be reset
    if (wseq == rseq)
      return 0;

    final int pkt = PACKET_INFO + ((wseq - 1) & packetMask) * LEN_PACKET_INFO;

    return buf.getInt(pkt) + buf.getInt(pkt + 4);
  }

  /**
   * Returns the absolute position of specified packet.
   * 
   * @param seq a packet number
   */
  private int start(int seq) {
    return buf.getInt(PACKET_INFO + (seq & packetMask) * LEN_PACKET_INFO);
  }

  /**
   * Returns how many bytes can be written in one single chunk at current
   * position. We can be limited either by the bounds of the ByteBuffer or by
   * how many bytes must still be read.
   * <p>
   * Result is (X - head) where X is the smallest of:
   * <ul>
   * <li>head + capacity - (head - head % capacity)
   * <li>tail + capacity
   * </ul>
   * 
   * @param rseq sequence number of reader
   * @param head position of last written byte
   */
  private int getAvailableSpace(int rseq, int head) {
    return Math.min(start(rseq), head - (head & dataMask)) + capacity - head;
  }

  /**
   * Returns the reader sequence number. Call must be preceded by a read memory
   * barrier.
   */
  private final int rseq() {
    return buf.getInt(RSEQ);
  }

  /**
   * Returns how many bytes can be written (mainly for test purposes).
   */
  public int available() {
    // cannot write if all packets are written and the reader didn't read any
    int rseq = rseq();
    if (wseq - rseq >= npackets)
      return 0;
    return getAvailableSpace(rseq, pend);
  }

  @Override
  protected void close0() {
    buf.putInt(WSEQ, -1);
    writeMemoryBarrier();
  }

  /**
   * For testing purposes.
   */
  public int getSeqNum() {
    return wseq;
  }

  /**
   * For testing purposes.
   */
  public int getPosition() {
    return head(rseq());
  }

  /**
   * For testing purposes.
   */
  public String debug() {
    return String.format(
        "wseq=%d rseq=%d pstart=%d plen=%d tail=%d dirty=%b capacity=%d", wseq,
        rseq(), pstart, pend - pstart, head(rseq()), pend > pstart, capacity);
  }
}

@@ -197,29 +197,15 @@
 /** 
-Current (not flushed)
+Pending
  pac
@@ -210,16 +210,25 @@
 acket's 
+absolute 
 start an
@@ -233,33 +233,24 @@
 and end 
-absolute 
 position
 . */%0A  p
@@ -241,16 +241,17 @@
 position
+s
 . */%0A  p
@@ -276,16 +276,172 @@
  pend;%0A%0A
+  /**%0A   * Equivalent to (pend %3E pstart) but looks like it is faster to store this%0A   * information in a dedicated boolean.%0A   */%0A  private boolean dirty;%0A%0A
   public
@@ -784,22 +784,14 @@
  && 
-pend == psta
+!di
 rt
+y
 ) %7B%0A
@@ -1265,24 +1265,44 @@
 ytes %3E 0) %7B%0A
+      dirty = true;%0A
       buf.po
@@ -1769,29 +1769,21 @@
     if (
-pend %3E psta
+di
 rt
+y
 ) %7B%0A    
@@ -1974,24 +1974,45 @@
 art = pend;%0A
+      dirty = false;%0A
     %7D%0A  %7D%0A%0A 

Store 'dirty' status in dedicated boolean