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

package org.bouncycastle.oer;

import java.util.Iterator;
import java.util.Enumeration;
import java.util.List;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Boolean;
import org.bouncycastle.asn1.ASN1BitString;
import org.bouncycastle.util.Strings;
import org.bouncycastle.asn1.ASN1UTF8String;
import java.io.IOException;
import org.bouncycastle.asn1.ASN1IA5String;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.util.Pack;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.encoders.Hex;
import java.math.BigInteger;
import org.bouncycastle.asn1.ASN1Enumerated;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.ASN1Set;
import java.io.ByteArrayOutputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Encodable;
import java.io.PrintWriter;
import java.io.OutputStream;

public class OEROutputStream extends OutputStream
{
    private static final int[] bits;
    private final OutputStream out;
    protected PrintWriter debugOutput;
    
    public OEROutputStream(final OutputStream out) {
        this.debugOutput = null;
        this.out = out;
    }
    
    public static int byteLength(long n) {
        long n2;
        int n3;
        for (n2 = -72057594037927936L, n3 = 8; n3 > 0 && (n & n2) == 0x0L; n <<= 8, --n3) {}
        return n3;
    }
    
    public void write(final ASN1Encodable asn1Encodable, final Element element) throws IOException {
        if (asn1Encodable == OEROptional.ABSENT) {
            return;
        }
        if (asn1Encodable instanceof OEROptional) {
            this.write(((OEROptional)asn1Encodable).get(), element);
            return;
        }
        final ASN1Primitive asn1Primitive = asn1Encodable.toASN1Primitive();
        switch (element.getBaseType()) {
            case Supplier: {
                this.write(asn1Primitive, element.getElementSupplier().build());
                break;
            }
            case SEQ: {
                final ASN1Sequence instance = ASN1Sequence.getInstance(asn1Primitive);
                int n = 7;
                int n2 = 0;
                boolean b = false;
                if (element.isExtensionsInDefinition()) {
                    for (int i = 0; i < element.getChildren().size(); ++i) {
                        final Element element2 = element.getChildren().get(i);
                        if (element2.getBaseType() == OERDefinition.BaseType.EXTENSION) {
                            break;
                        }
                        if (element2.getBlock() > 0 && i < instance.size() && !OEROptional.ABSENT.equals(instance.getObjectAt(i))) {
                            b = true;
                            break;
                        }
                    }
                    if (b) {
                        n2 |= OEROutputStream.bits[n];
                    }
                    --n;
                }
                for (int j = 0; j < element.getChildren().size(); ++j) {
                    final Element element3 = element.getChildren().get(j);
                    if (element3.getBaseType() != OERDefinition.BaseType.EXTENSION) {
                        if (element3.getBlock() > 0) {
                            break;
                        }
                        Element element4 = Element.expandDeferredDefinition(element3, element);
                        if (element.getaSwitch() != null) {
                            element4 = Element.expandDeferredDefinition(element.getaSwitch().result(new SwitchIndexer.Asn1SequenceIndexer(instance)), element);
                        }
                        if (n < 0) {
                            this.out.write(n2);
                            n = 7;
                            n2 = 0;
                        }
                        final ASN1Encodable object = instance.getObjectAt(j);
                        if (element4.isExplicit() && object instanceof OEROptional) {
                            throw new IllegalStateException("absent sequence element that is required by oer definition");
                        }
                        if (!element4.isExplicit()) {
                            final ASN1Encodable object2 = instance.getObjectAt(j);
                            if (element4.getDefaultValue() != null) {
                                if (object2 instanceof OEROptional) {
                                    if (((OEROptional)object2).isDefined() && !((OEROptional)object2).get().equals(element4.getDefaultValue())) {
                                        n2 |= OEROutputStream.bits[n];
                                    }
                                }
                                else if (!element4.getDefaultValue().equals(object2)) {
                                    n2 |= OEROutputStream.bits[n];
                                }
                            }
                            else if (object != OEROptional.ABSENT) {
                                n2 |= OEROutputStream.bits[n];
                            }
                            --n;
                        }
                    }
                }
                if (n != 7) {
                    this.out.write(n2);
                }
                List<Element> children;
                int k;
                Element result;
                ASN1Encodable object3;
                for (children = element.getChildren(), k = 0; k < children.size(); ++k) {
                    result = element.getChildren().get(k);
                    if (result.getBaseType() != OERDefinition.BaseType.EXTENSION) {
                        if (result.getBlock() > 0) {
                            break;
                        }
                        object3 = instance.getObjectAt(k);
                        if (result.getaSwitch() != null) {
                            result = result.getaSwitch().result(new SwitchIndexer.Asn1SequenceIndexer(instance));
                        }
                        if (result.getDefaultValue() == null || !result.getDefaultValue().equals(object3)) {
                            this.write(object3, result);
                        }
                    }
                }
                if (b) {
                    final int n3 = k;
                    final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    int n4 = 7;
                    int n5 = 0;
                    for (int l = n3; l < children.size(); ++l) {
                        if (n4 < 0) {
                            byteArrayOutputStream.write(n5);
                            n4 = 7;
                            n5 = 0;
                        }
                        if (l < instance.size() && !OEROptional.ABSENT.equals(instance.getObjectAt(l))) {
                            n5 |= OEROutputStream.bits[n4];
                        }
                        --n4;
                    }
                    if (n4 != 7) {
                        byteArrayOutputStream.write(n5);
                    }
                    this.encodeLength(byteArrayOutputStream.size() + 1);
                    if (n4 == 7) {
                        this.write(0);
                    }
                    else {
                        this.write(n4 + 1);
                    }
                    this.write(byteArrayOutputStream.toByteArray());
                    while (k < children.size()) {
                        if (k < instance.size() && !OEROptional.ABSENT.equals(instance.getObjectAt(k))) {
                            this.writePlainType(instance.getObjectAt(k), (Element)children.get(k));
                        }
                        ++k;
                    }
                }
                this.out.flush();
                this.debugPrint(element.appendLabel(""));
                break;
            }
            case SEQ_OF: {
                Enumeration enumeration;
                if (asn1Primitive instanceof ASN1Set) {
                    enumeration = ((ASN1Set)asn1Primitive).getObjects();
                    this.encodeQuantity(((ASN1Set)asn1Primitive).size());
                }
                else {
                    if (!(asn1Primitive instanceof ASN1Sequence)) {
                        throw new IllegalStateException("encodable at for SEQ_OF is not a container");
                    }
                    enumeration = ((ASN1Sequence)asn1Primitive).getObjects();
                    this.encodeQuantity(((ASN1Sequence)asn1Primitive).size());
                }
                final Element expandDeferredDefinition = Element.expandDeferredDefinition(element.getFirstChid(), element);
                while (enumeration.hasMoreElements()) {
                    this.write(enumeration.nextElement(), expandDeferredDefinition);
                }
                this.out.flush();
                this.debugPrint(element.appendLabel(""));
                break;
            }
            case CHOICE: {
                final ASN1Primitive asn1Primitive2 = asn1Primitive.toASN1Primitive();
                final BitBuilder bitBuilder = new BitBuilder();
                if (asn1Primitive2 instanceof ASN1TaggedObject) {
                    final ASN1TaggedObject asn1TaggedObject = (ASN1TaggedObject)asn1Primitive2;
                    final int tagClass = asn1TaggedObject.getTagClass();
                    bitBuilder.writeBit(tagClass & 0x80).writeBit(tagClass & 0x40);
                    final int tagNo = asn1TaggedObject.getTagNo();
                    final ASN1Primitive asn1Primitive3 = asn1TaggedObject.getBaseObject().toASN1Primitive();
                    if (tagNo <= 63) {
                        bitBuilder.writeBits(tagNo, 6);
                    }
                    else {
                        bitBuilder.writeBits(255L, 6);
                        bitBuilder.write7BitBytes(tagNo);
                    }
                    if (this.debugOutput != null && asn1Primitive2 instanceof ASN1TaggedObject) {
                        switch (((ASN1TaggedObject)asn1Primitive2).getTagClass()) {
                            case 64: {
                                this.debugPrint(element.appendLabel("AS"));
                                break;
                            }
                            case 128: {
                                this.debugPrint(element.appendLabel("CS"));
                                break;
                            }
                            case 192: {
                                this.debugPrint(element.appendLabel("PR"));
                                break;
                            }
                        }
                    }
                    bitBuilder.writeAndClear(this.out);
                    final Element expandDeferredDefinition2 = Element.expandDeferredDefinition(element.getChildren().get(tagNo), element);
                    if (expandDeferredDefinition2.getBlock() > 0) {
                        this.writePlainType(asn1Primitive3, expandDeferredDefinition2);
                    }
                    else {
                        this.write(asn1Primitive3, expandDeferredDefinition2);
                    }
                    this.out.flush();
                    break;
                }
                throw new IllegalStateException("only support tagged objects");
            }
            case ENUM: {
                BigInteger bigInteger;
                if (asn1Primitive instanceof ASN1Integer) {
                    bigInteger = ASN1Integer.getInstance(asn1Primitive).getValue();
                }
                else {
                    bigInteger = ASN1Enumerated.getInstance(asn1Primitive).getValue();
                }
                final Iterator<Element> iterator = element.getChildren().iterator();
                while (iterator.hasNext()) {
                    if (Element.expandDeferredDefinition(iterator.next(), element).getEnumValue().equals(bigInteger)) {
                        if (bigInteger.compareTo(BigInteger.valueOf(127L)) > 0) {
                            final byte[] byteArray = bigInteger.toByteArray();
                            this.out.write(0x80 | (byteArray.length & 0xFF));
                            this.out.write(byteArray);
                        }
                        else {
                            this.out.write(bigInteger.intValue() & 0x7F);
                        }
                        this.out.flush();
                        this.debugPrint(element.appendLabel(element.rangeExpression()));
                        return;
                    }
                }
                throw new IllegalArgumentException("enum value " + bigInteger + " " + Hex.toHexString(bigInteger.toByteArray()) + " no in defined child list");
            }
            case INT: {
                final ASN1Integer instance2 = ASN1Integer.getInstance(asn1Primitive);
                final int intBytesForRange = element.intBytesForRange();
                if (intBytesForRange > 0) {
                    final byte[] unsignedByteArray = BigIntegers.asUnsignedByteArray(intBytesForRange, instance2.getValue());
                    switch (intBytesForRange) {
                        case 1:
                        case 2:
                        case 4:
                        case 8: {
                            this.out.write(unsignedByteArray);
                            break;
                        }
                        default: {
                            throw new IllegalStateException("unknown uint length " + intBytesForRange);
                        }
                    }
                }
                else if (intBytesForRange < 0) {
                    final BigInteger value = instance2.getValue();
                    byte[] b2 = null;
                    switch (intBytesForRange) {
                        case -1: {
                            b2 = new byte[] { BigIntegers.byteValueExact(value) };
                            break;
                        }
                        case -2: {
                            b2 = Pack.shortToBigEndian(BigIntegers.shortValueExact(value));
                            break;
                        }
                        case -4: {
                            b2 = Pack.intToBigEndian(BigIntegers.intValueExact(value));
                            break;
                        }
                        case -8: {
                            b2 = Pack.longToBigEndian(BigIntegers.longValueExact(value));
                            break;
                        }
                        default: {
                            throw new IllegalStateException("unknown twos compliment length");
                        }
                    }
                    this.out.write(b2);
                }
                else {
                    byte[] b3;
                    if (element.isLowerRangeZero()) {
                        b3 = BigIntegers.asUnsignedByteArray(instance2.getValue());
                    }
                    else {
                        b3 = instance2.getValue().toByteArray();
                    }
                    this.encodeLength(b3.length);
                    this.out.write(b3);
                }
                this.debugPrint(element.appendLabel(element.rangeExpression()));
                this.out.flush();
                break;
            }
            case OCTET_STRING: {
                final byte[] octets = ASN1OctetString.getInstance(asn1Primitive).getOctets();
                if (element.isFixedLength()) {
                    this.out.write(octets);
                }
                else {
                    this.encodeLength(octets.length);
                    this.out.write(octets);
                }
                this.debugPrint(element.appendLabel(element.rangeExpression()));
                this.out.flush();
                break;
            }
            case IA5String: {
                final byte[] octets2 = ASN1IA5String.getInstance(asn1Primitive).getOctets();
                if (element.isFixedLength() && element.getUpperBound().intValue() != octets2.length) {
                    throw new IOException("IA5String string length does not equal declared fixed length " + octets2.length + " " + element.getUpperBound());
                }
                if (element.isFixedLength()) {
                    this.out.write(octets2);
                }
                else {
                    this.encodeLength(octets2.length);
                    this.out.write(octets2);
                }
                this.debugPrint(element.appendLabel(""));
                this.out.flush();
                break;
            }
            case UTF8_STRING: {
                final byte[] utf8ByteArray = Strings.toUTF8ByteArray(ASN1UTF8String.getInstance(asn1Primitive).getString());
                this.encodeLength(utf8ByteArray.length);
                this.out.write(utf8ByteArray);
                this.debugPrint(element.appendLabel(""));
                this.out.flush();
                break;
            }
            case BIT_STRING: {
                final ASN1BitString instance3 = ASN1BitString.getInstance(asn1Primitive);
                final byte[] bytes = instance3.getBytes();
                if (element.isFixedLength()) {
                    this.out.write(bytes);
                    this.debugPrint(element.appendLabel(element.rangeExpression()));
                }
                else {
                    final int padBits = instance3.getPadBits();
                    this.encodeLength(bytes.length + 1);
                    this.out.write(padBits);
                    this.out.write(bytes);
                    this.debugPrint(element.appendLabel(element.rangeExpression()));
                }
                this.out.flush();
            }
            case EXTENSION: {
                final byte[] octets3 = ASN1OctetString.getInstance(asn1Primitive).getOctets();
                if (element.isFixedLength()) {
                    this.out.write(octets3);
                }
                else {
                    this.encodeLength(octets3.length);
                    this.out.write(octets3);
                }
                this.debugPrint(element.appendLabel(element.rangeExpression()));
                this.out.flush();
            }
            case BOOLEAN: {
                this.debugPrint(element.getLabel());
                if (ASN1Boolean.getInstance(asn1Primitive).isTrue()) {
                    this.out.write(255);
                }
                else {
                    this.out.write(0);
                }
                this.out.flush();
                break;
            }
        }
    }
    
