// --- BEGIN COPYRIGHT BLOCK ---
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 2 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// (C) 2007 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---
package org.mozilla.jss.netscape.security.util;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * DER input buffer ... this is the main abstraction in the DER library
 * which actively works with the "untyped byte stream" abstraction. It
 * does so with impunity, since it's not intended to be exposed to the
 * anyone who could violate the "typed value stream" DER model and hence
 * corrupt the input stream of DER values.
 *
 * @version 1.11
 * @author David Brownell
 */
class DerInputBuffer extends ByteArrayInputStream implements Cloneable {

    DerInputBuffer(byte[] buf) {
        super(buf);
    }

    DerInputBuffer(byte[] buf, int offset, int len) {
        super(buf, offset, len);
    }

    DerInputBuffer dup() {
        try {
            DerInputBuffer retval = (DerInputBuffer) clone();

            retval.mark(Integer.MAX_VALUE);
            return retval;
        } catch (CloneNotSupportedException e) {
            throw new IllegalArgumentException(e.toString());
        }
    }

    byte[] toByteArray() throws IOException {
        int len = available();
        if (len <= 0)
            throw new IOException("No Buffer Space Available.");
        byte[] retval = new byte[len];

        System.arraycopy(buf, pos, retval, 0, len);
        return retval;
    }

    int peek() throws IOException {
        if (pos >= count)
            throw new IOException("out of data");
        else
            return buf[pos];
    }

    /**
     * Compares this DerInputBuffer for equality with the specified
     * object.
     */
    @Override
    public boolean equals(Object other) {
        if (other instanceof DerInputBuffer)
            return equals((DerInputBuffer) other);
        else
            return false;
    }

    boolean equals(DerInputBuffer other) {
        if (this == other)
            return true;

        int max = this.available();
        if (other.available() != max)
            return false;
        for (int i = 0; i < max; i++) {
            if (this.buf[this.pos + i] != other.buf[other.pos + i]) {
                return false;
            }
        }
        return true;
    }

    void truncate(int len) throws IOException {
        if (len > available())
            throw new IOException("insufficient data");
        count = pos + len;
    }

    /**
     * Returns the unsigned integer which takes up the specified number
     * of bytes in this buffer.
     */
    BigInt getUnsigned(int len) throws IOException {
        if (len > available())
            throw new IOException("short read, getInteger");

        /*
         * A prepended zero is used to ensure that the integer is
         * interpreted as unsigned even when the high order bit is
         * zero.  We don't support signed BigInts.
         *
         * Fix this here ... BigInts aren't expected to have these,
         * and stuff like signing (sigsize = f(modulus)) misbehaves.
         */
        if (len > 1 && buf[pos] == 0) {
            len--;
            skip(1);
        }

        /*
         * Consume the rest of the buffer, returning its value as
         * an unsigned integer.
         */
        byte[] bytes = new byte[len];

        System.arraycopy(buf, pos, bytes, 0, len);
        skip(len);
        return new BigInt(bytes);
    }

    /**
     * Returns the bit string which takes up the rest of this buffer.
     * This bit string must be byte-aligned.
     */
    byte[] getBitString() {
        if (pos >= count || buf[pos] != 0)
            return null;
        /*
         * Just copy the data into an aligned, padded octet buffer,
         * and consume the rest of the buffer.
         */
        int len = available();
        byte[] retval = new byte[len - 1];

        System.arraycopy(buf, pos + 1, retval, 0, len - 1);
        pos = count;
        return retval;
    }

    /**
     * Returns the bit string which takes up the rest of this buffer.
     * The bit string need not be byte-aligned.
     */
    BitArray getUnalignedBitString() {
        if (pos >= count)
            return null;
        /*
         * Just copy the data into an aligned, padded octet buffer,
         * and consume the rest of the buffer.
         */
        int len = available();
        byte[] bits = new byte[len - 1];
        int length = bits.length * 8 - buf[pos]; // number of valid bits

        System.arraycopy(buf, pos + 1, bits, 0, len - 1);

        BitArray bitArray = new BitArray(length, bits);
        pos = count;
        return bitArray;
    }

    /**
     * Package-access method to optimize output operations
     */
    void dump(OutputStream out, int length) throws IOException {
        if (count < mark + length)
            throw new IOException("short DER value (encode)");
        out.write(buf, mark, length);
    }

}
