package Tools;

import java.io.*;

/**
 * Builds a ``longer'' InputStream out of two others. When the first
 * input stream runs out, read() requests will be satisfied from the
 * second input stream. Supports mark() and reset(), even across stream
 * boundaries, when both input streams support mark and reset.
 */
public class ChainInputStream
	extends InputStream {

	InputStream s1, s2;
	boolean doneWithS1 = false;
	InputStream markStream = null;
	int readlimit;

	public ChainInputStream(InputStream s1, InputStream s2) {
		this.s1 = s1;
		this.s2 = s2;
	}

	public int available()
		throws java.io.IOException {
		if (doneWithS1) {
			return s1.available()+s2.available();
		} else {
			return s2.available();
		}
	}

	public void close()
		throws java.io.IOException {
		if (!doneWithS1) {
			s1.close();
		}
		s2.close();
	}

	public boolean markSupported() {
		return true;
	}

	public synchronized void mark(int readlimit) {
		if (doneWithS1) {
			s2.mark(readlimit);
			markStream = s2;
		} else {
			s1.mark(readlimit);
			s2.mark(readlimit);	// so if we reset to mark, we
								// can reset second stream
			markStream = s1;
		}
		this.readlimit = readlimit;
	}

	public synchronized void reset()
		throws java.io.IOException {
		if (markStream==s1) {
			if (doneWithS1) {
				s2.reset();	// hope we set mark at beginning of s2! :v)
			}
			s1.reset();
			doneWithS1 = false;
				// what if mark was set just as we read EOF of S1?
		} else {
			// mark is on s2
			s2.reset();
		}
	}

	public int read()
		throws java.io.IOException {
		if (!doneWithS1) {
			int rc = s1.read();
			if (rc==-1) {
				// stream is done
				changeStreams();
				// fall through and read off s2
			} else {
				return rc;
			}
		}
		return s2.read();
	}

	public int read(byte[] buf)
		throws java.io.IOException {
		return read(buf, 0, buf.length);
	}

	public int read(byte[] buf, int offset, int length)
		throws java.io.IOException {
		if (!doneWithS1) {
			int rc = s1.read(buf, offset, length);
			if (rc<=0) {
				// stream is done
				changeStreams();
				// fall through and read off s2
			} else {
				return rc;
			}
		}
		return s2.read(buf, offset, length);
	}

	public long skip(long n)
		throws java.io.IOException {
		if (!doneWithS1) {
			long rc = s1.skip(n);
			if (rc<=0L) {
				// stream is done
				changeStreams();
				// fall through and skip over s2
			} else {
				return rc;
			}
		}
		return s2.skip(n);
	}

	private void changeStreams() {
		doneWithS1 = true;
	}
}
