// 
// Decompiled by Procyon v0.6.0
// 

package org.bouncycastle.asn1;

import java.io.EOFException;
import org.bouncycastle.util.io.Streams;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.FilterInputStream;

public class ASN1InputStream extends FilterInputStream implements BERTags
{
    private final int limit;
    private final boolean lazyEvaluate;
    private final byte[][] tmpBuffers;
    
    public ASN1InputStream(final InputStream inputStream) {
        this(inputStream, StreamUtil.findLimit(inputStream));
    }
    
    public ASN1InputStream(final byte[] buf) {
        this(new ByteArrayInputStream(buf), buf.length);
    }
    
    public ASN1InputStream(final byte[] buf, final boolean b) {
        this(new ByteArrayInputStream(buf), buf.length, b);
    }
    
    public ASN1InputStream(final InputStream inputStream, final int n) {
        this(inputStream, n, false);
    }
    
    public ASN1InputStream(final InputStream inputStream, final boolean b) {
        this(inputStream, StreamUtil.findLimit(inputStream), b);
    }
    
    public ASN1InputStream(final InputStream inputStream, final int n, final boolean b) {
        this(inputStream, n, b, new byte[11][]);
    }
    
    private ASN1InputStream(final InputStream in, final int limit, final boolean lazyEvaluate, final byte[][] tmpBuffers) {
        super(in);
        this.limit = limit;
        this.lazyEvaluate = lazyEvaluate;
        this.tmpBuffers = tmpBuffers;
    }
    
    int getLimit() {
        return this.limit;
    }
    
    protected int readLength() throws IOException {
        return readLength(this, this.limit, false);
    }
    
    protected void readFully(final byte[] array) throws IOException {
        if (Streams.readFully(this, array, 0, array.length) != array.length) {
            throw new EOFException("EOF encountered in middle of object");
        }
    }
    
    protected ASN1Primitive buildObject(final int n, final int i, final int n2) throws IOException {
        final DefiniteLengthInputStream definiteLengthInputStream = new DefiniteLengthInputStream(this, n2, this.limit);
        if (0x0 == (n & 0xE0)) {
            return createPrimitiveDERObject(i, definiteLengthInputStream, this.tmpBuffers);
        }
        final int n3 = n & 0xC0;
        if (n3 != 0) {
            return this.readTaggedObjectDL(n3, i, (n & 0x20) != 0x0, definiteLengthInputStream);
        }
        switch (i) {
            case 3: {
                return this.buildConstructedBitString(this.readVector(definiteLengthInputStream));
            }
            case 4: {
                return this.buildConstructedOctetString(this.readVector(definiteLengthInputStream));
            }
            case 16: {
                if (definiteLengthInputStream.getRemaining() < 1) {
                    return DLFactory.EMPTY_SEQUENCE;
                }
                if (this.lazyEvaluate) {
                    return new LazyEncodedSequence(definiteLengthInputStream.toByteArray());
                }
                return DLFactory.createSequence(this.readVector(definiteLengthInputStream));
            }
            case 17: {
                return DLFactory.createSet(this.readVector(definiteLengthInputStream));
            }
            case 8: {
                return DLFactory.createSequence(this.readVector(definiteLengthInputStream)).toASN1External();
            }
            default: {
                throw new IOException("unknown tag " + i + " encountered");
            }
        }
    }
    
    public ASN1Primitive readObject() throws IOException {
        final int read = this.read();
        if (read <= 0) {
            if (read == 0) {
                throw new IOException("unexpected end-of-contents marker");
            }
            return null;
        }
        else {
            final int tagNumber = readTagNumber(this, read);
            final int length = this.readLength();
            if (length >= 0) {
                try {
                    return this.buildObject(read, tagNumber, length);
                }
                catch (final IllegalArgumentException ex) {
                    throw new ASN1Exception("corrupted stream detected", ex);
                }
            }
            if (0x0 == (read & 0x20)) {
                throw new IOException("indefinite-length primitive encoding encountered");
            }
            final ASN1StreamParser asn1StreamParser = new ASN1StreamParser(new IndefiniteLengthInputStream(this, this.limit), this.limit, this.tmpBuffers);
            final int n = read & 0xC0;
            if (n != 0) {
                return asn1StreamParser.loadTaggedIL(n, tagNumber);
            }
            switch (tagNumber) {
                case 3: {
                    return BERBitStringParser.parse(asn1StreamParser);
                }
                case 4: {
                    return BEROctetStringParser.parse(asn1StreamParser);
                }
                case 8: {
                    return DERExternalParser.parse(asn1StreamParser);
                }
                case 16: {
                    return BERSequenceParser.parse(asn1StreamParser);
                }
                case 17: {
                    return BERSetParser.parse(asn1StreamParser);
                }
                default: {
                    throw new IOException("unknown BER object encountered");
                }
            }
        }
    }
    
