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

package org.bouncycastle.asn1;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import org.bouncycastle.util.Arrays;
import java.io.IOException;

public abstract class ASN1BitString extends ASN1Primitive implements ASN1String, ASN1BitStringParser
{
    static final ASN1UniversalType TYPE;
    private static final char[] table;
    final byte[] contents;
    
    public static ASN1BitString getInstance(final Object o) {
        if (o == null || o instanceof ASN1BitString) {
            return (ASN1BitString)o;
        }
        if (o instanceof ASN1Encodable) {
            final ASN1Primitive asn1Primitive = ((ASN1Encodable)o).toASN1Primitive();
            if (asn1Primitive instanceof ASN1BitString) {
                return (ASN1BitString)asn1Primitive;
            }
        }
        else if (o instanceof byte[]) {
            try {
                return (ASN1BitString)ASN1BitString.TYPE.fromByteArray((byte[])o);
            }
            catch (final IOException ex) {
                throw new IllegalArgumentException("failed to construct BIT STRING from byte[]: " + ex.getMessage());
            }
        }
        throw new IllegalArgumentException("illegal object in getInstance: " + o.getClass().getName());
    }
    
    public static ASN1BitString getInstance(final ASN1TaggedObject asn1TaggedObject, final boolean b) {
        return (ASN1BitString)ASN1BitString.TYPE.getContextTagged(asn1TaggedObject, b);
    }
    
    public static ASN1BitString getTagged(final ASN1TaggedObject asn1TaggedObject, final boolean b) {
        return (ASN1BitString)ASN1BitString.TYPE.getTagged(asn1TaggedObject, b);
    }
    
    protected static int getPadBits(final int n) {
        int n2 = 0;
        for (int i = 3; i >= 0; --i) {
            if (i != 0) {
                if (n >> i * 8 != 0) {
                    n2 = (n >> i * 8 & 0xFF);
                    break;
                }
            }
            else if (n != 0) {
                n2 = (n & 0xFF);
                break;
            }
        }
        if (n2 == 0) {
            return 0;
        }
        int n3 = 1;
        while (((n2 <<= 1) & 0xFF) != 0x0) {
            ++n3;
        }
        return 8 - n3;
    }
    
    protected static byte[] getBytes(final int n) {
        if (n == 0) {
            return new byte[0];
        }
        int n2 = 4;
        for (int n3 = 3; n3 >= 1 && (n & 255 << n3 * 8) == 0x0; --n3) {
            --n2;
        }
        final byte[] array = new byte[n2];
        for (int i = 0; i < n2; ++i) {
            array[i] = (byte)(n >> i * 8 & 0xFF);
        }
        return array;
    }
    
    ASN1BitString(final byte b, final int n) {
        if (n > 7 || n < 0) {
            throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
        }
        this.contents = new byte[] { (byte)n, b };
    }
    
    ASN1BitString(final byte[] array, final int n) {
        if (array == null) {
            throw new NullPointerException("'data' cannot be null");
        }
        if (array.length == 0 && n != 0) {
            throw new IllegalArgumentException("zero length data with non-zero pad bits");
        }
        if (n > 7 || n < 0) {
            throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
        }
        this.contents = Arrays.prepend(array, (byte)n);
    }
    
    ASN1BitString(final byte[] contents, final boolean b) {
        if (b) {
            if (null == contents) {
                throw new NullPointerException("'contents' cannot be null");
            }
            if (contents.length < 1) {
                throw new IllegalArgumentException("'contents' cannot be empty");
            }
            final int n = contents[0] & 0xFF;
            if (n > 0) {
                if (contents.length < 2) {
                    throw new IllegalArgumentException("zero length data with non-zero pad bits");
                }
                if (n > 7) {
                    throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
                }
            }
        }
        this.contents = contents;
    }
    
    @Override
    public InputStream getBitStream() throws IOException {
        return new ByteArrayInputStream(this.contents, 1, this.contents.length - 1);
    }
    
    @Override
    public InputStream getOctetStream() throws IOException {
        final int i = this.contents[0] & 0xFF;
        if (i != 0) {
            throw new IOException("expected octet-aligned bitstring, but found padBits: " + i);
        }
        return this.getBitStream();
    }
    
    public ASN1BitStringParser parser() {
        return this;
    }
    
