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

package org.bouncycastle.oer;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Integer;
import java.math.BigInteger;

public class OERDefinition
{
    static final BigInteger[] uIntMax;
    static final BigInteger[][] sIntRange;
    
    public static Builder bool() {
        return new Builder(BaseType.BOOLEAN);
    }
    
    public static Builder integer() {
        return new Builder(BaseType.INT);
    }
    
    public static Builder integer(final long n) {
        return new Builder(BaseType.INT).defaultValue(new ASN1Integer(n));
    }
    
    public static Builder bitString(final long n) {
        return new Builder(BaseType.BIT_STRING).fixedSize(n);
    }
    
    public static Builder integer(final BigInteger bigInteger, final BigInteger bigInteger2) {
        return new Builder(BaseType.INT).range(bigInteger, bigInteger2);
    }
    
    public static Builder integer(final long val, final long val2) {
        return new Builder(BaseType.INT).range(BigInteger.valueOf(val), BigInteger.valueOf(val2));
    }
    
    public static Builder integer(final long n, final long n2, final ASN1Encodable asn1Encodable) {
        return new Builder(BaseType.INT).range(n, n2, asn1Encodable);
    }
    
    public static Builder nullValue() {
        return new Builder(BaseType.NULL);
    }
    
    public static Builder seq() {
        return new Builder(BaseType.SEQ);
    }
    
    public static Builder seq(final Object... array) {
        return new Builder(BaseType.SEQ).items(array);
    }
    
    public static Builder aSwitch(final Switch switch1) {
        return new Builder(BaseType.Switch).decodeSwitch(switch1);
    }
    
    public static Builder enumItem(final String s) {
        return new Builder(BaseType.ENUM_ITEM).label(s);
    }
    
    public static Builder enumItem(final String s, final BigInteger bigInteger) {
        return new Builder(BaseType.ENUM_ITEM).enumValue(bigInteger).label(s);
    }
    
    public static Builder enumeration(final Object... array) {
        return new Builder(BaseType.ENUM).items(array);
    }
    
    public static Builder choice(final Object... array) {
        return new Builder(BaseType.CHOICE).items(array);
    }
    
    public static Builder placeholder() {
        return new Builder(null);
    }
    
    public static Builder seqof(final Object... array) {
        return new Builder(BaseType.SEQ_OF).items(array);
    }
    
    public static Builder octets() {
        return new Builder(BaseType.OCTET_STRING).unbounded();
    }
    
    public static Builder octets(final int n) {
        return new Builder(BaseType.OCTET_STRING).fixedSize(n);
    }
    
    public static Builder octets(final int n, final int n2) {
        return new Builder(BaseType.OCTET_STRING).range(BigInteger.valueOf(n), BigInteger.valueOf(n2));
    }
    
    public static Builder ia5String() {
        return new Builder(BaseType.IA5String);
    }
    
    public static Builder utf8String() {
        return new Builder(BaseType.UTF8_STRING);
    }
    
    public static Builder utf8String(final int n) {
        return new Builder(BaseType.UTF8_STRING).rangeToMAXFrom(n);
    }
    
    public static Builder utf8String(final int n, final int n2) {
        return new Builder(BaseType.UTF8_STRING).range(BigInteger.valueOf(n), BigInteger.valueOf(n2));
    }
    
    public static Builder opaque() {
        return new Builder(BaseType.OPAQUE);
    }
    
    public static List<Object> optional(final Object... a) {
        return new OptionalList(Arrays.asList(a));
    }
    
    public static ExtensionList extension(final Object... a) {
        return new ExtensionList(1, Arrays.asList(a));
    }
    
    public static ExtensionList extension(final int n, final Object... a) {
        return new ExtensionList(n, Arrays.asList(a));
    }
    
    public static Builder deferred(final ElementSupplier elementSupplier) {
        return new Builder(BaseType.Supplier).elementSupplier(elementSupplier);
    }
    
