package alonzo.io;

import java.io.IOException;
import java.io.Reader;

import alonzo.common.Validate;

/**
 * AlonzoReader provides an interface for reading, character by character, from an underlying
 * {@link Reader}. Characters are buffered so as to provide for the efficient reading of the
 * underlying Reader.
 */
public class AlonzoReader implements AutoCloseable {

    private final Reader in;
    private boolean isClosed;

    private final char[] buffer;
    private int length;
    private int index;

    /**
     * The default buffer size to use.
     * @see #AlonzoReader(Reader)
     */
    public static final int DEFAULT_BUFFER_SIZE = 8192;

    /**
     * Constructs an AlonzoReader instance wrapping the given Reader and with the given buffer size.
     * @param in The Reader to read from.s
     * @param bufferSize The size of the buffer to use.
     */
    public AlonzoReader(final Reader in, final int bufferSize) {
        this.in = Validate.notNull(in, "null Reader");
        this.isClosed = false;

        Validate.isTrue(bufferSize > 0, "negative buffer size (%d)", bufferSize);
        this.buffer = new char[bufferSize];
        this.index = 0;
        this.length = 0;
    }

    /**
     * Constructs an AlonzoReader with the default buffer size.
     * @see #AlonzoReader(Reader, int)
     * @see #DEFAULT_BUFFER_SIZE
     */
    public AlonzoReader(final Reader in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }

    /**
     * Reads a character from the underlying Reader.
     * @return a character from the underlying Reader, or -1 if there is no more input.
     * @throws IOException if the underlying Reader has a problem or if this reader is already
     *                     closed.
     */
    public int read() throws IOException {
        if (isClosed) {
            throw new IOException("Stream is closed");
        }

        if (bufferEmpty()) { fillBuffer(); }
        if (bufferEmpty()) { return -1; }

        return buffer[index++];
    }

    /**
     * Closes the underlying Reader. If this reader is already closed, this method does nothing.
     * @throws IOException if there was a problem closing the underlying Reader.
     */
    @Override
    public void close() throws IOException {
        if (isClosed) { return; }

        try {
            in.close();
        } finally {
            this.isClosed = true;
        }
    }

    /** @return true if this reader is closed, false otherwise. */
    public boolean isClosed() {
        return isClosed;
    }

    private void fillBuffer() throws IOException {
        int n = 0;
        while (n == 0) {
            n = in.read(buffer, 0, buffer.length);
        }

        if (n > 0) {
            length = n;
            index = 0;
        }
    }

    private boolean bufferEmpty() {
        return index >= length;
    }

}
@@ -63,16 +63,44 @@
 .Reader;
+%0Aimport java.nio.CharBuffer;
 %0A%0Aimport
@@ -478,68 +478,25 @@
 nal 
-char%5B%5D b
+CharB
 uffer
-;%0A    private int length;%0A    private int index
+ buffer
 ;%0A%0A 
@@ -763,18 +763,22 @@
  @param 
-in
+reader
  The Rea
@@ -794,17 +794,16 @@
 ad from.
-s
 %0A     * 
@@ -896,18 +896,22 @@
  Reader 
-in
+reader
 , final 
@@ -936,21 +936,16 @@
         
-this.
 in = Val
@@ -958,18 +958,22 @@
 notNull(
-in
+reader
 , %22null 
@@ -982,37 +982,32 @@
 ader%22);%0A        
-this.
 isClosed = false
@@ -1099,21 +1099,16 @@
         
-this.
 buffer =
@@ -1112,17 +1112,28 @@
 r = 
-new char%5B
+CharBuffer.allocate(
 buff
@@ -1138,17 +1138,17 @@
 fferSize
-%5D
+)
 ;%0A      
@@ -1153,47 +1153,23 @@
     
-this.index = 0;%0A        this.length = 0
+buffer.limit(0)
 ;%0A  
@@ -1362,18 +1362,22 @@
  Reader 
-in
+reader
 ) %7B%0A    
@@ -1385,18 +1385,22 @@
    this(
-in
+reader
 , DEFAUL
@@ -1775,28 +1775,16 @@
 losed) %7B
-%0A           
  throw n
@@ -1818,24 +1818,16 @@
 losed%22);
-%0A       
  %7D%0A%0A    
@@ -1906,16 +1906,19 @@
 turn -1;
+   
  %7D%0A%0A    
@@ -1938,17 +1938,14 @@
 ffer
-%5Bindex++%5D
+.get()
 ;%0A  
@@ -2520,32 +2520,57 @@
 s IOException %7B%0A
+        buffer.clear();%0A%0A
         int n = 
@@ -2631,26 +2631,8 @@
 ffer
-, 0, buffer.length
 );%0A 
@@ -2653,77 +2653,64 @@
     
-if (n %3E 0) %7B%0A            length = n;%0A            index = 0;%0A        %7D
+buffer.position(0);%0A        buffer.limit(n %3E 0 ? n : 0);
 %0A   
@@ -2768,23 +2768,30 @@
 urn 
-index %3E= length
+!buffer.hasRemaining()
 ;%0A  

Change AlonzoReader to use CharBuffer.