    ASN1BitString buildConstructedBitString(final ASN1EncodableVector asn1EncodableVector) throws IOException {
        final ASN1BitString[] array = new ASN1BitString[asn1EncodableVector.size()];
        for (int i = 0; i != array.length; ++i) {
            final ASN1Encodable value = asn1EncodableVector.get(i);
            if (!(value instanceof ASN1BitString)) {
                throw new ASN1Exception("unknown object encountered in constructed BIT STRING: " + ((ASN1BitString)value).getClass());
            }
            array[i] = (ASN1BitString)value;
        }
        return new BERBitString(array);
    }
    
    ASN1OctetString buildConstructedOctetString(final ASN1EncodableVector asn1EncodableVector) throws IOException {
        final ASN1OctetString[] array = new ASN1OctetString[asn1EncodableVector.size()];
        for (int i = 0; i != array.length; ++i) {
            final ASN1Encodable value = asn1EncodableVector.get(i);
            if (!(value instanceof ASN1OctetString)) {
                throw new ASN1Exception("unknown object encountered in constructed OCTET STRING: " + ((ASN1OctetString)value).getClass());
            }
            array[i] = (ASN1OctetString)value;
        }
        return new BEROctetString(array);
    }
    
    ASN1Primitive readTaggedObjectDL(final int n, final int n2, final boolean b, final DefiniteLengthInputStream definiteLengthInputStream) throws IOException {
        if (!b) {
            return ASN1TaggedObject.createPrimitive(n, n2, definiteLengthInputStream.toByteArray());
        }
        return ASN1TaggedObject.createConstructedDL(n, n2, this.readVector(definiteLengthInputStream));
    }
    
    ASN1EncodableVector readVector() throws IOException {
        ASN1Primitive asn1Primitive = this.readObject();
        if (null == asn1Primitive) {
            return new ASN1EncodableVector(0);
        }
        final ASN1EncodableVector asn1EncodableVector = new ASN1EncodableVector();
        do {
            asn1EncodableVector.add(asn1Primitive);
        } while ((asn1Primitive = this.readObject()) != null);
        return asn1EncodableVector;
    }
    
    ASN1EncodableVector readVector(final DefiniteLengthInputStream definiteLengthInputStream) throws IOException {
        final int remaining = definiteLengthInputStream.getRemaining();
        if (remaining < 1) {
            return new ASN1EncodableVector(0);
        }
        return new ASN1InputStream(definiteLengthInputStream, remaining, this.lazyEvaluate, this.tmpBuffers).readVector();
    }
    
    static int readTagNumber(final InputStream inputStream, final int n) throws IOException {
        int n2 = n & 0x1F;
        if (n2 == 31) {
            int n3 = inputStream.read();
            if (n3 < 31) {
                if (n3 < 0) {
                    throw new EOFException("EOF found inside tag value.");
                }
                throw new IOException("corrupted stream - high tag number < 31 found");
            }
            else {
                n2 = (n3 & 0x7F);
                if (n2 == 0) {
                    throw new IOException("corrupted stream - invalid high tag number found");
                }
                while ((n3 & 0x80) != 0x0) {
                    if (n2 >>> 24 != 0) {
                        throw new IOException("Tag number more than 31 bits");
                    }
                    final int n4 = n2 << 7;
                    n3 = inputStream.read();
                    if (n3 < 0) {
                        throw new EOFException("EOF found inside tag value.");
                    }
                    n2 = (n4 | (n3 & 0x7F));
                }
            }
        }
        return n2;
    }
    
    static int readLength(final InputStream inputStream, final int i, final boolean b) throws IOException {
        final int read = inputStream.read();
        if (0 == read >>> 7) {
            return read;
        }
        if (128 == read) {
            return -1;
        }
        if (read < 0) {
            throw new EOFException("EOF found when length expected");
        }
        if (255 == read) {
            throw new IOException("invalid long form definite-length 0xFF");
        }
        final int n = read & 0x7F;
        int n2 = 0;
        int j = 0;
        do {
            final int read2 = inputStream.read();
            if (read2 < 0) {
                throw new EOFException("EOF found reading length");
            }
            if (j >>> 23 != 0) {
                throw new IOException("long form definite-length more than 31 bits");
            }
            j = (j << 8) + read2;
        } while (++n2 < n);
        if (j >= i && !b) {
            throw new IOException("corrupted stream - out of bounds length found: " + j + " >= " + i);
        }
        return j;
    }
    
