package scone.util.tokenstream;


import java.io.IOException;
import java.util.Hashtable;

import com.ibm.wbi.Copyright;


/**
 * reads Tokens from the TokenBuffer a TokenOutputStream writes to.
 * 
 * @author Volkert Buchmann (ported from code by Team WBI), 
 *         IBM Almaden Research Center, 
 *         (c) Copyright IBM Corp 2000
 * @version 1.0
 */
public class TokenInputStreamBufferImpl extends TokenInputStream {

    private final static String COPYRIGHT = Copyright.COPYRIGHT;

    private TokenBuffer buffer = null;
    private Object bufferContext = null;
    private boolean closed = false;
    private long timeout = 0;
    private boolean untouched = true;
    private Hashtable metaInfo = null;

    /**
     * creates a new TokenInputBufferImpl that reads from a TokenBuffer.
     * @param out the TokenOutputStream that holds the TokenBuffer
     */
    public TokenInputStreamBufferImpl(TokenOutputStream out) {
        this.buffer = out.getTokenBuffer();
        this.metaInfo = out.getMetaInfo();
        bufferContext = out.getBufferContext();
    }

    /**
     * Read a Token from the input stream.  Blocks if no data available and
     * end of stream not yet reached. If no Token is available because the
     * end of the stream has been reached, null is returned.
     * @return  The next Token from the stream, or null if the end of the stream has been reached
     * @exception   IOException     If stream closed.
     * @exception   MarkPreservedException If read would invalidate preserved
     *              mark
     */
    public Token read() throws IOException {
        checkClosed();
        // read one Token from the buffer
        Token b[] = new Token[1];
        int rc = buffer.read(b, 0, 1, bufferContext, timeout, false);

        untouched = false;
        // if buffer.read() returned -1, it's the end of the stream
        if (rc < 0) { 
            return null;
        } else { 
            return b[0];
        }
    }

    /**
     * Read multiple Tokens from the input stream.  Blocks if no data available
     * and end of stream not yet reached.
     * @param   buf     The Token buffer to populate.
     * @param   offset  The offset in <code>buf</code> to starting writing to.
     * @param   length  The amount of Tokens to write.
     * @return  The amount of Tokens read, or -1 if no more Tokens available to
     *          read.
     * @exception   IOException     If stream closed.
     * @exception   MarkPreservedException If read would invalidate preserved
     *              mark
     */
    public int read(Token buf[], int offset, int length) throws IOException {
        checkClosed();
        int rc = 
                buffer.read(buf, offset, length, bufferContext, timeout, false);

        untouched = false;
        return rc;
    }

    /**
     * Read Tokens from the input stream filling the specified Token array.
     * Blocks if no data available and end of stream not yet reached.
     * @param   buf     The Token buffer to populate.
     * @return  The amount of Tokens read, or -1 if no more Tokens available to
     *          read.
     * @exception   IOException     If stream closed.
     * @exception   MarkPreservedException If read would invalidate preserved
     *              mark
     */
    public int read(Token buf[]) throws IOException {
        checkClosed();
        int rc = 
                buffer.read(buf, 0, buf.length, bufferContext, timeout, false);

        untouched = false;
        return rc;
    }

    /**
     * Close the input stream.  If the stream is already closed, this method
     * has not effect.
     */
    public void close() throws IOException {
        if (!closed) {
            closed = true;
            buffer.unsubscribe(bufferContext);
            bufferContext = null;
        }
    }

    /**
     * Determines if the input stream is closed.
     * @return  <code>true</code> if stream closed; else <code>false</code>.
     */
    public boolean isClosed() {
        return closed;
    }

    /**
     * Mark the stream for later resetting
     * @param readlimit The number of Tokens read beyond which the mark
     * will become invalid.
     */
    public void mark(int readlimit) {
        buffer.mark(bufferContext, readlimit);
    }

    /**
     * Reset the stream to its previous mark
     * @exception IOException if the stream was not marked or the mark
     * has become invalid.
     */
    public void reset() throws IOException {
        checkClosed();
        buffer.reset(bufferContext);
    }

    /**
     * Test whether <code>mark()</code> and <code>reset()</code> are supported.
     * @return <code>true</code> if <code>mark()</code> and
     * <code>reset()</code> are supported, <code>false</code>
     * otherwise.
     */
    public boolean markSupported() {
        return true;
    }

    /**
     * Check and determine if the stream is closed.  If it is closed, then
     * throw an exception.
     * @exception   IOException     If stream closed.
     */
    private void checkClosed() throws IOException {
        if (closed) {
            throw new IOException("TokenInputStream is closed to read.");
        }
    }
    
    /**
     * returns the meta info associated with this TokenInputStream
     * @return the metaInfo
     */    
    public Hashtable getMetaInfo() {
    	//System.out.print("TokenInputStreamBufferImpl says:");
    	//System.out.println(metaInfo);
        return metaInfo;
    }
}

