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

package org.bouncycastle.asn1;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Enumeration;
import org.bouncycastle.util.Arrays;
import java.io.IOException;
import org.bouncycastle.util.Iterable;

public abstract class ASN1Set extends ASN1Primitive implements Iterable<ASN1Encodable>
{
    static final ASN1UniversalType TYPE;
    protected final ASN1Encodable[] elements;
    protected ASN1Encodable[] sortedElements;
    
    public static ASN1Set getInstance(final Object o) {
        if (o == null || o instanceof ASN1Set) {
            return (ASN1Set)o;
        }
        if (o instanceof ASN1Encodable) {
            final ASN1Primitive asn1Primitive = ((ASN1Encodable)o).toASN1Primitive();
            if (asn1Primitive instanceof ASN1Set) {
                return (ASN1Set)asn1Primitive;
            }
        }
        else if (o instanceof byte[]) {
            try {
                return (ASN1Set)ASN1Set.TYPE.fromByteArray((byte[])o);
            }
            catch (final IOException ex) {
                throw new IllegalArgumentException("failed to construct set from byte[]: " + ex.getMessage());
            }
        }
        throw new IllegalArgumentException("unknown object in getInstance: " + o.getClass().getName());
    }
    
    public static ASN1Set getInstance(final ASN1TaggedObject asn1TaggedObject, final boolean b) {
        return (ASN1Set)ASN1Set.TYPE.getContextTagged(asn1TaggedObject, b);
    }
    
    public static ASN1Set getTagged(final ASN1TaggedObject asn1TaggedObject, final boolean b) {
        return (ASN1Set)ASN1Set.TYPE.getTagged(asn1TaggedObject, b);
    }
    
    protected ASN1Set() {
        this.elements = ASN1EncodableVector.EMPTY_ELEMENTS;
        this.sortedElements = this.elements;
    }
    
    protected ASN1Set(final ASN1Encodable asn1Encodable) {
        if (null == asn1Encodable) {
            throw new NullPointerException("'element' cannot be null");
        }
        this.elements = new ASN1Encodable[] { asn1Encodable };
        this.sortedElements = this.elements;
    }
    
    protected ASN1Set(final ASN1EncodableVector asn1EncodableVector, final boolean b) {
        if (null == asn1EncodableVector) {
            throw new NullPointerException("'elementVector' cannot be null");
        }
        ASN1Encodable[] elements;
        if (b && asn1EncodableVector.size() >= 2) {
            elements = asn1EncodableVector.copyElements();
            sort(elements);
        }
        else {
            elements = asn1EncodableVector.takeElements();
        }
        this.elements = elements;
        this.sortedElements = (ASN1Encodable[])((b || elements.length < 2) ? this.elements : null);
    }
    
    protected ASN1Set(final ASN1Encodable[] array, final boolean b) {
        if (Arrays.isNullOrContainsNull(array)) {
            throw new NullPointerException("'elements' cannot be null, or contain null");
        }
        final ASN1Encodable[] cloneElements = ASN1EncodableVector.cloneElements(array);
        if (b && cloneElements.length >= 2) {
            sort(cloneElements);
        }
        this.elements = cloneElements;
        this.sortedElements = (ASN1Encodable[])((b || cloneElements.length < 2) ? array : null);
    }
    
    ASN1Set(final boolean b, final ASN1Encodable[] elements) {
        this.elements = elements;
        this.sortedElements = (ASN1Encodable[])((b || elements.length < 2) ? elements : null);
    }
    
    ASN1Set(final ASN1Encodable[] elements, final ASN1Encodable[] sortedElements) {
        this.elements = elements;
        this.sortedElements = sortedElements;
    }
    
    public Enumeration getObjects() {
        return new Enumeration() {
            private int pos = 0;
            
            @Override
            public boolean hasMoreElements() {
                return this.pos < ASN1Set.this.elements.length;
            }
            
            @Override
            public Object nextElement() {
                if (this.pos < ASN1Set.this.elements.length) {
                    return ASN1Set.this.elements[this.pos++];
                }
                throw new NoSuchElementException();
            }
        };
    }
    
    public ASN1Encodable getObjectAt(final int n) {
        return this.elements[n];
    }
    
    public int size() {
        return this.elements.length;
    }
    
    public ASN1Encodable[] toArray() {
        return ASN1EncodableVector.cloneElements(this.elements);
    }
    
    public ASN1SetParser parser() {
        return new ASN1SetParser() {
            private int pos = 0;
            final /* synthetic */ int val$count = ASN1Set.this.size();
            
            @Override
            public ASN1Encodable readObject() throws IOException {
                if (this.val$count == this.pos) {
                    return null;
                }
                final ASN1Encodable asn1Encodable = ASN1Set.this.elements[this.pos++];
                if (asn1Encodable instanceof ASN1Sequence) {
                    return ((ASN1Sequence)asn1Encodable).parser();
                }
                if (asn1Encodable instanceof ASN1Set) {
                    return ((ASN1Set)asn1Encodable).parser();
                }
                return asn1Encodable;
            }
            
            @Override
            public ASN1Primitive getLoadedObject() {
                return ASN1Set.this;
            }
            
            @Override
            public ASN1Primitive toASN1Primitive() {
                return ASN1Set.this;
            }
        };
    }
    
