// CHECKSTYLE_OFF: Copyrighted to the Android Open Source Project.
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
// CHECKSTYLE_ON

package java.io;

import static javaemul.internal.InternalPreconditions.checkNotNull;

/**
 * A readable source of bytes.
 *
 * <p>Most clients will use input streams that read data from the file system
 * ({@link FileInputStream}), the network ({@link java.net.Socket#getInputStream()}/{@link
 * java.net.HttpURLConnection#getInputStream()}), or from an in-memory byte
 * array ({@link ByteArrayInputStream}).
 *
 * <p>Use {@link InputStreamReader} to adapt a byte stream like this one into a
 * character stream.
 *
 * <p>Most clients should wrap their input stream with {@link
 * BufferedInputStream}. Callers that do only bulk reads may omit buffering.
 *
 * <p>Some implementations support marking a position in the input stream and
 * resetting back to this position later. Implementations that don't return
 * false from {@link #markSupported()} and throw an {@link IOException} when
 * {@link #reset()} is called.
 *
 * <h3>Subclassing InputStream</h3>
 * Subclasses that decorate another input stream should consider subclassing
 * {@link FilterInputStream}, which delegates all calls to the source input
 * stream.
 *
 * <p>All input stream subclasses should override <strong>both</strong> {@link
 * #read() read()} and {@link #read(byte[],int,int) read(byte[],int,int)}. The
 * three argument overload is necessary for bulk access to the data. This is
 * much more efficient than byte-by-byte access.
 *
 * @see OutputStream
 */
public abstract class InputStream extends Object implements Closeable {

    /**
     * Size of the temporary buffer used when skipping bytes with {@link skip(long)}.
     */
    private static final int MAX_SKIP_BUFFER_SIZE = 4096;

    /**
     * This constructor does nothing. It is provided for signature
     * compatibility.
     */
    public InputStream() {
        /* empty */
    }

    /**
     * Returns an estimated number of bytes that can be read or skipped without blocking for more
     * input.
     *
     * <p>Note that this method provides such a weak guarantee that it is not very useful in
     * practice.
     *
     * <p>Firstly, the guarantee is "without blocking for more input" rather than "without
     * blocking": a read may still block waiting for I/O to complete&nbsp;&mdash; the guarantee is
     * merely that it won't have to wait indefinitely for data to be written. The result of this
     * method should not be used as a license to do I/O on a thread that shouldn't be blocked.
     *
     * <p>Secondly, the result is a
     * conservative estimate and may be significantly smaller than the actual number of bytes
     * available. In particular, an implementation that always returns 0 would be correct.
     * In general, callers should only use this method if they'd be satisfied with
     * treating the result as a boolean yes or no answer to the question "is there definitely
     * data ready?".
     *
     * <p>Thirdly, the fact that a given number of bytes is "available" does not guarantee that a
     * read or skip will actually read or skip that many bytes: they may read or skip fewer.
     *
     * <p>It is particularly important to realize that you <i>must not</i> use this method to
     * size a container and assume that you can read the entirety of the stream without needing
     * to resize the container. Such callers should probably write everything they read to a
     * {@link ByteArrayOutputStream} and convert that to a byte array. Alternatively, if you're
     * reading from a file, {@link File#length} returns the current length of the file (though
     * assuming the file's length can't change may be incorrect, reading a file is inherently
     * racy).
     *
     * <p>The default implementation of this method in {@code InputStream} always returns 0.
     * Subclasses should override this method if they are able to indicate the number of bytes
     * available.
     *
     * @return the estimated number of bytes available
     * @throws IOException if this stream is closed or an error occurs
     */
    public int available() throws IOException {
        return 0;
    }

    /**
     * Closes this stream. Concrete implementations of this class should free
     * any resources during close. This implementation does nothing.
     *
     * @throws IOException
     *             if an error occurs while closing this stream.
     */
    @Override
    public void close() throws IOException {
        /* empty */
    }