    private static byte[] getBuffer(final DefiniteLengthInputStream definiteLengthInputStream, final byte[][] array) throws IOException {
        final int remaining = definiteLengthInputStream.getRemaining();
        if (remaining >= array.length) {
            return definiteLengthInputStream.toByteArray();
        }
        byte[] array2 = array[remaining];
        if (array2 == null) {
            final int n = remaining;
            final byte[] array3 = new byte[remaining];
            array[n] = array3;
            array2 = array3;
        }
        definiteLengthInputStream.readAllIntoByteArray(array2);
        return array2;
    }
    
    private static char[] getBMPCharBuffer(final DefiniteLengthInputStream definiteLengthInputStream) throws IOException {
        int i = definiteLengthInputStream.getRemaining();
        if (0x0 != (i & 0x1)) {
            throw new IOException("malformed BMPString encoding encountered");
        }
        final char[] array = new char[i / 2];
        int n = 0;
        final byte[] array2 = new byte[8];
        while (i >= 8) {
            if (Streams.readFully(definiteLengthInputStream, array2, 0, 8) != 8) {
                throw new EOFException("EOF encountered in middle of BMPString");
            }
            array[n] = (char)(array2[0] << 8 | (array2[1] & 0xFF));
            array[n + 1] = (char)(array2[2] << 8 | (array2[3] & 0xFF));
            array[n + 2] = (char)(array2[4] << 8 | (array2[5] & 0xFF));
            array[n + 3] = (char)(array2[6] << 8 | (array2[7] & 0xFF));
            n += 4;
            i -= 8;
        }
        if (i > 0) {
            if (Streams.readFully(definiteLengthInputStream, array2, 0, i) != i) {
                throw new EOFException("EOF encountered in middle of BMPString");
            }
            int j = 0;
            do {
                array[n++] = (char)(array2[j++] << 8 | (array2[j++] & 0xFF));
            } while (j < i);
        }
        if (0 != definiteLengthInputStream.getRemaining() || array.length != n) {
            throw new IllegalStateException();
        }
        return array;
    }
    
    static ASN1Primitive createPrimitiveDERObject(final int n, final DefiniteLengthInputStream definiteLengthInputStream, final byte[][] array) throws IOException {
        try {
            switch (n) {
                case 3: {
                    return ASN1BitString.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 30: {
                    return ASN1BMPString.createPrimitive(getBMPCharBuffer(definiteLengthInputStream));
                }
                case 1: {
                    return ASN1Boolean.createPrimitive(getBuffer(definiteLengthInputStream, array));
                }
                case 10: {
                    return ASN1Enumerated.createPrimitive(getBuffer(definiteLengthInputStream, array), true);
                }
                case 27: {
                    return ASN1GeneralString.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 24: {
                    return ASN1GeneralizedTime.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 25: {
                    return ASN1GraphicString.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 22: {
                    return ASN1IA5String.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 2: {
                    return ASN1Integer.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 5: {
                    ASN1Null.checkContentsLength(definiteLengthInputStream.getRemaining());
                    return ASN1Null.createPrimitive();
                }
                case 18: {
                    return ASN1NumericString.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 7: {
                    return ASN1ObjectDescriptor.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 6: {
                    ASN1ObjectIdentifier.checkContentsLength(definiteLengthInputStream.getRemaining());
                    return ASN1ObjectIdentifier.createPrimitive(getBuffer(definiteLengthInputStream, array), true);
                }
                case 4: {
                    return ASN1OctetString.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 19: {
                    return ASN1PrintableString.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 13: {
                    ASN1RelativeOID.checkContentsLength(definiteLengthInputStream.getRemaining());
                    return ASN1RelativeOID.createPrimitive(getBuffer(definiteLengthInputStream, array), true);
                }
                case 20: {
                    return ASN1T61String.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 28: {
                    return ASN1UniversalString.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 23: {
                    return ASN1UTCTime.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 12: {
                    return ASN1UTF8String.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 21: {
                    return ASN1VideotexString.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 26: {
                    return ASN1VisibleString.createPrimitive(definiteLengthInputStream.toByteArray());
                }
                case 14:
                case 31:
                case 32:
                case 33:
                case 34:
                case 35:
                case 36: {
                    throw new IOException("unsupported tag " + n + " encountered");
                }
                default: {
                    throw new IOException("unknown tag " + n + " encountered");
                }
            }
        }
        catch (final IllegalArgumentException ex) {
            throw new ASN1Exception(ex.getMessage(), ex);
        }
        catch (final IllegalStateException ex2) {
            throw new ASN1Exception(ex2.getMessage(), ex2);
        }
    }
}