    protected void debugPrint(final String csq) {
        if (this.debugOutput != null) {
            final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            int i = -1;
            for (int j = 0; j != stackTrace.length; ++j) {
                final StackTraceElement stackTraceElement = stackTrace[j];
                if (stackTraceElement.getMethodName().equals("debugPrint")) {
                    i = 0;
                }
                else if (stackTraceElement.getClassName().contains("OERInput")) {
                    ++i;
                }
            }
            while (i > 0) {
                this.debugOutput.append("    ");
                --i;
            }
            this.debugOutput.append(csq).append("\n");
            this.debugOutput.flush();
        }
    }
    
    private void encodeLength(final long val) throws IOException {
        if (val <= 127L) {
            this.out.write((int)val);
        }
        else {
            final byte[] unsignedByteArray = BigIntegers.asUnsignedByteArray(BigInteger.valueOf(val));
            this.out.write(unsignedByteArray.length | 0x80);
            this.out.write(unsignedByteArray);
        }
    }
    
    private void encodeQuantity(final long val) throws IOException {
        final byte[] unsignedByteArray = BigIntegers.asUnsignedByteArray(BigInteger.valueOf(val));
        this.out.write(unsignedByteArray.length);
        this.out.write(unsignedByteArray);
    }
    
    @Override
    public void write(final int n) throws IOException {
        this.out.write(n);
    }
    
    public void writePlainType(final ASN1Encodable asn1Encodable, final Element element) throws IOException {
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        final OEROutputStream oerOutputStream = new OEROutputStream(byteArrayOutputStream);
        oerOutputStream.write(asn1Encodable, element);
        oerOutputStream.flush();
        oerOutputStream.close();
        this.encodeLength(byteArrayOutputStream.size());
        this.write(byteArrayOutputStream.toByteArray());
    }
    
    static {
        bits = new int[] { 1, 2, 4, 8, 16, 32, 64, 128 };
    }
}