    /**
     * Sets a mark position in this InputStream. The parameter {@code readlimit}
     * indicates how many bytes can be read before the mark is invalidated.
     * Sending {@code reset()} will reposition the stream back to the marked
     * position provided {@code readLimit} has not been surpassed.
     * <p>
     * This default implementation does nothing and concrete subclasses must
     * provide their own implementation.
     *
     * @param readlimit
     *            the number of bytes that can be read from this stream before
     *            the mark is invalidated.
     * @see #markSupported()
     * @see #reset()
     */
    public void mark(int readlimit) {
        /* empty */
    }

    /**
     * Indicates whether this stream supports the {@code mark()} and
     * {@code reset()} methods. The default implementation returns {@code false}.
     *
     * @return always {@code false}.
     * @see #mark(int)
     * @see #reset()
     */
    public boolean markSupported() {
        return false;
    }

    /**
     * Reads a single byte from this stream and returns it as an integer in the
     * range from 0 to 255. Returns -1 if the end of the stream has been
     * reached. Blocks until one byte has been read, the end of the source
     * stream is detected or an exception is thrown.
     *
     * @throws IOException
     *             if the stream is closed or another IOException occurs.
     */
    public abstract int read() throws IOException;

    /**
     * Equivalent to {@code read(buffer, 0, buffer.length)}.
     */
    public int read(byte[] buffer) throws IOException {
        // Note that GWT will throw a JavascriptException rather than a NullPointerException if we
        // skip this check and the buffer array is null. This way we ensure that this implementation
        // behaves in the same way as the classes that are emulated.
        checkNotNull(buffer);
        return read(buffer, 0, buffer.length);
    }

    /**
     * Reads up to {@code byteCount} bytes from this stream and stores them in
     * the byte array {@code buffer} starting at {@code byteOffset}.
     * Returns the number of bytes actually read or -1 if the end of the stream
     * has been reached.
     *
     * @throws IndexOutOfBoundsException
     *   if {@code byteOffset < 0 || byteCount < 0 || byteOffset + byteCount > buffer.length}.
     * @throws IOException
     *             if the stream is closed or another IOException occurs.
     */
    public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
        IOUtils.checkOffsetAndCount(buffer, byteOffset, byteCount);
        for (int i = 0; i < byteCount; ++i) {
            int c;
            try {
                if ((c = read()) == -1) {
                    return i == 0 ? -1 : i;
                }
            } catch (IOException e) {
                if (i != 0) {
                    return i;
                }
                throw e;
            }
            buffer[byteOffset + i] = (byte) c;
        }
        return byteCount;
    }

    /**
     * Resets this stream to the last marked location. Throws an
     * {@code IOException} if the number of bytes read since the mark has been
     * set is greater than the limit provided to {@code mark}, or if no mark
     * has been set.
     * <p>
     * This implementation always throws an {@code IOException} and concrete
     * subclasses should provide the proper implementation.
     *
     * @throws IOException
     *             if this stream is closed or another IOException occurs.
     */
    public void reset() throws IOException {
        throw new IOException();
    }

    /**
     * Skips at most {@code byteCount} bytes in this stream. The number of actual
     * bytes skipped may be anywhere between 0 and {@code byteCount}. If
     * {@code byteCount} is negative, this method does nothing and returns 0, but
     * some subclasses may throw.
     *
     * <p>Note the "at most" in the description of this method: this method may
     * choose to skip fewer bytes than requested. Callers should <i>always</i>
     * check the return value.
     *
     * <p>This default implementation reads bytes into a temporary buffer. Concrete
     * subclasses should provide their own implementation.
     *
     * @return the number of bytes actually skipped.
     * @throws IOException if this stream is closed or another IOException
     *             occurs.
     */
    public long skip(long byteCount) throws IOException {
        if (byteCount <= 0) {
            return 0;
        }
        final int bSize = (int) Math.min(MAX_SKIP_BUFFER_SIZE, byteCount);
        final byte[] b = new byte[bSize];
        long skipped = 0;
        while (skipped < byteCount) {
            final int toRead = (int) Math.min(byteCount - skipped, b.length);
            final int readCount = read(b, 0, toRead);
            if (readCount == -1) {
                break;
            }
            skipped += readCount;
            if (readCount < toRead) {
                break;
            }
        }
        return skipped;
    }
}
