/*******************************************************************************
 * Copyright (c) 2003, 2011 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.osgi.framework.internal.reliablefile;

import java.io.*;

/**
 * A ReliableFile FileInputStream replacement class.
 * This class can be used just like FileInputStream. The class
 * is in partnership with ReliableFileOutputStream to avoid losing
 * file data by using multiple files.
 *
 * @see			ReliableFileOutputStream
 */
public class ReliableFileInputStream extends FilterInputStream {
	/**
	 * ReliableFile object for this file.
	 */
	private ReliableFile reliable;

	/** 
	 * size of crc and signature
	 */
	private int sigSize;

	/** 
	 * current position reading from file
	 */
	private long readPos;

	/** 
	 * total file length available for reading
	 */
	private long length;

	/**
	 * Constructs a new ReliableFileInputStream on the file named <code>name</code>.  If the
	 * file does not exist, the <code>FileNotFoundException</code> is thrown.
	 * The <code>name</code> may be absolute or relative
	 * to the System property <code>"user.dir"</code>.
	 *
	 * @param		name	the file on which to stream reads.
	 * @exception 	java.io.IOException If an error occurs opening the file.
	 */
	public ReliableFileInputStream(String name) throws IOException {
		this(ReliableFile.getReliableFile(name), ReliableFile.GENERATION_LATEST, ReliableFile.OPEN_BEST_AVAILABLE);
	}

	/**
	 * Constructs a new ReliableFileInputStream on the File <code>file</code>.  If the
	 * file does not exist, the <code>FileNotFoundException</code> is thrown.
	 *
	 * @param		file		the File on which to stream reads.
	 * @exception 	java.io.IOException If an error occurs opening the file.
	 */
	public ReliableFileInputStream(File file) throws IOException {
		this(ReliableFile.getReliableFile(file), ReliableFile.GENERATION_LATEST, ReliableFile.OPEN_BEST_AVAILABLE);
	}

	/**
	 * Constructs a new ReliableFileInputStream on the File <code>file</code>.  If the
	 * file does not exist, the <code>FileNotFoundException</code> is thrown.
	 * 
	 * @param file the File on which to stream reads.
	 * @param generation a specific generation requested.
	 * @param openMask mask used to open data.
	 * are invalid (corrupt, missing, etc).
	 * @exception 	java.io.IOException If an error occurs opening the file.
	 */
	public ReliableFileInputStream(File file, int generation, int openMask) throws IOException {
		this(ReliableFile.getReliableFile(file), generation, openMask);
	}

	/**
	 * 
	 * @param reliable The ReliableFile on which to read.
	 * @param generation a specific generation requested.
	 * @param openMask mask used to open data. 
	 * are invalid (corrupt, missing, etc).
	 * @throws IOException If an error occurs opening the file.
	 */
	private ReliableFileInputStream(ReliableFile reliable, int generation, int openMask) throws IOException {
		super(reliable.getInputStream(generation, openMask));

		this.reliable = reliable;
		sigSize = reliable.getSignatureSize();
		readPos = 0;
		this.length = reliable.getInputLength();
		if (sigSize > length)
			length = 0; // shouldn't ever happen
		else
			length -= sigSize;
	}

	/**
	 * Closes this input stream and releases any system resources associated
	 * with the stream.
	 *
	 * @exception 	java.io.IOException If an error occurs closing the file.
	 */
	public synchronized void close() throws IOException {
		if (reliable != null) {
			try {
				super.close();
			} finally {
				reliable.closeInputFile();
				reliable = null;
			}
		}
	}

	/**
	 * Override default FilterInputStream method.
	 * @see FilterInputStream#read(byte[], int, int)
	 */
	public synchronized int read(byte b[], int off, int len) throws IOException {
		if (readPos >= length) {
			return -1;
		}
		int num = super.read(b, off, len);

		if (num != -1) {
			if (num + readPos > length) {
				num = (int) (length - readPos);
			}
			readPos += num;
		}
		return num;
	}

	/**
	 * Override default FilterInputStream method.
	 * @see FilterInputStream#read(byte[])
	 */
	public synchronized int read(byte b[]) throws IOException {
		return read(b, 0, b.length);
	}

	/**
	 * Override default FilterInputStream method.
	 * @see FilterInputStream#read()
	 */
	public synchronized int read() throws IOException {
		if (readPos >= length) {
			return -1;
		}
		int num = super.read();

		if (num != -1) {
			readPos++;
		}
		return num;
	}

	/**
	 * Override default available method.
	 * @throws IOException 
	 * @see FilterInputStream#available()
	 */
	public synchronized int available() throws IOException {
		if (readPos < length) // just in case
			return (int) (length - readPos);
		return 0;
	}

	/**
	 * Override default skip method.
	 * @see FilterInputStream#skip(long)
	 */
	public synchronized long skip(long n) throws IOException {
		long len = super.skip(n);
		if (readPos + len > length)
			len = length - readPos;
		readPos += len;
		return len;
	}

	/**
	 * Override default markSupported method.
	 * @see FilterInputStream#markSupported()
	 */
	public boolean markSupported() {
		return false;
	}

	/**
	 * Override default mark method.
	 * @see FilterInputStream#mark(int)
	 */
	public void mark(int readlimit) {
		//ignore
	}

	/**
	 * Override default reset method.
	 * @see FilterInputStream#reset()
	 */
	public void reset() throws IOException {
		throw new IOException("reset not supported."); //$NON-NLS-1$
	}
}
