/*
 * 
 * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * The contents of this file are subject to the Sun Community Source
 * License v3.0/Jini Technology Specific Attachment v 1.0 (the
 * "License"). You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 * http://wwws.sun.com/software/jini/licensing/SCSL3_JiniTSA1.html.
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Reference Code is Jini Technology Core Platform code, v2.0. The
 * Developer of the Reference Code is Sun Microsystems, Inc.
 * 
 * Contributor(s): Sun Microsystems, Inc.
 * 
 * The contents of this file comply with the Jini Technology Core
 * Platform Compatibility Kit, v2.0A.
 * 
 * Tester(s): Sun Microsystems, Inc.
 * 
 * Test Platform(s):
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Solaris(TM)SPARC/x86
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Linux (Intel x86)
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Windows (Intel Platform)
 * 
 */
package com.sun.jini.mercury;

import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

/**
 * This class implements the interface for interacting with input log
 * streams. It extends <tt>java.util.InputStream</tt> and overrides its
 * methods in order to provide buffered input as well as byte 
 * offset tracking, which is useful during recovery situations.
 *
 * @author Sun Microsystems, Inc.
 *
 * @since 1.1
 */
class LogInputStream extends InputStream implements LogStream {

    /** Holds value of the internal buffer size to allocate */
    private static final int BUFSIZE = 1024;

    /** byte array buffer to hold read data */
    private byte[] buf = new byte[BUFSIZE];

    /** count of available bytes in the read buffer */
    private int count = 0;

    /** index/offset into the read buffer for the next set of bytes to read */
    private int pos = 0;

    /** cumulative index/offset into this stream */
    private long offset = 0;

    /** Underlying input stream from which bytes are read */
    private FileInputStream in;

    /** Associated key for this stream object */
    private /*final*/ StreamKey key;
    
    /**
     * Simple constructor that accepts a <tt>File</tt> and <tt>StreamKey</tt>
     * arguments. The <tt>File</tt> and <tt>StreamKey</tt>
     * arguments are assigned to their respective internal fields.  
     *
     * @param     file   the <tt>File</tt> to used
     * @param     key    the associated key for this object
     *
     * @exception  IllegalArgumentException  if either the <tt>file</tt> or 
     *                   <tt>key</tt> arguments are null.
     *
     * @exception  FileNotFoundException  if the file exists but is a directory
     *                   rather than a regular file, does not exist but cannot
     *                   be created, or cannot be opened for any other reason.
     */
    public LogInputStream(File file, StreamKey key) 
        throws FileNotFoundException 
    {
        if (file == null || key == null)
            throw new IllegalArgumentException("Arguments cannot be null");
	in = new FileInputStream(file);
	this.key = key;
    }

    // javadoc inherited from supertype 
    public int read() throws IOException {
	if (pos >= count)
	    refill();
	if (count < 0)
	    return -1;

	offset++;
	return buf[pos++] & 0xFF;
    }

    // javadoc inherited from supertype 
    public int read(byte[] b) throws IOException {
	return read(b, 0, b.length);
    }

    // javadoc inherited from supertype 
    public int read(byte[] b, int off, int len) throws IOException {
	if (pos >= count)
	    refill();
	if (count < 0)
	    return -1;

	int rlen = Math.min(len, count - pos);
	System.arraycopy(buf, pos, b, off, rlen);
	pos += rlen;
	offset += rlen;
	return rlen;
    }
    
    // javadoc inherited from supertype 
    public long skip(long n) throws IOException {
	if (count < 0)
	    return 0;

	int slen = (int) Math.min(n, count - pos);
	pos += slen;
	n -= slen;
	if (n > 0)
	    slen += in.skip(n);

	offset += slen;
	return slen;
    }

    // javadoc inherited from supertype 
    public int available() throws IOException {
	return (count >= 0) ? (count - pos) + in.available() : 0;
    }

    // javadoc inherited from supertype 
    public void close() throws IOException {
	count = -1;
	in.close();
    }

    /**
     * Returns the current "read" offset into this stream object. 
     */
    public long getOffset() {
	return offset;
    }
    
    /**
     * Refills the internal buffer with any available bytes. This method
     * will block until either bytes have been read, or no more bytes can be
     * read.
     */
    private void refill() throws IOException {
	if (count < 0)
	    return;
	pos = 0;
	do {
	    count = in.read(buf);
	} while (count == 0);
    }

    // javadoc inherited from supertype 
    public Object getKey() {
        return key;
    }
}