    static {
        uIntMax = new BigInteger[] { new BigInteger("256"), new BigInteger("65536"), new BigInteger("4294967296"), new BigInteger("18446744073709551616") };
        sIntRange = new BigInteger[][] { { new BigInteger("-128"), new BigInteger("127") }, { new BigInteger("-32768"), new BigInteger("32767") }, { new BigInteger("-2147483648"), new BigInteger("2147483647") }, { new BigInteger("-9223372036854775808"), new BigInteger("9223372036854775807") } };
    }
    
    public enum BaseType
    {
        SEQ, 
        SEQ_OF, 
        CHOICE, 
        ENUM, 
        INT, 
        OCTET_STRING, 
        OPAQUE, 
        UTF8_STRING, 
        BIT_STRING, 
        NULL, 
        EXTENSION, 
        ENUM_ITEM, 
        BOOLEAN, 
        IS0646String, 
        PrintableString, 
        NumericString, 
        BMPString, 
        UniversalString, 
        IA5String, 
        VisibleString, 
        Switch, 
        Supplier;
    }
    
    public static class Builder
    {
        protected final BaseType baseType;
        protected ArrayList<Builder> children;
        protected boolean explicit;
        protected String typeName;
        protected String label;
        protected BigInteger upperBound;
        protected BigInteger lowerBound;
        protected BigInteger enumValue;
        protected ASN1Encodable defaultValue;
        protected Builder placeholderValue;
        protected Boolean inScope;
        protected Switch aSwitch;
        protected ArrayList<ASN1Encodable> validSwitchValues;
        protected ElementSupplier elementSupplier;
        protected boolean mayRecurse;
        protected Map<String, ElementSupplier> supplierMap;
        protected int block;
        private final ItemProvider defaultItemProvider;
        
        public Builder(final BaseType baseType) {
            this.children = new ArrayList<Builder>();
            this.explicit = true;
            this.validSwitchValues = new ArrayList<ASN1Encodable>();
            this.supplierMap = new HashMap<String, ElementSupplier>();
            this.defaultItemProvider = new ItemProvider() {
                @Override
                public Builder existingChild(final int n, final Builder builder) {
                    return builder.copy(Builder.this.defaultItemProvider);
                }
            };
            this.baseType = baseType;
        }
        
        private Builder copy(final ItemProvider itemProvider) {
            final Builder builder = new Builder(this.baseType);
            int n = 0;
            final Iterator<Builder> iterator = this.children.iterator();
            while (iterator.hasNext()) {
                builder.children.add(itemProvider.existingChild(n++, iterator.next()));
            }
            builder.explicit = this.explicit;
            builder.label = this.label;
            builder.upperBound = this.upperBound;
            builder.lowerBound = this.lowerBound;
            builder.defaultValue = this.defaultValue;
            builder.enumValue = this.enumValue;
            builder.inScope = this.inScope;
            builder.aSwitch = this.aSwitch;
            builder.validSwitchValues = new ArrayList<ASN1Encodable>(this.validSwitchValues);
            builder.elementSupplier = this.elementSupplier;
            builder.mayRecurse = this.mayRecurse;
            builder.typeName = this.typeName;
            builder.supplierMap = new HashMap<String, ElementSupplier>(this.supplierMap);
            builder.block = this.block;
            return builder;
        }
        
        protected Builder block(final int block) {
            final Builder copy = this.copy();
            copy.block = block;
            return copy;
        }
        
        public Builder copy() {
            return this.copy(this.defaultItemProvider);
        }
        
        public Builder elementSupplier(final ElementSupplier elementSupplier) {
            final Builder copy = this.copy();
            copy.elementSupplier = elementSupplier;
            return copy;
        }
        
        public Builder validSwitchValue(final ASN1Encodable... a) {
            final Builder copy = this.copy();
            copy.validSwitchValues.addAll(Arrays.asList(a));
            return copy;
        }
        
        public Builder inScope(final boolean b) {
            final Builder copy = this.copy();
            copy.inScope = b;
            return copy;
        }
        
