/* *******************************************************************************
 *   Copyright (C) 2004 Tristan Allwood,                                         *
 *                 2004 Matthew Sackman                                          *
 *                                                                               *
 *   This program is free software; you can redistribute it and/or               *
 *   modify it under the terms of the GNU General Public License                 *
 *   as published by the Free Software Foundation; either version 2              *
 *   of the License, or (at your option) any later version.                      *
 *                                                                               *
 *   This program is distributed in the hope that it will be useful,             *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of              *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
 *   GNU General Public License for more details.                                *
 *                                                                               *
 *   You should have received a copy of the GNU General Public License           *
 *   along with this program; if not, write to the Free Software                 *
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. *
 *                                                                               *
 *   The authors can be contacted by email at toa02@doc.ic.ac.uk                 *
 *                                             ms02@doc.ic.ac.uk                 *
 *                                                                               *
 *********************************************************************************/

/*
 * Created on 01-Jul-2004
 *
 */
package uk.co.zonetora.fj;

import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;

/**
 * A subclass of the PushbackReader which automatically expands and contracts
 * its buffer as you read and unread data.
 * 
 * @author Matthew Sackman (ms02)
 * @version 1
 */
public class VariablePushbackReader extends PushbackReader {

    private final List buffer = new ArrayList();

    public VariablePushbackReader(Reader in) {
        super(in);
    }

    public int read() throws IOException {
        synchronized (lock) {
            if (buffer.size() > 0) {
                final int idx = buffer.size() - 1;
                final int read = ((Integer) buffer.get(idx)).intValue();
                buffer.remove(idx);
                return read;
            } else {
                return super.read();
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.io.Reader#read(char[])
     */
    public int read(char[] cbuf) throws IOException {
        synchronized (lock) {
            int count = 0;
            for (; count < cbuf.length; count++) {
                cbuf[count] = (char) read();
            }
            return count;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.io.PushbackReader#read(char[], int, int)
     */
    public int read(char[] cbuf, int off, int len) throws IOException {
        synchronized (lock) {
            int count = 0;
            for (; count < len; count++) {
                cbuf[off + count] = (char) read();
            }
            return count;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.io.PushbackReader#ready()
     */
    public boolean ready() throws IOException {
        synchronized (lock) {
            return buffer.size() > 0 || super.ready();
        }
    }

    public void mark(int readAheadLimit) throws IOException {
        throw new IOException("mark/reset not supported");
    }

    public void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }

    public boolean markSupported() {
        return false;
    }

    public void close() throws IOException {
        synchronized (lock) {
            buffer.clear();
            super.close();
        }
    }

    public long skip(long n) throws IOException {
        if (n < 0L)
                throw new IllegalArgumentException("Skip value is negative ("
                        + n + ")");
        if (n == 0) return n;
        int skipped = 0;
        synchronized (lock) {
            while (n > 0 && buffer.size() > 0) {
                buffer.remove(buffer.size() - 1);
                n--;
                skipped++;
            }
            while (n > 0) {
                super.read();
                n--;
                skipped++;
            }
            return skipped;
        }
    }

    public void unread(int c) {
        synchronized (lock) {
            buffer.add(new Integer(c));
        }
    }

    public void unread(char[] cbuf) {
        synchronized (lock) {
            for (int idx = cbuf.length - 1; idx != 0; idx--)
                unread(cbuf[idx]);
        }
    }

    public void unread(char[] cbuf, int off, int len) {
        synchronized (lock) {
            for (int idx = len; idx != 0; idx--)
                unread(cbuf[off + idx]);
        }
    }
}