    @Override
    public String getString() {
        byte[] encoded;
        try {
            encoded = this.getEncoded();
        }
        catch (final IOException ex) {
            throw new ASN1ParsingException("Internal error encoding BitString: " + ex.getMessage(), ex);
        }
        final StringBuilder sb = new StringBuilder(1 + encoded.length * 2);
        sb.append('#');
        for (int i = 0; i != encoded.length; ++i) {
            final byte b = encoded[i];
            sb.append(ASN1BitString.table[b >>> 4 & 0xF]);
            sb.append(ASN1BitString.table[b & 0xF]);
        }
        return sb.toString();
    }
    
    public int intValue() {
        int n = 0;
        final int min = Math.min(5, this.contents.length - 1);
        for (int i = 1; i < min; ++i) {
            n |= (this.contents[i] & 0xFF) << 8 * (i - 1);
        }
        if (1 <= min && min < 5) {
            n |= ((byte)(this.contents[min] & 255 << (this.contents[0] & 0xFF)) & 0xFF) << 8 * (min - 1);
        }
        return n;
    }
    
    public byte[] getOctets() {
        if (this.contents[0] != 0) {
            throw new IllegalStateException("attempt to get non-octet aligned data from BIT STRING");
        }
        return Arrays.copyOfRange(this.contents, 1, this.contents.length);
    }
    
    public byte[] getBytes() {
        if (this.contents.length == 1) {
            return ASN1OctetString.EMPTY_OCTETS;
        }
        final int n = this.contents[0] & 0xFF;
        final byte[] copyOfRange;
        final byte[] array = copyOfRange = Arrays.copyOfRange(this.contents, 1, this.contents.length);
        final int n2 = array.length - 1;
        copyOfRange[n2] &= (byte)(255 << n);
        return array;
    }
    
    public int getBytesLength() {
        return this.contents.length - 1;
    }
    
    public boolean isOctetAligned() {
        return this.getPadBits() == 0;
    }
    
    @Override
    public int getPadBits() {
        return this.contents[0] & 0xFF;
    }
    
    @Override
    public String toString() {
        return this.getString();
    }
    
    @Override
    public int hashCode() {
        if (this.contents.length < 2) {
            return 1;
        }
        final int n = this.contents[0] & 0xFF;
        final int n2 = this.contents.length - 1;
        return Arrays.hashCode(this.contents, 0, n2) * 257 ^ (byte)(this.contents[n2] & 255 << n);
    }
    
    @Override
    boolean asn1Equals(final ASN1Primitive asn1Primitive) {
        if (!(asn1Primitive instanceof ASN1BitString)) {
            return false;
        }
        final ASN1BitString asn1BitString = (ASN1BitString)asn1Primitive;
        final byte[] contents = this.contents;
        final byte[] contents2 = asn1BitString.contents;
        final int length = contents.length;
        if (contents2.length != length) {
            return false;
        }
        if (length == 1) {
            return true;
        }
        final int n = length - 1;
        for (int i = 0; i < n; ++i) {
            if (contents[i] != contents2[i]) {
                return false;
            }
        }
        final int n2 = contents[0] & 0xFF;
        return (byte)(contents[n] & 255 << n2) == (byte)(contents2[n] & 255 << n2);
    }
    
    @Override
    public ASN1Primitive getLoadedObject() {
        return this.toASN1Primitive();
    }
    
    @Override
    ASN1Primitive toDERObject() {
        return new DERBitString(this.contents, false);
    }
    
    @Override
    ASN1Primitive toDLObject() {
        return new DLBitString(this.contents, false);
    }
    
    static ASN1BitString createPrimitive(final byte[] array) {
        final int length = array.length;
        if (length < 1) {
            throw new IllegalArgumentException("truncated BIT STRING detected");
        }
        final int n = array[0] & 0xFF;
        if (n > 0) {
            if (n > 7 || length < 2) {
                throw new IllegalArgumentException("invalid pad bits detected");
            }
            final byte b = array[length - 1];
            if (b != (byte)(b & 255 << n)) {
                return new DLBitString(array, false);
            }
        }
        return new DERBitString(array, false);
    }
    
    static {
        TYPE = new ASN1UniversalType(3) {
            @Override
            ASN1Primitive fromImplicitPrimitive(final DEROctetString derOctetString) {
                return ASN1BitString.createPrimitive(derOctetString.getOctets());
            }
            
            @Override
            ASN1Primitive fromImplicitConstructed(final ASN1Sequence asn1Sequence) {
                return asn1Sequence.toASN1BitString();
            }
        };
        table = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    }
}