    @Override
    public int hashCode() {
        int length = this.elements.length;
        int n = length + 1;
        while (--length >= 0) {
            n += this.elements[length].toASN1Primitive().hashCode();
        }
        return n;
    }
    
    @Override
    ASN1Primitive toDERObject() {
        if (this.sortedElements == null) {
            sort(this.sortedElements = this.elements.clone());
        }
        return new DERSet(true, this.sortedElements);
    }
    
    @Override
    ASN1Primitive toDLObject() {
        return new DLSet(this.elements, this.sortedElements);
    }
    
    @Override
    boolean asn1Equals(final ASN1Primitive asn1Primitive) {
        if (!(asn1Primitive instanceof ASN1Set)) {
            return false;
        }
        final ASN1Set set = (ASN1Set)asn1Primitive;
        final int size = this.size();
        if (set.size() != size) {
            return false;
        }
        final DERSet set2 = (DERSet)this.toDERObject();
        final DERSet set3 = (DERSet)set.toDERObject();
        for (int i = 0; i < size; ++i) {
            final ASN1Primitive asn1Primitive2 = set2.elements[i].toASN1Primitive();
            final ASN1Primitive asn1Primitive3 = set3.elements[i].toASN1Primitive();
            if (asn1Primitive2 != asn1Primitive3 && !asn1Primitive2.asn1Equals(asn1Primitive3)) {
                return false;
            }
        }
        return true;
    }
    
    @Override
    boolean encodeConstructed() {
        return true;
    }
    
    @Override
    public String toString() {
        final int size = this.size();
        if (size == 0) {
            return "[]";
        }
        final StringBuilder sb = new StringBuilder();
        sb.append('[');
        int n = 0;
        while (true) {
            sb.append(this.elements[n]);
            if (++n >= size) {
                break;
            }
            sb.append(", ");
        }
        sb.append(']');
        return sb.toString();
    }
    
    @Override
    public Iterator<ASN1Encodable> iterator() {
        return new Arrays.Iterator<ASN1Encodable>(this.toArray());
    }
    
    private static byte[] getDEREncoded(final ASN1Encodable asn1Encodable) {
        try {
            return asn1Encodable.toASN1Primitive().getEncoded("DER");
        }
        catch (final IOException ex) {
            throw new IllegalArgumentException("cannot encode object added to SET");
        }
    }
    
    private static boolean lessThanOrEqual(final byte[] array, final byte[] array2) {
        final int n = array[0] & 0xDF;
        final int n2 = array2[0] & 0xDF;
        if (n != n2) {
            return n < n2;
        }
        final int n3 = Math.min(array.length, array2.length) - 1;
        for (int i = 1; i < n3; ++i) {
            if (array[i] != array2[i]) {
                return (array[i] & 0xFF) < (array2[i] & 0xFF);
            }
        }
        return (array[n3] & 0xFF) <= (array2[n3] & 0xFF);
    }
    
    private static void sort(final ASN1Encodable[] array) {
        final int length = array.length;
        if (length < 2) {
            return;
        }
        ASN1Encodable asn1Encodable = array[0];
        ASN1Encodable asn1Encodable2 = array[1];
        byte[] derEncoded = getDEREncoded(asn1Encodable);
        byte[] derEncoded2 = getDEREncoded(asn1Encodable2);
        if (lessThanOrEqual(derEncoded2, derEncoded)) {
            final ASN1Encodable asn1Encodable3 = asn1Encodable2;
            asn1Encodable2 = asn1Encodable;
            asn1Encodable = asn1Encodable3;
            final byte[] array2 = derEncoded2;
            derEncoded2 = derEncoded;
            derEncoded = array2;
        }
        for (int i = 2; i < length; ++i) {
            final ASN1Encodable asn1Encodable4 = array[i];
            final byte[] derEncoded3 = getDEREncoded(asn1Encodable4);
            if (lessThanOrEqual(derEncoded2, derEncoded3)) {
                array[i - 2] = asn1Encodable;
                asn1Encodable = asn1Encodable2;
                derEncoded = derEncoded2;
                asn1Encodable2 = asn1Encodable4;
                derEncoded2 = derEncoded3;
            }
            else if (lessThanOrEqual(derEncoded, derEncoded3)) {
                array[i - 2] = asn1Encodable;
                asn1Encodable = asn1Encodable4;
                derEncoded = derEncoded3;
            }
            else {
                int n = i - 1;
                while (--n > 0) {
                    final ASN1Encodable asn1Encodable5 = array[n - 1];
                    if (lessThanOrEqual(getDEREncoded(asn1Encodable5), derEncoded3)) {
                        break;
                    }
                    array[n] = asn1Encodable5;
                }
                array[n] = asn1Encodable4;
            }
        }
        array[length - 2] = asn1Encodable;
        array[length - 1] = asn1Encodable2;
    }
    
    static {
        TYPE = new ASN1UniversalType(17) {
            @Override
            ASN1Primitive fromImplicitConstructed(final ASN1Sequence asn1Sequence) {
                return asn1Sequence.toASN1Set();
            }
        };
    }
}