        public Builder limitScopeTo(final String... a) {
            final Builder copy = this.copy();
            final HashSet set = new HashSet();
            set.addAll(Arrays.asList(a));
            final ArrayList children = new ArrayList();
            for (final Builder builder : this.children) {
                children.add(builder.copy().inScope(set.contains(builder.label)));
            }
            copy.children = children;
            return copy;
        }
        
        public Builder typeName(final String s) {
            final Builder copy = this.copy();
            copy.typeName = s;
            if (copy.label == null) {
                copy.label = s;
            }
            return copy;
        }
        
        public Builder unbounded() {
            final Builder copy = this.copy();
            copy.lowerBound = null;
            copy.upperBound = null;
            return copy;
        }
        
        public Builder decodeSwitch(final Switch aSwitch) {
            final Builder copy = this.copy();
            copy.aSwitch = aSwitch;
            return copy;
        }
        
        public Builder labelPrefix(final String str) {
            final Builder copy = this.copy();
            copy.label = str + " " + this.label;
            return copy;
        }
        
        public Builder explicit(final boolean explicit) {
            final Builder copy = this.copy();
            copy.explicit = explicit;
            return copy;
        }
        
        public Builder defaultValue(final ASN1Encodable defaultValue) {
            final Builder copy = this.copy();
            copy.defaultValue = defaultValue;
            return copy;
        }
        
        protected Builder wrap(final boolean b, final Object o) {
            if (o instanceof Builder) {
                return ((Builder)o).explicit(b);
            }
            if (o instanceof BaseType) {
                return new Builder((BaseType)o).explicit(b);
            }
            if (o instanceof String) {
                return OERDefinition.enumItem((String)o);
            }
            throw new IllegalStateException("Unable to wrap item in builder");
        }
        
        protected void addExtensions(final Builder builder, final ExtensionList list) {
            if (list.isEmpty()) {
                final Builder e = new Builder(BaseType.EXTENSION);
                e.block = list.block;
                builder.children.add(e);
                return;
            }
            for (final OptionalList next : list) {
                if (next instanceof OptionalList) {
                    this.addOptionals(builder, list.block, next);
                }
                else {
                    final Builder wrap = this.wrap(true, next);
                    wrap.block = list.block;
                    builder.children.add(wrap);
                }
            }
        }
        
        protected void addOptionals(final Builder builder, final int block, final OptionalList list) {
            for (final ExtensionList next : list) {
                if (next instanceof ExtensionList) {
                    this.addExtensions(builder, next);
                }
                else {
                    final Builder wrap = this.wrap(false, next);
                    wrap.block = block;
                    builder.children.add(wrap);
                }
            }
        }
        
        public Builder items(final Object... array) {
            final Builder copy = this.copy();
            for (int i = 0; i != array.length; ++i) {
                final Object o = array[i];
                if (o instanceof ExtensionList) {
                    this.addExtensions(copy, (ExtensionList)o);
                }
                else if (o instanceof OptionalList) {
                    this.addOptionals(copy, copy.block, (OptionalList)o);
                }
                else if (((OptionalList)o).getClass().isArray()) {
                    for (int j = 0; j < ((Object[])o).length; ++j) {
                        copy.children.add(this.wrap(true, ((Object[])o)[j]));
                    }
                }
                else {
                    copy.children.add(this.wrap(true, o));
                }
            }
            return copy;
        }
        
        public Builder label(final String label) {
            final Builder copy = this.copy();
            copy.label = label;
            return copy;
        }
        
        public Builder mayRecurse(final boolean mayRecurse) {
            final Builder copy = this.copy();
            copy.mayRecurse = mayRecurse;
            return copy;
        }
        
        public Element build() {
            final ArrayList list = new ArrayList();
            boolean b = false;
            if (this.baseType == BaseType.ENUM) {
                int n = 0;
                final HashSet set = new HashSet();
                for (int i = 0; i < this.children.size(); ++i) {
                    final Builder builder = this.children.get(i);
                    if (builder.enumValue == null) {
                        builder.enumValue = BigInteger.valueOf(n);
                        ++n;
                    }
                    if (set.contains(builder.enumValue)) {
                        throw new IllegalStateException("duplicate enum value at index " + i);
                    }
                    set.add(builder.enumValue);
                }
            }
            int n2 = 0;
            boolean b2 = false;
            for (final Builder builder2 : this.children) {
                if (!b && builder2.block > 0) {
                    b = true;
                }
                if (!builder2.explicit) {
                    ++n2;
                }
                if (!b2 && builder2.defaultValue != null) {
                    b2 = true;
                }
                list.add(builder2.build());
            }
            return new Element(this.baseType, list, this.defaultValue == null && this.explicit, this.label, this.lowerBound, this.upperBound, b, this.enumValue, this.defaultValue, this.aSwitch, this.validSwitchValues.isEmpty() ? null : this.validSwitchValues, this.elementSupplier, this.mayRecurse, this.typeName, this.supplierMap.isEmpty() ? null : this.supplierMap, this.block, n2, b2);
        }
        
        public Builder range(final BigInteger lowerBound, final BigInteger upperBound) {
            final Builder copy = this.copy();
            copy.lowerBound = lowerBound;
            copy.upperBound = upperBound;
            return copy;
        }
        
        public Builder rangeToMAXFrom(final long val) {
            final Builder copy = this.copy();
            copy.lowerBound = BigInteger.valueOf(val);
            copy.upperBound = null;
            return copy;
        }
        
        public Builder rangeZeroTo(final long val) {
            final Builder copy = this.copy();
            copy.upperBound = BigInteger.valueOf(val);
            copy.lowerBound = BigInteger.ZERO;
            return copy;
        }
        
        public Builder fixedSize(final long n) {
            final Builder copy = this.copy();
            copy.upperBound = BigInteger.valueOf(n);
            copy.lowerBound = BigInteger.valueOf(n);
            return copy;
        }
        
        public Builder range(final long val, final long val2, final ASN1Encodable defaultValue) {
            final Builder copy = this.copy();
            copy.lowerBound = BigInteger.valueOf(val);
            copy.upperBound = BigInteger.valueOf(val2);
            copy.defaultValue = defaultValue;
            return copy;
        }
        
        public Builder enumValue(final BigInteger enumValue) {
            final Builder copy = this.copy();
            this.enumValue = enumValue;
            return copy;
        }
        
        public Builder replaceChild(final int n, final Builder builder) {
            return this.copy(new ItemProvider() {
                @Override
                public Builder existingChild(final int n, final Builder builder) {
                    return (n == n) ? builder : builder;
                }
            });
        }
    }
    
    private static class ExtensionList extends ArrayList<Object>
    {
        protected final int block;
        
        public ExtensionList(final int block, final List<Object> c) {
            this.block = block;
            this.addAll(c);
        }
    }
    
    public interface ItemProvider
    {
        Builder existingChild(final int p0, final Builder p1);
    }
    
    private static class OptionalList extends ArrayList<Object>
    {
        public OptionalList(final List<Object> c) {
            this.addAll(c);
        }
    }
    
    public static class MutableBuilder extends Builder
    {
        private boolean frozen;
        
        public MutableBuilder(final BaseType baseType) {
            super(baseType);
            this.frozen = false;
        }
        
        @Override
        public MutableBuilder label(final String label) {
            this.label = label;
            return this;
        }
        
        public MutableBuilder addItemsAndFreeze(final Builder... array) {
            if (this.frozen) {
                throw new IllegalStateException("build cannot be modified and must be copied only");
            }
            for (int i = 0; i != array.length; ++i) {
                final Builder builder = array[i];
                if (builder instanceof OptionalList) {
                    final Iterator iterator = ((List)builder).iterator();
                    while (iterator.hasNext()) {
                        super.children.add(this.wrap(false, iterator.next()));
                    }
                }
                else if (((List)builder).getClass().isArray()) {
                    final Object[] array2 = (Object)builder;
                    for (int length = array2.length, j = 0; j < length; ++j) {
                        super.children.add(this.wrap(true, array2[j]));
                    }
                }
                else {
                    super.children.add(this.wrap(true, builder));
                }
            }
            this.frozen = true;
            return this;
        }
    }
}
