text:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Security.Cryptography;

namespace Service.Layer.EncryptionService.KeyCreation.Builders
{
    internal class AsnKeyBuilder
    {
        private static readonly byte[] ZERO = new byte[] {0};
        private static readonly byte[] EMPTY = new byte[] {};

        // PublicKeyInfo (X.509 compatible) message
        /// <summary>
        ///     Returns the AsnMessage representing the X.509 PublicKeyInfo.
        /// </summary>
        /// <param name="publicKey">The DSA key to be encoded.</param>
        /// <returns>
        ///     Returns the AsnType representing the
        ///     X.509 PublicKeyInfo.
        /// </returns>
        /// <seealso cref="PrivateKeyToPKCS8(DSAParameters)" />
        /// <seealso cref="PrivateKeyToPKCS8(RSAParameters)" />
        /// <seealso cref="PublicKeyToX509(RSAParameters)" />
        internal static AsnMessage PublicKeyToX509(DSAParameters publicKey)
        {
            // Value Type cannot be null
            // Debug.Assert(null != publicKey);

            /* *
            * SEQUENCE              // PrivateKeyInfo
            * +- SEQUENCE           // AlgorithmIdentifier
            * |  +- OID             // 1.2.840.10040.4.1
            * |  +- SEQUENCE        // DSS-Params (Optional Parameters)
            * |    +- INTEGER (P)
            * |    +- INTEGER (Q)
            * |    +- INTEGER (G)
            * +- BITSTRING          // PublicKey
            *    +- INTEGER(Y)      // DSAPublicKey Y
            * */

            // DSA Parameters
            AsnType p = CreateIntegerPos(publicKey.P);
            AsnType q = CreateIntegerPos(publicKey.Q);
            AsnType g = CreateIntegerPos(publicKey.G);

            // Sequence - DSA-Params
            AsnType dssParams = CreateSequence(new[] {p, q, g});

            // OID - packed 1.2.840.10040.4.1
            //   { 0x2A, 0x86, 0x48, 0xCE, 0x38, 0x04, 0x01 }
            AsnType oid = CreateOid("1.2.840.10040.4.1");

            // Sequence
            AsnType algorithmID = CreateSequence(new[] {oid, dssParams});

            // Public Key Y
            AsnType y = CreateIntegerPos(publicKey.Y);
            AsnType key = CreateBitString(y);

            // Sequence 'A'
            AsnType publicKeyInfo =
                CreateSequence(new[] {algorithmID, key});

            return new AsnMessage(publicKeyInfo.GetBytes(), "X.509");
        }

        // PublicKeyInfo (X.509 compatible) message
        /// <summary>
        ///     Returns the AsnMessage representing the X.509 PublicKeyInfo.
        /// </summary>
        /// <param name="publicKey">The RSA key to be encoded.</param>
        /// <returns>
        ///     Returns the AsnType representing the
        ///     X.509 PublicKeyInfo.
        /// </returns>
        /// <seealso cref="PrivateKeyToPKCS8(DSAParameters)" />
        /// <seealso cref="PrivateKeyToPKCS8(RSAParameters)" />
        /// <seealso cref="PublicKeyToX509(DSAParameters)" />
        internal static AsnMessage PublicKeyToX509(RSAParameters publicKey)
        {
            // Value Type cannot be null
            // Debug.Assert(null != publicKey);

            /* *
            * SEQUENCE              // PrivateKeyInfo
            * +- SEQUENCE           // AlgorithmIdentifier
            *    +- OID             // 1.2.840.113549.1.1.1
            *    +- Null            // Optional Parameters
            * +- BITSTRING          // PrivateKey
            *    +- SEQUENCE        // RSAPrivateKey
            *       +- INTEGER(N)   // N
            *       +- INTEGER(E)   // E
            * */

            // OID - packed 1.2.840.113549.1.1.1
            //   { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }
            AsnType oid = CreateOid("1.2.840.113549.1.1.1");
            AsnType algorithmID =
                CreateSequence(new[] {oid, CreateNull()});

            AsnType n = CreateIntegerPos(publicKey.Modulus);
            AsnType e = CreateIntegerPos(publicKey.Exponent);
            AsnType key = CreateBitString(
                CreateSequence(new[] {n, e})
                );

            AsnType publicKeyInfo =
                CreateSequence(new[] {algorithmID, key});

            return new AsnMessage(publicKeyInfo.GetBytes(), "X.509");
        }

        // PKCS #8, Section 6 (PrivateKeyInfo) message
        // !!!!!!!!!!!!!!! Unencrypted !!!!!!!!!!!!!!!
        /// <summary>
        ///     Returns AsnMessage representing the unencrypted
        ///     PKCS #8 PrivateKeyInfo.
        /// </summary>
        /// <param name="privateKey">The DSA key to be encoded.</param>
        /// <returns>
        ///     Returns the AsnType representing the unencrypted
        ///     PKCS #8 PrivateKeyInfo.
        /// </returns>
        /// <seealso cref="PrivateKeyToPKCS8(RSAParameters)" />
        /// <seealso cref="PublicKeyToX509(DSAParameters)" />
        /// <seealso cref="PublicKeyToX509(RSAParameters)" />
        internal static AsnMessage PrivateKeyToPKCS8(DSAParameters privateKey)
        {
            // Value Type cannot be null
            // Debug.Assert(null != privateKey);

            /* *
            * SEQUENCE              // PrivateKeyInfo
            * +- INTEGER(0)         // Version (v1998)
            * +- SEQUENCE           // AlgorithmIdentifier
            * |  +- OID             // 1.2.840.10040.4.1
            * |  +- SEQUENCE        // DSS-Params (Optional Parameters)
            * |    +- INTEGER (P)
            * |    +- INTEGER (Q)
            * |    +- INTEGER (G)
            * +- OCTETSTRING        // PrivateKey
            *    +- INTEGER(X)   // DSAPrivateKey X
            * */

            // Version - 0 (v1998)
            AsnType version = CreateInteger(ZERO);

            // Domain Parameters
            AsnType p = CreateIntegerPos(privateKey.P);
            AsnType q = CreateIntegerPos(privateKey.Q);
            AsnType g = CreateIntegerPos(privateKey.G);

            AsnType dssParams = CreateSequence(new[] {p, q, g});

            // OID - packed 1.2.840.10040.4.1
            //   { 0x2A, 0x86, 0x48, 0xCE, 0x38, 0x04, 0x01 }
            AsnType oid = CreateOid("1.2.840.10040.4.1");

            // AlgorithmIdentifier
            AsnType algorithmID = CreateSequence(new[] {oid, dssParams});

            // Private Key X
            AsnType x = CreateIntegerPos(privateKey.X);
            AsnType key = CreateOctetString(x);

            // Sequence
            AsnType privateKeyInfo =
                CreateSequence(new[] {version, algorithmID, key});

            return new AsnMessage(privateKeyInfo.GetBytes(), "PKCS#8");
        }

        // PKCS #8, Section 6 (PrivateKeyInfo) message
        // !!!!!!!!!!!!!!! Unencrypted !!!!!!!!!!!!!!!
        /// <summary>
        ///     Returns AsnMessage representing the unencrypted
        ///     PKCS #8 PrivateKeyInfo.
        /// </summary>
        /// <param name="privateKey">The RSA key to be encoded.</param>
        /// <returns>
        ///     Returns the AsnType representing the unencrypted
        ///     PKCS #8 PrivateKeyInfo.
        /// </returns>
        /// <seealso cref="PrivateKeyToPKCS8(DSAParameters)" />
        /// <seealso cref="PublicKeyToX509(DSAParameters)" />
        /// <seealso cref="PublicKeyToX509(RSAParameters)" />
        internal static AsnMessage PrivateKeyToPKCS8(RSAParameters privateKey)
        {
            // Value Type cannot be null
            // Debug.Assert(null != privateKey);

            /* *
            * SEQUENCE                  // PublicKeyInfo
            * +- INTEGER(0)             // Version - 0 (v1998)
            * +- SEQUENCE               // AlgorithmIdentifier
            *    +- OID                 // 1.2.840.113549.1.1.1
            *    +- NULL                // Optional Parameters
            * +- OCTETSTRING            // PrivateKey
            *    +- SEQUENCE            // RSAPrivateKey
            *       +- INTEGER(0)       // Version - 0 (v1998)
            *       +- INTEGER(N)
            *       +- INTEGER(E)
            *       +- INTEGER(D)
            *       +- INTEGER(P)
            *       +- INTEGER(Q)
            *       +- INTEGER(DP)
            *       +- INTEGER(DQ)
            *       +- INTEGER(Inv Q)
            * */

            AsnType n = CreateIntegerPos(privateKey.Modulus);
            AsnType e = CreateIntegerPos(privateKey.Exponent);
            AsnType d = CreateIntegerPos(privateKey.D);
            AsnType p = CreateIntegerPos(privateKey.P);
            AsnType q = CreateIntegerPos(privateKey.Q);
            AsnType dp = CreateIntegerPos(privateKey.DP);
            AsnType dq = CreateIntegerPos(privateKey.DQ);
            AsnType iq = CreateIntegerPos(privateKey.InverseQ);

            // Version - 0 (v1998)
            AsnType version = CreateInteger(new byte[] {0});

            // octstring = OCTETSTRING(SEQUENCE(INTEGER(0)INTEGER(N)...))
            AsnType key = CreateOctetString(
                CreateSequence(new[] {version, n, e, d, p, q, dp, dq, iq})
                );

            // OID - packed 1.2.840.113549.1.1.1
            //   { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }
            AsnType algorithmID = CreateSequence(new[] {CreateOid("1.2.840.113549.1.1.1"), CreateNull()}
                );

            // PrivateKeyInfo
            AsnType privateKeyInfo =
                CreateSequence(new[] {version, algorithmID, key});

            return new AsnMessage(privateKeyInfo.GetBytes(), "PKCS#8");
        }

        /// <summary>
        ///     <para>
        ///         An ordered collection of one or more types.
        ///         Returns the AsnType representing an ASN.1 encoded sequence.
        ///     </para>
        ///     <para>
        ///         If the AsnType is null, an empty sequence (length 0)
        ///         is returned.
        ///     </para>
        /// </summary>
        /// <param name="value">
        ///     An AsnType consisting of
        ///     a single value to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded sequence.
        /// </returns>
        /// <seealso cref="CreateSet(AsnType)" />
        /// <seealso cref="CreateSet(AsnType[])" />
        /// <seealso cref="CreateSetOf(AsnType)" />
        /// <seealso cref="CreateSetOf(AsnType[])" />
        /// <seealso cref="CreateSequence(AsnType)" />
        /// <seealso cref="CreateSequence(AsnType[])" />
        /// <seealso cref="CreateSequenceOf(AsnType)" />
        /// <seealso cref="CreateSequenceOf(AsnType[])" />
        internal static AsnType CreateSequence(AsnType value)
        {
            // Should be at least 1...
            Debug.Assert(!IsEmpty(value));

            // One or more required
            if (IsEmpty(value))
            {
                throw new ArgumentException("A sequence requires at least one value.");
            }

            // Sequence: Tag 0x30 (16, Universal, Constructed)
            return new AsnType(0x30, value.GetBytes());
        }

        /// <summary>
        ///     <para>
        ///         An ordered collection of one or more types.
        ///         Returns the AsnType representing an ASN.1 encoded sequence.
        ///     </para>
        ///     <para>
        ///         If the AsnType is null, an
        ///         empty sequence (length 0) is returned.
        ///     </para>
        /// </summary>
        /// <param name="values">
        ///     An array of AsnType consisting of
        ///     the values in the collection to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded Set.
        /// </returns>
        /// <seealso cref="CreateSet(AsnType)" />
        /// <seealso cref="CreateSet(AsnType[])" />
        /// <seealso cref="CreateSetOf(AsnType)" />
        /// <seealso cref="CreateSetOf(AsnType[])" />
        /// <seealso cref="CreateSequence(AsnType)" />
        /// <seealso cref="CreateSequence(AsnType[])" />
        /// <seealso cref="CreateSequenceOf(AsnType)" />
        /// <seealso cref="CreateSequenceOf(AsnType[])" />
        internal static AsnType CreateSequence(AsnType[] values)
        {
            // Should be at least 1...
            Debug.Assert(!IsEmpty(values));

            // One or more required
            if (IsEmpty(values))
            {
                throw new ArgumentException("A sequence requires at least one value.");
            }

            // Sequence: Tag 0x30 (16, Universal, Constructed)
            return new AsnType((0x10 | 0x20), Concatenate(values));
        }

        /// <summary>
        ///     <para>
        ///         An ordered collection zero, one or more types.
        ///         Returns the AsnType representing an ASN.1 encoded sequence.
        ///     </para>
        ///     <para>
        ///         If the AsnType value is null,an
        ///         empty sequence (length 0) is returned.
        ///     </para>
        /// </summary>
        /// <param name="value">
        ///     An AsnType consisting of
        ///     a single value to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded sequence.
        /// </returns>
        /// <seealso cref="CreateSet(AsnType)" />
        /// <seealso cref="CreateSet(AsnType[])" />
        /// <seealso cref="CreateSetOf(AsnType)" />
        /// <seealso cref="CreateSetOf(AsnType[])" />
        /// <seealso cref="CreateSequence(AsnType)" />
        /// <seealso cref="CreateSequence(AsnType[])" />
        /// <seealso cref="CreateSequenceOf(AsnType)" />
        /// <seealso cref="CreateSequenceOf(AsnType[])" />
        internal static AsnType CreateSequenceOf(AsnType value)
        {
            // From the ASN.1 Mailing List
            if (IsEmpty(value))
            {
                return new AsnType(0x30, EMPTY);
            }

            // Sequence: Tag 0x30 (16, Universal, Constructed)
            return new AsnType(0x30, value.GetBytes());
        }

        /// <summary>
        ///     <para>
        ///         An ordered collection zero, one or more types.
        ///         Returns the AsnType representing an ASN.1 encoded sequence.
        ///     </para>
        ///     <para>
        ///         If the AsnType array is null or the array is 0 length,
        ///         an empty sequence (length 0) is returned.
        ///     </para>
        /// </summary>
        /// <param name="values">
        ///     An AsnType consisting of
        ///     the values in the collection to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded sequence.
        /// </returns>
        /// <seealso cref="CreateSet(AsnType)" />
        /// <seealso cref="CreateSet(AsnType[])" />
        /// <seealso cref="CreateSetOf(AsnType)" />
        /// <seealso cref="CreateSetOf(AsnType[])" />
        /// <seealso cref="CreateSequence(AsnType)" />
        /// <seealso cref="CreateSequence(AsnType[])" />
        /// <seealso cref="CreateSequenceOf(AsnType)" />
        /// <seealso cref="CreateSequenceOf(AsnType[])" />
        internal static AsnType CreateSequenceOf(AsnType[] values)
        {
            // From the ASN.1 Mailing List
            if (IsEmpty(values))
            {
                return new AsnType(0x30, EMPTY);
            }

            // Sequence: Tag 0x30 (16, Universal, Constructed)
            return new AsnType(0x30, Concatenate(values));
        }

        /// <summary>
        ///     <para>
        ///         An ordered sequence of zero, one or more bits. Returns
        ///         the AsnType representing an ASN.1 encoded bit string.
        ///     </para>
        ///     <para>
        ///         If octets is null or length is 0, an empty (0 length)
        ///         bit string is returned.
        ///     </para>
        /// </summary>
        /// <param name="octets">
        ///     A MSB (big endian) byte[] representing the
        ///     bit string to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded bit string.
        /// </returns>
        /// <seealso cref="CreateBitString(byte[], uint)" />
        /// <seealso cref="CreateBitString(AsnType)" />
        /// <seealso cref="CreateBitString(AsnType[])" />
        /// <seealso cref="CreateBitString(string)" />
        /// <seealso cref="CreateOctetString(byte[])" />
        /// <seealso cref="CreateOctetString(AsnType)" />
        /// <seealso cref="CreateOctetString(AsnType[])" />
        /// <seealso cref="CreateOctetString(string)" />
        internal static AsnType CreateBitString(byte[] octets)
        {
            // BitString: Tag 0x03 (3, Universal, Primitive)
            return CreateBitString(octets, 0);
        }

        /// <summary>
        ///     <para>
        ///         An ordered sequence of zero, one or more bits. Returns
        ///         the AsnType representing an ASN.1 encoded bit string.
        ///     </para>
        ///     <para>
        ///         unusedBits is applied to the end of the bit string,
        ///         not the start of the bit string. unusedBits must be less than 8
        ///         (the size of an octet). Refer to ITU X.680, Section 32.
        ///     </para>
        ///     <para>
        ///         If octets is null or length is 0, an empty (0 length)
        ///         bit string is returned.
        ///     </para>
        /// </summary>
        /// <param name="octets">
        ///     A MSB (big endian) byte[] representing the
        ///     bit string to be encoded.
        /// </param>
        /// <param name="unusedBits">
        ///     The number of unused trailing binary
        ///     digits in the bit string to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded bit string.
        /// </returns>
        /// <seealso cref="CreateBitString(byte[])" />
        /// <seealso cref="CreateBitString(AsnType)" />
        /// <seealso cref="CreateBitString(AsnType[])" />
        /// <seealso cref="CreateBitString(string)" />
        /// <seealso cref="CreateOctetString(byte[])" />
        /// <seealso cref="CreateOctetString(AsnType)" />
        /// <seealso cref="CreateOctetString(AsnType[])" />
        /// <seealso cref="CreateOctetString(string)" />
        internal static AsnType CreateBitString(byte[] octets, uint unusedBits)
        {
            if (IsEmpty(octets))
            {
                // Empty octet string
                return new AsnType(0x03, EMPTY);
            }

            if (!(unusedBits < 8))
            {
                throw new ArgumentException("Unused bits must be less than 8.");
            }

            byte[] b = Concatenate(new[] {(byte) unusedBits}, octets);
            // BitString: Tag 0x03 (3, Universal, Primitive)
            return new AsnType(0x03, b);
        }

        /// <summary>
        ///     An ordered sequence of zero, one or more bits. Returns
        ///     the AsnType representing an ASN.1 encoded bit string.
        ///     If value is null, an empty (0 length) bit string is
        ///     returned.
        /// </summary>
        /// <param name="value">An AsnType object to be encoded.</param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded bit string.
        /// </returns>
        /// <seealso cref="CreateBitString(byte[])" />
        /// <seealso cref="CreateBitString(byte[], uint)" />
        /// <seealso cref="CreateBitString(AsnType[])" />
        /// <seealso cref="CreateBitString(string)" />
        /// <seealso cref="CreateOctetString(byte[])" />
        /// <seealso cref="CreateOctetString(AsnType)" />
        /// <seealso cref="CreateOctetString(AsnType[])" />
        /// <seealso cref="CreateOctetString(string)" />
        internal static AsnType CreateBitString(AsnType value)
        {
            if (IsEmpty(value))
            {
                return new AsnType(0x03, EMPTY);
            }

            // BitString: Tag 0x03 (3, Universal, Primitive)
            return CreateBitString(value.GetBytes(), 0x00);
        }

        /// <summary>
        ///     An ordered sequence of zero, one or more bits. Returns
        ///     the AsnType representing an ASN.1 encoded bit string.
        ///     If value is null, an empty (0 length) bit string is
        ///     returned.
        /// </summary>
        /// <param name="values">An AsnType object to be encoded.</param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded bit string.
        /// </returns>
        /// <seealso cref="CreateBitString(byte[])" />
        /// <seealso cref="CreateBitString(byte[], uint)" />
        /// <seealso cref="CreateBitString(AsnType)" />
        /// <seealso cref="CreateBitString(string)" />
        /// <seealso cref="CreateOctetString(byte[])" />
        /// <seealso cref="CreateOctetString(AsnType)" />
        /// <seealso cref="CreateOctetString(AsnType[])" />
        /// <seealso cref="CreateOctetString(string)" />
        internal static AsnType CreateBitString(AsnType[] values)
        {
            if (IsEmpty(values))
            {
                return new AsnType(0x03, EMPTY);
            }

            // BitString: Tag 0x03 (3, Universal, Primitive)
            return CreateBitString(Concatenate(values), 0x00);
        }

        /// <summary>
        ///     <para>
        ///         An ordered sequence of zero, one or more bits. Returns
        ///         the AsnType representing an ASN.1 encoded bit string.
        ///     </para>
        ///     <para>
        ///         If octets is null or length is 0, an empty (0 length)
        ///         bit string is returned.
        ///     </para>
        ///     <para>
        ///         If conversion fails, the bit string returned is a partial
        ///         bit string. The partial bit string ends at the octet before the
        ///         point of failure (it does not include the octet which could
        ///         not be parsed, or subsequent octets).
        ///     </para>
        /// </summary>
        /// <param name="value">
        ///     A MSB (big endian) byte[] representing the
        ///     bit string to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded bit string.
        /// </returns>
        /// <seealso cref="CreateBitString(byte[])" />
        /// <seealso cref="CreateBitString(byte[], uint)" />
        /// <seealso cref="CreateBitString(AsnType)" />
        /// <seealso cref="CreateOctetString(byte[])" />
        /// <seealso cref="CreateOctetString(AsnType)" />
        /// <seealso cref="CreateOctetString(AsnType[])" />
        /// <seealso cref="CreateOctetString(String)" />
        internal static AsnType CreateBitString(String value)
        {
            if (IsEmpty(value))
            {
                return CreateBitString(EMPTY);
            }

            // Any unused bits?
            int lstrlen = value.Length;
            int unusedBits = 8 - (lstrlen%8);
            if (8 == unusedBits)
            {
                unusedBits = 0;
            }

            for (int i = 0; i < unusedBits; i++)
            {
                value += "0";
            }

            // Determine number of octets
            int loctlen = (lstrlen + 7)/8;

            var octets = new List<byte>();
            for (int i = 0; i < loctlen; i++)
            {
                String s = value.Substring(i*8, 8);
                byte b = 0x00;

                try
                {
                    b = Convert.ToByte(s, 2);
                }

                catch (FormatException /*e*/)
                {
                    unusedBits = 0;
                    break;
                }
                catch (OverflowException /*e*/)
                {
                    unusedBits = 0;
                    break;
                }

                octets.Add(b);
            }

            // BitString: Tag 0x03 (3, Universal, Primitive)
            return CreateBitString(octets.ToArray(), (uint) unusedBits);
        }

        /// <summary>
        ///     An ordered sequence of zero, one or more octets. Returns
        ///     the ASN.1 encoded octet string. If octets is null or length
        ///     is 0, an empty (0 length) octet string is returned.
        /// </summary>
        /// <param name="value">
        ///     A MSB (big endian) byte[] representing the
        ///     octet string to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded octet string.
        /// </returns>
        /// <seealso cref="CreateBitString(byte[])" />
        /// <seealso cref="CreateBitString(byte[], uint)" />
        /// <seealso cref="CreateBitString(AsnType)" />
        /// <seealso cref="CreateBitString(String)" />
        /// <seealso cref="CreateOctetString(AsnType)" />
        /// <seealso cref="CreateOctetString(AsnType[])" />
        /// <seealso cref="CreateOctetString(String)" />
        internal static AsnType CreateOctetString(byte[] value)
        {
            if (IsEmpty(value))
            {
                // Empty octet string
                return new AsnType(0x04, EMPTY);
            }

            // OctetString: Tag 0x04 (4, Universal, Primitive)
            return new AsnType(0x04, value);
        }

        /// <summary>
        ///     An ordered sequence of zero, one or more octets. Returns
        ///     the byte[] representing an ASN.1 encoded octet string.
        ///     If octets is null or length is 0, an empty (0 length)
        ///     o ctet string is returned.
        /// </summary>
        /// <param name="value">An AsnType object to be encoded.</param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded octet string.
        /// </returns>
        /// <seealso cref="CreateBitString(byte[])" />
        /// <seealso cref="CreateBitString(byte[], uint)" />
        /// <seealso cref="CreateBitString(AsnType)" />
        /// <seealso cref="CreateBitString(String)" />
        /// <seealso cref="CreateOctetString(byte[])" />
        /// <seealso cref="CreateOctetString(String)" />
        internal static AsnType CreateOctetString(AsnType value)
        {
            if (IsEmpty(value))
            {
                // Empty octet string
                return new AsnType(0x04, 0x00);
            }

            // OctetString: Tag 0x04 (4, Universal, Primitive)
            return new AsnType(0x04, value.GetBytes());
        }

        /// <summary>
        ///     An ordered sequence of zero, one or more octets. Returns
        ///     the byte[] representing an ASN.1 encoded octet string.
        ///     If octets is null or length is 0, an empty (0 length)
        ///     o ctet string is returned.
        /// </summary>
        /// <param name="values">An AsnType object to be encoded.</param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded octet string.
        /// </returns>
        /// <seealso cref="CreateBitString(byte[])" />
        /// <seealso cref="CreateBitString(byte[], uint)" />
        /// <seealso cref="CreateBitString(AsnType)" />
        /// <seealso cref="CreateBitString(String)" />
        /// <seealso cref="CreateOctetString(byte[])" />
        /// <seealso cref="CreateOctetString(AsnType)" />
        /// <seealso cref="CreateOctetString(String)" />
        internal static AsnType CreateOctetString(AsnType[] values)
        {
            if (IsEmpty(values))
            {
                // Empty octet string
                return new AsnType(0x04, 0x00);
            }

            // OctetString: Tag 0x04 (4, Universal, Primitive)
            return new AsnType(0x04, Concatenate(values));
        }

        /// <summary>
        ///     <para>
        ///         An ordered sequence of zero, one or more bits. Returns
        ///         the AsnType representing an ASN.1 encoded octet string.
        ///     </para>
        ///     <para>
        ///         If octets is null or length is 0, an empty (0 length)
        ///         octet string is returned.
        ///     </para>
        ///     <para>
        ///         If conversion fails, the bit string returned is a partial
        ///         bit string. The partial octet string ends at the octet before the
        ///         point of failure (it does not include the octet which could
        ///         not be parsed, or subsequent octets).
        ///     </para>
        /// </summary>
        /// <param name="value">
        ///     A string representing the
        ///     octet string to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded octet string.
        /// </returns>
        /// <seealso cref="CreateBitString(byte[])" />
        /// <seealso cref="CreateBitString(byte[], uint)" />
        /// <seealso cref="CreateBitString(String)" />
        /// <seealso cref="CreateBitString(AsnType)" />
        /// <seealso cref="CreateOctetString(byte[])" />
        /// <seealso cref="CreateOctetString(AsnType)" />
        /// <seealso cref="CreateOctetString(AsnType[])" />
        internal static AsnType CreateOctetString(String value)
        {
            if (IsEmpty(value))
            {
                return CreateOctetString(EMPTY);
            }

            // Determine number of octets
            int len = (value.Length + 255)/256;

            var octets = new List<byte>();
            for (int i = 0; i < len; i++)
            {
                String s = value.Substring(i*2, 2);
                byte b = 0x00;

                try
                {
                    b = Convert.ToByte(s, 16);
                }
                catch (FormatException /*e*/)
                {
                    break;
                }
                catch (OverflowException /*e*/)
                {
                    break;
                }

                octets.Add(b);
            }

            // OctetString: Tag 0x04 (4, Universal, Primitive)
            return CreateOctetString(octets.ToArray());
        }

        /// <summary>
        ///     <para>
        ///         Returns the AsnType representing a ASN.1 encoded
        ///         integer. The octets pass through this method are not modified.
        ///     </para>
        ///     <para>
        ///         If octets is null or zero length, the method returns an
        ///         AsnType equivalent to CreateInteger(byte[]{0})..
        ///     </para>
        /// </summary>
        /// <param name="value">
        ///     A MSB (big endian) byte[] representing the
        ///     integer to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded integer.
        /// </returns>
        /// <example>
        ///     ASN.1 encoded 0:
        ///     <code>CreateInteger(null)</code>
        ///     <code>CreateInteger(new byte[]{0x00})</code>
        ///     <code>CreateInteger(new byte[]{0x00, 0x00})</code>
        /// </example>
        /// <example>
        ///     ASN.1 encoded 1:
        ///     <code>CreateInteger(new byte[]{0x01})</code>
        /// </example>
        /// <seealso cref="CreateIntegerPos" />
        /// <seealso cref="CreateIntegerNeg" />
        internal static AsnType CreateInteger(byte[] value)
        {
            // Is it better to add a '0', or silently
            //   drop the Integer? Dropping integers
            //   is probably not te best choice...
            if (IsEmpty(value))
            {
                return CreateInteger(ZERO);
            }

            return new AsnType(0x02, value);
        }

        /// <summary>
        ///     <para>
        ///         Returns the AsnType representing a positive ASN.1 encoded
        ///         integer. If the high bit of most significant byte is set,
        ///         the method prepends a 0x00 to octets before assigning the
        ///         value to ensure the resulting integer is interpreted as
        ///         positive in the application.
        ///     </para>
        ///     <para>
        ///         If octets is null or zero length, the method returns an
        ///         AsnType equivalent to CreateInteger(byte[]{0})..
        ///     </para>
        /// </summary>
        /// <param name="value">
        ///     A MSB (big endian) byte[] representing the
        ///     integer to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded positive integer.
        /// </returns>
        /// <example>
        ///     ASN.1 encoded 0:
        ///     <code>CreateIntegerPos(null)</code>
        ///     <code>CreateIntegerPos(new byte[]{0x00})</code>
        ///     <code>CreateIntegerPos(new byte[]{0x00, 0x00})</code>
        /// </example>
        /// <example>
        ///     ASN.1 encoded 1:
        ///     <code>CreateInteger(new byte[]{0x01})</code>
        /// </example>
        /// <seealso cref="CreateInteger" />
        /// <seealso cref="CreateIntegerNeg" />
        internal static AsnType CreateIntegerPos(byte[] value)
        {
            byte[] i = null, d = Duplicate(value);

            if (IsEmpty(d))
            {
                d = ZERO;
            }

            // Mediate the 2's compliment representation.
            // If the first byte has its high bit set, we will
            // add the additional byte of 0x00
            if (d.Length > 0 && d[0] > 0x7F)
            {
                i = new byte[d.Length + 1];
                i[0] = 0x00;
                Array.Copy(d, 0, i, 1, value.Length);
            }
            else
            {
                i = d;
            }

            // Integer: Tag 0x02 (2, Universal, Primitive)
            return CreateInteger(i);
        }

        /// <summary>
        ///     <para>
        ///         Returns the negative ASN.1 encoded integer. If the high
        ///         bit of most significant byte is set, the integer is already
        ///         considered negative.
        ///     </para>
        ///     <para>
        ///         If the high bit of most significant byte
        ///         is <bold>not</bold> set, the integer will be 2's complimented
        ///         to form a negative integer.
        ///     </para>
        ///     <para>
        ///         If octets is null or zero length, the method returns an
        ///         AsnType equivalent to CreateInteger(byte[]{0})..
        ///     </para>
        /// </summary>
        /// <param name="value">
        ///     A MSB (big endian) byte[] representing the
        ///     integer to be encoded.
        /// </param>
        /// <returns>Returns the negative ASN.1 encoded integer.</returns>
        /// <example>
        ///     ASN.1 encoded 0:
        ///     <code>CreateIntegerNeg(null)</code>
        ///     <code>CreateIntegerNeg(new byte[]{0x00})</code>
        ///     <code>CreateIntegerNeg(new byte[]{0x00, 0x00})</code>
        /// </example>
        /// <example>
        ///     ASN.1 encoded -1 (2's compliment 0xFF):
        ///     <code>CreateIntegerNeg(new byte[]{0x01})</code>
        /// </example>
        /// <example>
        ///     ASN.1 encoded -2 (2's compliment 0xFE):
        ///     <code>CreateIntegerNeg(new byte[]{0x02})</code>
        /// </example>
        /// <example>
        ///     ASN.1 encoded -1:
        ///     <code>CreateIntegerNeg(new byte[]{0xFF})</code>
        ///     <code>CreateIntegerNeg(new byte[]{0xFF,0xFF})</code>
        ///     Note: already negative since the high bit is set.
        /// </example>
        /// <example>
        ///     ASN.1 encoded -255 (2's compliment 0xFF, 0x01):
        ///     <code>CreateIntegerNeg(new byte[]{0x00,0xFF})</code>
        /// </example>
        /// <example>
        ///     ASN.1 encoded -255 (2's compliment 0xFF, 0xFF, 0x01):
        ///     <code>CreateIntegerNeg(new byte[]{0x00,0x00,0xFF})</code>
        /// </example>
        /// <seealso cref="CreateInteger" />
        /// <seealso cref="CreateIntegerPos" />
        internal static AsnType CreateIntegerNeg(byte[] value)
        {
            // Is it better to add a '0', or silently
            //   drop the Integer? Dropping integers
            //   is probably not te best choice...
            if (IsEmpty(value))
            {
                return CreateInteger(ZERO);
            }

            // No Trimming
            // The byte[] may be that way for a reason
            if (IsZero(value))
            {
                return CreateInteger(value);
            }

            //
            // At this point, we know we have at least 1 octet
            //

            // Is this integer already negative?
            if (value[0] >= 0x80)
                // Pass through with no modifications
            {
                return CreateInteger(value);
            }

            // No need to Duplicate - Compliment2s
            // performs the action
            byte[] c = Compliment2s(value);

            return CreateInteger(c);
        }

        /// <summary>
        ///     Returns the AsnType representing an ASN.1 encoded null.
        /// </summary>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded null.
        /// </returns>
        internal static AsnType CreateNull()
        {
            return new AsnType(0x05, new byte[] {0x00});
        }

        /// <summary>
        ///     Removes leading 0x00 octets from the byte[] octets. This
        ///     method may return an empty byte array (0 length).
        /// </summary>
        /// <param name="octets">An array of octets to trim.</param>
        /// <returns>A byte[] with leading 0x00 octets removed.</returns>
        internal static byte[] TrimStart(byte[] octets)
        {
            if (IsEmpty(octets) || IsZero(octets))
            {
                return new byte[] {};
            }

            byte[] d = Duplicate(octets);

            // Position of the first non-zero value
            int pos = 0;
            foreach (byte b in d)
            {
                if (0 != b)
                {
                    break;
                }
                pos++;
            }

            // Nothing to trim
            if (pos == d.Length)
            {
                return octets;
            }

            // Allocate trimmed array
            var t = new byte[d.Length - pos];

            // Copy
            Array.Copy(d, pos, t, 0, t.Length);

            return t;
        }

        /// <summary>
        ///     Removes trailing 0x00 octets from the byte[] octets. This
        ///     method may return an empty byte array (0 length).
        /// </summary>
        /// <param name="octets">An array of octets to trim.</param>
        /// <returns>A byte[] with trailing 0x00 octets removed.</returns>
        internal static byte[] TrimEnd(byte[] octets)
        {
            if (IsEmpty(octets) || IsZero(octets))
            {
                return EMPTY;
            }

            byte[] d = Duplicate(octets);

            Array.Reverse(d);

            d = TrimStart(d);

            Array.Reverse(d);

            return d;
        }

        /// <summary>
        ///     Returns the AsnType representing an ASN.1 encoded OID.
        ///     If conversion fails, the result is a partial conversion
        ///     up to the point of failure. If the oid string is null or
        ///     not well formed, an empty byte[] is returned.
        /// </summary>
        /// <param name="value">
        ///     The string representing the object
        ///     identifier to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded object identifier.
        /// </returns>
        /// <example>
        ///     The following assigns the encoded AsnType
        ///     for a RSA key to oid:
        ///     <code>AsnType oid = CreateOid("1.2.840.113549.1.1.1")</code>
        /// </example>
        /// <seealso cref="CreateOid(byte[])" />
        internal static AsnType CreateOid(String value)
        {
            // Punt?
            if (IsEmpty(value))
            {
                return null;
            }

            String[] tokens = value.Split(new[] {' ', '.'});

            // Punt?
            if (IsEmpty(tokens))
            {
                return null;
            }

            // Parsing/Manipulation of the arc value
            UInt64 a = 0;

            // One or more strings are available
            var arcs = new List<UInt64>();

            foreach (String t in tokens)
            {
                // No empty or ill-formed strings...
                if (t.Length == 0)
                {
                    break;
                }

                try
                {
                    a = Convert.ToUInt64(t, CultureInfo.InvariantCulture);
                }
                catch (FormatException /*e*/)
                {
                    break;
                }
                catch (OverflowException /*e*/)
                {
                    break;
                }

                arcs.Add(a);
            }

            // Punt?
            if (0 == arcs.Count)
            {
                return null;
            }

            // Octets to be returned to caller
            var octets = new List<byte>();

            // Guard the case of a small list
            // The list has at least 1 item...
            if (arcs.Count >= 1)
            {
                a = arcs[0]*40;
            }
            if (arcs.Count >= 2)
            {
                a += arcs[1];
            }
            octets.Add((byte) (a));

            // Add remaining arcs (subidentifiers)
            for (int i = 2; i < arcs.Count; i++)
            {
                // Scratch list builder for this arc
                var temp = new List<byte>();

                // The current arc (subidentifier)
                UInt64 arc = arcs[i];

                // Build the arc (subidentifier) byte array
                // The array is built in reverse (LSB to MSB).
                do
                {
                    // Each entry is formed from the low 7 bits (0x7F).
                    // Set high bit of all entries (0x80) per X.680. We
                    // will unset the high bit of the final byte later.
                    temp.Add((byte) (0x80 | (arc & 0x7F)));
                    arc >>= 7;
                } while (0 != arc);

                // Grab resulting array. Because of the do/while,
                // there is at least one value in the array.
                byte[] t = temp.ToArray();

                // Unset high bit of byte t[0]
                // t[0] will be LSB after the array is reversed.
                t[0] = (byte) (0x7F & t[0]);

                // MSB first...
                Array.Reverse(t);

                // Add to the resulting array
                foreach (byte b in t)
                {
                    octets.Add(b);
                }
            }

            return CreateOid(octets.ToArray());
        }

        /// <summary>
        ///     Returns the AsnType representing an ASN.1 encoded OID.
        ///     If conversion fails, the result is a partial conversion
        ///     (up to the point of failure). If octets is null, an
        ///     empty byte[] is returned.
        /// </summary>
        /// <param name="value">
        ///     The packed byte[] representing the object
        ///     identifier to be encoded.
        /// </param>
        /// <returns>
        ///     Returns the AsnType representing an ASN.1
        ///     encoded object identifier.
        /// </returns>
        /// <example>
        ///     The following assigns the encoded AsnType for a RSA
        ///     key to oid:
        ///     <code>// Packed 1.2.840.113549.1.1.1
        /// byte[] rsa = new byte[] { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
        /// AsnType = CreateOid(rsa)</code>
        /// </example>
        /// <seealso cref="CreateOid(String)" />
        internal static AsnType CreateOid(byte[] value)
        {
            // Punt...
            if (IsEmpty(value))
            {
                return null;
            }

            // OID: Tag 0x06 (6, Universal, Primitive)
            return new AsnType(0x06, value);
        }

        private static byte[] Compliment2s(byte[] value)
        {
            if (IsEmpty(value))
            {
                return EMPTY;
            }

            // 2s Compliment of 0 is 0
            if (IsZero(value))
            {
                return Duplicate(value);
            }

            // Make a copy of octet array
            byte[] d = Duplicate(value);

            int carry = 1;
            for (int i = d.Length - 1; i >= 0; i--)
            {
                // Compliment
                d[i] = (byte) ~d[i];

                // Add
                int j = d[i] + carry;

                // Write Back
                d[i] = (byte) (j & 0xFF);

                // Determine Next Carry
                if (0x100 == (j & 0x100))
                {
                    carry = 1;
                }
                else
                {
                    carry = 0;
                }
            }

            // Carry Array (we may need to carry out of 'd'
            byte[] c = null;
            if (1 == carry)
            {
                c = new byte[d.Length + 1];

                // Sign Extend....
                c[0] = 0xFF;

                Array.Copy(d, 0, c, 1, d.Length);
            }
            else
            {
                c = d;
            }

            return c;
        }

        private static byte[] Concatenate(ICollection<AsnType> values)
        {
            // Nothing in, nothing out
            if (IsEmpty(values))
            {
                return new byte[] {};
            }

            int length = 0;
            foreach (AsnType t in values)
            {
                if (null != t)
                {
                    length += t.GetBytes().Length;
                }
            }

            var cated = new byte[length];

            int current = 0;
            foreach (AsnType t in values)
            {
                if (null != t)
                {
                    byte[] b = t.GetBytes();

                    Array.Copy(b, 0, cated, current, b.Length);
                    current += b.Length;
                }
            }

            return cated;
        }

        private static byte[] Concatenate(byte[] first, byte[] second)
        {
            return Concatenate(new[] {first, second});
        }

        private static byte[] Concatenate(ICollection<byte[]> values)
        {
            // Nothing in, nothing out
            if (IsEmpty(values))
            {
                return new byte[] {};
            }

            int length = 0;
            foreach (var b in values)
            {
                if (null != b)
                {
                    length += b.Length;
                }
            }

            var cated = new byte[length];

            int current = 0;
            foreach (var b in values)
            {
                if (null != b)
                {
                    Array.Copy(b, 0, cated, current, b.Length);
                    current += b.Length;
                }
            }

            return cated;
        }

        private static byte[] Duplicate(byte[] b)
        {
            if (IsEmpty(b))
            {
                return EMPTY;
            }

            var d = new byte[b.Length];
            Array.Copy(b, d, b.Length);

            return d;
        }

        private static bool IsZero(IList<byte> octets)
        {
            if (IsEmpty(octets))
            {
                return false;
            }

            bool allZeros = true;
            for (int i = 0; i < octets.Count; i++)
            {
                if (0 != octets[i])
                {
                    allZeros = false;
                    break;
                }
            }
            return allZeros;
        }

        private static bool IsEmpty(ICollection<byte> octets)
        {
            if (null == octets || 0 == octets.Count)
            {
                return true;
            }

            return false;
        }

        private static bool IsEmpty(String s)
        {
            if (null == s || 0 == s.Length)
            {
                return true;
            }

            return false;
        }

        private static bool IsEmpty(ICollection<string> strings)
        {
            if (null == strings || 0 == strings.Count)
            {
                return true;
            }

            return false;
        }

        private static bool IsEmpty(AsnType value)
        {
            if (null == value)
            {
                return true;
            }

            return false;
        }

        private static bool IsEmpty(ICollection<AsnType> values)
        {
            if (null == values || 0 == values.Count)
            {
                return true;
            }

            return false;
        }

        private static bool IsEmpty(ICollection<byte[]> arrays)
        {
            if (null == arrays || 0 == arrays.Count)
            {
                return true;
            }

            return false;
        }

        internal class AsnMessage
        {
            private readonly String m_format;
            private readonly byte[] m_octets;

            internal AsnMessage(byte[] octets, String format)
            {
                m_octets = octets;
                m_format = format;
            }

            internal int Length
            {
                get
                {
                    if (null == m_octets)
                    {
                        return 0;
                    }
                    return m_octets.Length;
                }
                // set { m_length = value; }
            }

            internal byte[] GetBytes()
            {
                if (null == m_octets)
                {
                    return new byte[] {};
                }

                return m_octets;
            }

            internal String GetFormat()
            {
                return m_format;
            }
        }

        internal class AsnType
        {
            // Constructors
            // No default - must specify tag and data

            private readonly bool m_raw;

            // Setters and Getters
            private readonly byte[] m_tag;
            private byte[] m_length;
            private byte[] m_octets;

            public AsnType(byte tag, byte octet)
            {
                m_raw = false;
                m_tag = new[] {tag};
                m_octets = new[] {octet};
            }

            public AsnType(byte tag, byte[] octets)
            {
                m_raw = false;
                m_tag = new[] {tag};
                m_octets = octets;
            }

            public AsnType(byte tag, byte[] length, byte[] octets)
            {
                m_raw = true;
                m_tag = new[] {tag};
                m_length = length;
                m_octets = octets;
            }

            public byte[] Tag
            {
                get
                {
                    if (null == m_tag)
                    {
                        return EMPTY;
                    }
                    return m_tag;
                }
                // set { m_tag = value; }
            }

            public byte[] Length
            {
                get
                {
                    if (null == m_length)
                    {
                        return EMPTY;
                    }
                    return m_length;
                }
                // set { m_length = value; }
            }

            public byte[] Octets
            {
                get
                {
                    if (null == m_octets)
                    {
                        return EMPTY;
                    }
                    return m_octets;
                }
                set { m_octets = value; }
            }

            // Methods
            internal byte[] GetBytes()
            {
                // Created raw by user
                // return the bytes....
                if (m_raw)
                {
                    return Concatenate(
                        new[] {m_tag, m_length, m_octets}
                        );
                }

                SetLength();

                // Special case
                // Null does not use length
                if (0x05 == m_tag[0])
                {
                    return Concatenate(
                        new[] {m_tag, m_octets}
                        );
                }

                return Concatenate(
                    new[] {m_tag, m_length, m_octets}
                    );
            }

            private void SetLength()
            {
                if (null == m_octets)
                {
                    m_length = ZERO;
                    return;
                }

                // Special case
                // Null does not use length
                if (0x05 == m_tag[0])
                {
                    m_length = EMPTY;
                    return;
                }

                byte[] length = null;

                // Length: 0 <= l < 0x80
                if (m_octets.Length < 0x80)
                {
                    length = new byte[1];
                    length[0] = (byte) m_octets.Length;
                }
                    // 0x80 < length <= 0xFF
                else if (m_octets.Length <= 0xFF)
                {
                    length = new byte[2];
                    length[0] = 0x81;
                    length[1] = (byte) ((m_octets.Length & 0xFF));
                }

                    //
                    // We should almost never see these...
                    //

                    // 0xFF < length <= 0xFFFF
                else if (m_octets.Length <= 0xFFFF)
                {
                    length = new byte[3];
                    length[0] = 0x82;
                    length[1] = (byte) ((m_octets.Length & 0xFF00) >> 8);
                    length[2] = (byte) ((m_octets.Length & 0xFF));
                }

                    // 0xFFFF < length <= 0xFFFFFF
                else if (m_octets.Length <= 0xFFFFFF)
                {
                    length = new byte[4];
                    length[0] = 0x83;
                    length[1] = (byte) ((m_octets.Length & 0xFF0000) >> 16);
                    length[2] = (byte) ((m_octets.Length & 0xFF00) >> 8);
                    length[3] = (byte) ((m_octets.Length & 0xFF));
                }
                    // 0xFFFFFF < length <= 0xFFFFFFFF
                else
                {
                    length = new byte[5];
                    length[0] = 0x84;
                    length[1] = (byte) ((m_octets.Length & 0xFF000000) >> 24);
                    length[2] = (byte) ((m_octets.Length & 0xFF0000) >> 16);
                    length[3] = (byte) ((m_octets.Length & 0xFF00) >> 8);
                    length[4] = (byte) ((m_octets.Length & 0xFF));
                }

                m_length = length;
            }

            private byte[] Concatenate(ICollection<byte[]> values)
            {
                // Nothing in, nothing out
                if (IsEmpty(values))
                {
                    return new byte[] {};
                }

                int length = 0;
                foreach (var b in values)
                {
                    if (null != b)
                    {
                        length += b.Length;
                    }
                }

                var cated = new byte[length];

                int current = 0;
                foreach (var b in values)
                {
                    if (null != b)
                    {
                        Array.Copy(b, 0, cated, current, b.Length);
                        current += b.Length;
                    }
                }

                return cated;
            }
        };
    }
}


text:
require 'deferrable_gratification'

describe DeferrableGratification::Primitives do
  describe '.success' do
    describe 'DG.success' do
      subject { DG.success }

      it { should succeed_with_anything }
    end

    describe 'DG.success(42)' do
      subject { DG.success(42) }

      it { should succeed_with 42 }
    end

    describe 'DG.success(:foo, :bar, :baz)' do
      subject { DG.success(:foo, :bar, :baz) }

      it { should succeed_with [:foo, :bar, :baz] }
    end
  end

  describe '.const' do
    describe 'DG.const("Hello")' do
      subject { DG.const("Hello") }

      it { should succeed_with('Hello') }
    end
  end

  describe '.failure' do
    describe 'DG.failure("does not compute")' do
      subject { DG.failure("does not compute") }

      it { should fail_with(RuntimeError, 'does not compute') }
    end

    describe 'DG.failure(ArgumentError)' do
      subject { DG.failure(ArgumentError) }

      it { should fail_with(ArgumentError) }
    end

    describe 'DG.failure(ArgumentError, "unacceptable command")' do
      subject { DG.failure(ArgumentError, "unacceptable command") }

      it { should fail_with(ArgumentError, 'unacceptable command') }
    end

    describe 'DG.failure(RangeError.new("you shall not pass!"))' do
      subject { DG.failure(RangeError.new("you shall not pass!")) }

      it { should fail_with(RangeError, 'you shall not pass!') }
    end
  end
end

redpajama_set_name: RedPajamaGithub


text:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
@include_once( APPPATH . 'controllers/seller/Admin_Controller.php');

class user_profile extends Admin_Controller {

	public function __construct() {
		parent::__construct();

		$this->load->model('seller/user_profile_model');

 	}

 	public function index()
	{

	   	$data['sellers_cat_display'] = $this->user_profile_model->seller_categories();
	   	//$data['seller_orders'] = $this->user_profile_model->total_orders();
	   //echo print_r($data);exit;
	   //echo '<pre>';print_r($some);exit;
		$this->template->write_view('content', 'seller/userprofile/index',$data);
		$this->template->render();

	}

 }


text:
/**
 * @fileoverview Implementation of a progress bar.
 *
 * @see ../demos/progressbar.html
 */

goog.provide('goog.ui.ProgressBar');
goog.provide('goog.ui.ProgressBar.Orientation');

goog.require('goog.a11y.aria');
goog.require('goog.asserts');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.dom.classlist');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.ui.Component');
goog.require('goog.ui.RangeModel');
goog.require('goog.userAgent');
goog.requireType('goog.events.Event');

/**
 * This creates a progress bar object.
 * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
 * @constructor
 * @extends {goog.ui.Component}
 */
goog.ui.ProgressBar = function(opt_domHelper) {
  'use strict';
  goog.ui.Component.call(this, opt_domHelper);

  /** @type {?HTMLDivElement} */
  this.thumbElement_;

  /**
   * The underlying data model for the progress bar.
   * @type {goog.ui.RangeModel}
   * @private
   */
  this.rangeModel_ = new goog.ui.RangeModel;
  goog.events.listen(
      this.rangeModel_, goog.ui.Component.EventType.CHANGE, this.handleChange_,
      false, this);
};
goog.inherits(goog.ui.ProgressBar, goog.ui.Component);

/**
 * Enum for representing the orientation of the progress bar.
 *
 * @enum {string}
 */
goog.ui.ProgressBar.Orientation = {
  VERTICAL: 'vertical',
  HORIZONTAL: 'horizontal'
};

/**
 * Map from progress bar orientation to CSS class names.
 * @type {!Object<string, string>}
 * @private
 */
goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_ = {};
goog.ui.ProgressBar
    .ORIENTATION_TO_CSS_NAME_[goog.ui.ProgressBar.Orientation.VERTICAL] =
    goog.getCssName('progress-bar-vertical');
goog.ui.ProgressBar
    .ORIENTATION_TO_CSS_NAME_[goog.ui.ProgressBar.Orientation.HORIZONTAL] =
    goog.getCssName('progress-bar-horizontal');

/**
 * Creates the DOM nodes needed for the progress bar
 * @override
 */
goog.ui.ProgressBar.prototype.createDom = function() {
  'use strict';
  this.thumbElement_ = this.createThumb_();
  this.setElementInternal(this.getDomHelper().createDom(
      goog.dom.TagName.DIV,
      goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[this.orientation_],
      this.thumbElement_));
  this.setValueState_();
  this.setMinimumState_();
  this.setMaximumState_();
};

/** @override */
goog.ui.ProgressBar.prototype.enterDocument = function() {
  'use strict';
  goog.ui.ProgressBar.superClass_.enterDocument.call(this);
  this.attachEvents_();
  this.updateUi_();

  var element = this.getElement();
  goog.asserts.assert(element, 'The progress bar DOM element cannot be null.');
  // state live = polite will notify the user of updates,
  // but will not interrupt ongoing feedback
  goog.a11y.aria.setRole(element, 'progressbar');
  goog.a11y.aria.setState(element, 'live', 'polite');
};

/** @override */
goog.ui.ProgressBar.prototype.exitDocument = function() {
  'use strict';
  goog.ui.ProgressBar.superClass_.exitDocument.call(this);
  this.detachEvents_();
};

/**
 * This creates the thumb element.
 * @private
 * @return {!HTMLDivElement} The created thumb element.
 */
goog.ui.ProgressBar.prototype.createThumb_ = function() {
  'use strict';
  return this.getDomHelper().createDom(
      goog.dom.TagName.DIV, goog.getCssName('progress-bar-thumb'));
};

/**
 * Adds the initial event listeners to the element.
 * @private
 * @suppress {strictPrimitiveOperators} Part of the go/strict_warnings_migration
 */
goog.ui.ProgressBar.prototype.attachEvents_ = function() {
  'use strict';
  if (goog.userAgent.IE && goog.userAgent.VERSION < 7) {
    goog.events.listen(
        this.getElement(), goog.events.EventType.RESIZE, this.updateUi_, false,
        this);
  }
};

/**
 * Removes the event listeners added by attachEvents_.
 * @private
 * @suppress {strictPrimitiveOperators} Part of the go/strict_warnings_migration
 */
goog.ui.ProgressBar.prototype.detachEvents_ = function() {
  'use strict';
  if (goog.userAgent.IE && goog.userAgent.VERSION < 7) {
    goog.events.unlisten(
        this.getElement(), goog.events.EventType.RESIZE, this.updateUi_, false,
        this);
  }
};

/**
 * Decorates an existing HTML DIV element as a progress bar input. If the
 * element contains a child with a class name of 'progress-bar-thumb' that will
 * be used as the thumb.
 * @param {Element} element  The HTML element to decorate.
 * @override
 */
goog.ui.ProgressBar.prototype.decorateInternal = function(element) {
  'use strict';
  goog.ui.ProgressBar.superClass_.decorateInternal.call(this, element);
  goog.dom.classlist.add(
      goog.asserts.assert(this.getElement()),
      goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[this.orientation_]);

  // find thumb
  var thumb = goog.dom.getElementsByTagNameAndClass(
      null, goog.getCssName('progress-bar-thumb'), this.getElement())[0];
  if (!thumb) {
    thumb = this.createThumb_();
    this.getElement().appendChild(/** @type {!Node} */ (thumb));
  }
  this.thumbElement_ = /** @type {!HTMLDivElement} */ (thumb);
};

/**
 * @return {number} The value.
 */
goog.ui.ProgressBar.prototype.getValue = function() {
  'use strict';
  return this.rangeModel_.getValue();
};

/**
 * Sets the value
 * @param {number} v The value.
 */
goog.ui.ProgressBar.prototype.setValue = function(v) {
  'use strict';
  this.rangeModel_.setValue(v);
  if (this.getElement()) {
    this.setValueState_();
  }
};

/**
 * Sets the state for a11y of the current value.
 * @private
 */
goog.ui.ProgressBar.prototype.setValueState_ = function() {
  'use strict';
  var element = this.getElement();
  goog.asserts.assert(element, 'The progress bar DOM element cannot be null.');
  goog.a11y.aria.setState(element, 'valuenow', this.getValue());
};

/**
 * @return {number} The minimum value.
 */
goog.ui.ProgressBar.prototype.getMinimum = function() {
  'use strict';
  return this.rangeModel_.getMinimum();
};

/**
 * Sets the minimum number
 * @param {number} v The minimum value.
 */
goog.ui.ProgressBar.prototype.setMinimum = function(v) {
  'use strict';
  this.rangeModel_.setMinimum(v);
  if (this.getElement()) {
    this.setMinimumState_();
  }
};

/**
 * Sets the state for a11y of the minimum value.
 * @private
 */
goog.ui.ProgressBar.prototype.setMinimumState_ = function() {
  'use strict';
  var element = this.getElement();
  goog.asserts.assert(element, 'The progress bar DOM element cannot be null.');
  goog.a11y.aria.setState(element, 'valuemin', this.getMinimum());
};

/**
 * @return {number} The maximum value.
 */
goog.ui.ProgressBar.prototype.getMaximum = function() {
  'use strict';
  return this.rangeModel_.getMaximum();
};

/**
 * Sets the maximum number
 * @param {number} v The maximum value.
 */
goog.ui.ProgressBar.prototype.setMaximum = function(v) {
  'use strict';
  this.rangeModel_.setMaximum(v);
  if (this.getElement()) {
    this.setMaximumState_();
  }
};

/**
 * Sets the state for a11y of the maximum valiue.
 * @private
 */
goog.ui.ProgressBar.prototype.setMaximumState_ = function() {
  'use strict';
  var element = this.getElement();
  goog.asserts.assert(element, 'The progress bar DOM element cannot be null.');
  goog.a11y.aria.setState(element, 'valuemax', this.getMaximum());
};

/**
 *
 * @type {goog.ui.ProgressBar.Orientation}
 * @private
 */
goog.ui.ProgressBar.prototype.orientation_ =
    goog.ui.ProgressBar.Orientation.HORIZONTAL;

/**
 * Call back when the internal range model changes
 * @param {goog.events.Event} e The event object.
 * @private
 */
goog.ui.ProgressBar.prototype.handleChange_ = function(e) {
  'use strict';
  this.updateUi_();
  this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
};

/**
 * This is called when we need to update the size of the thumb. This happens
 * when first created as well as when the value and the orientation changes.
 * @private
 * @suppress {strictPrimitiveOperators} Part of the go/strict_warnings_migration
 */
goog.ui.ProgressBar.prototype.updateUi_ = function() {
  'use strict';
  if (this.thumbElement_) {
    var min = this.getMinimum();
    var max = this.getMaximum();
    var val = this.getValue();
    var ratio = (val - min) / (max - min);
    var size = Math.round(ratio * 100);
    if (this.orientation_ == goog.ui.ProgressBar.Orientation.VERTICAL) {
      // Note(arv): IE up to version 6 has some serious computation bugs when
      // using percentages or bottom. We therefore first set the height to
      // 100% and measure that and base the top and height on that size instead.
      if (goog.userAgent.IE && goog.userAgent.VERSION < 7) {
        this.thumbElement_.style.top = '0';
        this.thumbElement_.style.height = '100%';
        var h = this.thumbElement_.offsetHeight;
        var bottom = Math.round(ratio * h);
        this.thumbElement_.style.top = h - bottom + 'px';
        this.thumbElement_.style.height = bottom + 'px';
      } else {
        this.thumbElement_.style.top = (100 - size) + '%';
        this.thumbElement_.style.height = size + '%';
      }
    } else {
      this.thumbElement_.style.width = size + '%';
    }
  }
};

/**
 * This is called when we need to setup the UI sizes and positions. This
 * happens when we create the element and when we change the orientation.
 * @private
 */
goog.ui.ProgressBar.prototype.initializeUi_ = function() {
  'use strict';
  var tStyle = this.thumbElement_.style;
  if (this.orientation_ == goog.ui.ProgressBar.Orientation.VERTICAL) {
    tStyle.left = '0';
    tStyle.width = '100%';
  } else {
    tStyle.top = tStyle.left = '0';
    tStyle.height = '100%';
  }
};

/**
 * Changes the orientation
 * @param {goog.ui.ProgressBar.Orientation} orient The orientation.
 */
goog.ui.ProgressBar.prototype.setOrientation = function(orient) {
  'use strict';
  if (this.orientation_ != orient) {
    var oldCss =
        goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[this.orientation_];
    var newCss = goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[orient];
    this.orientation_ = orient;

    // Update the DOM
    var element = this.getElement();
    if (element) {
      goog.dom.classlist.swap(element, oldCss, newCss);
      this.initializeUi_();
      this.updateUi_();
    }
  }
};

/**
 * @return {goog.ui.ProgressBar.Orientation} The orientation of the
 *     progress bar.
 */
goog.ui.ProgressBar.prototype.getOrientation = function() {
  'use strict';
  return this.orientation_;
};

/** @override */
goog.ui.ProgressBar.prototype.disposeInternal = function() {
  'use strict';
  this.detachEvents_();
  goog.ui.ProgressBar.superClass_.disposeInternal.call(this);
  this.thumbElement_ = null;
  this.rangeModel_.dispose();
};

/**
 * @return {?number} The step value used to determine how to round the value.
 */
goog.ui.ProgressBar.prototype.getStep = function() {
  'use strict';
  return this.rangeModel_.getStep();
};

/**
 * Sets the step value. The step value is used to determine how to round the
 * value.
 * @param {?number} step  The step size.
 */
goog.ui.ProgressBar.prototype.setStep = function(step) {
  'use strict';
  this.rangeModel_.setStep(step);
};


text:
@interface RDMSetupGroupsViewController_iPad ()

@property (nonatomic, strong) UIBarButtonItem *activityIndicatorBarButtonItem;

@property (nonatomic, strong) UIPopoverController *actionPopoverController;
@property (nonatomic, strong) RDMGroup *groupActionViewShownFor;

@property (nonatomic, strong) NSFetchedResultsController *groupsFetchedResultsController;
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
- (IBAction)refreshButtonPushed:(id)sender;
@property (strong, nonatomic) IBOutlet UIBarButtonItem *refreshButton;

@property (nonatomic, strong) id syncCompleteObserver;
@property (nonatomic, strong) id userDidChangeObserver;

@end

@implementation RDMSetupGroupsViewController_iPad {

    NSMutableArray *_objectChanges;
    NSMutableArray *_sectionChanges;

}

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view.

    _objectChanges = [NSMutableArray array];
    _sectionChanges = [NSMutableArray array];

    [self adjustLeftBarButtonItemAnimated:NO];

    if (RDM_IS_IOS7_BASED_ON_VIEW(self.view)) {
        self.view.tintColor = self.navigationController.navigationBar.barTintColor;
    }

    self.title = @"Groups";

    self.userDidChangeObserver = [[NSNotificationCenter defaultCenter] addObserverForName:RDMUserDidChangeNotification
                                                                                object:nil
                                                                                queue:[NSOperationQueue mainQueue]
                                                                               usingBlock:^(NSNotification *note) {

                                                                                self.groupsFetchedResultsController = nil;
                                                                                [self.collectionView reloadData];

                                                                                if (self.presentedViewController) {
                                                                                [self dismissViewControllerAnimated:YES completion:nil];
                                                                                }
                                                                               }];

    self.syncCompleteObserver = [[NSNotificationCenter defaultCenter] addObserverForName:RDMSyncEngineDidFinishNotification
                                                                                object:nil
                                                                                queue:nil
                                                                              usingBlock:^(NSNotification * note) {

                                                                                self.navigationItem.rightBarButtonItem = self.refreshButton;
                                                                                self.activityIndicatorBarButtonItem = nil;

                                                                              }];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDidChangeAlwaysShowMenuNotification:)
                                                 name:RDMRevealMenuAlwaysShowMenuDidChangeNotification object:self.rdm_rootViewController];
}

-(void) handleDidChangeAlwaysShowMenuNotification:(NSNotification*)note {

    [self adjustLeftBarButtonItemAnimated:YES];

}

-(void) adjustLeftBarButtonItemAnimated:(BOOL)animated {

    if ([self.rdm_rootViewController menuShouldAlwaysShow]) {
        [self.navigationItem setLeftBarButtonItem:nil animated:YES];
    } else {
        UIImage *image = [RDMRootViewController defaultImage];
        UIBarButtonItem *leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:image
                                                                              style:UIBarButtonItemStylePlain
                                                                             target:self
                                                                             action:@selector(menuBarButtonItemPushed:)];
        [self.navigationItem setLeftBarButtonItem:leftBarButtonItem animated:YES];
    }

}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void) dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:RDMRevealMenuAlwaysShowMenuDidChangeNotification object:self.rdm_rootViewController];
    [[NSNotificationCenter defaultCenter] removeObserver:self.userDidChangeObserver];
    [[NSNotificationCenter defaultCenter] removeObserver:self.syncCompleteObserver];
}

-(void) viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    [self.collectionView.collectionViewLayout invalidateLayout];
}

-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    if ([segue.identifier isEqualToString:@"editGroupSegueID"]) {

        UICollectionViewCell *cell = sender;
        NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
        RDMGroup *group = [self.groupsFetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForItem:indexPath.row - 1
                                                                                inSection:indexPath.section]];
        UINavigationController *navController = (UINavigationController*)segue.destinationViewController;
        RDMEditGroupViewController_iPad *vc = (RDMEditGroupViewController_iPad*)[navController topViewController];
        vc.dataController = self.dataController;
        vc.groupToEdit = group;

        if (RDM_IS_IOS7_BASED_ON_VIEW(self.view)) {
            navController.view.tintColor = [UIColor whiteColor];
        }

    } else if ([segue.identifier isEqualToString:@"groupCreateSegueID"]) {

        UINavigationController *navController = (UINavigationController*)segue.destinationViewController;
        RDMCreateGroupViewController *vc = (RDMCreateGroupViewController*)[navController topViewController];
        vc.dataController = self.dataController;

        if (RDM_IS_IOS7_BASED_ON_VIEW(self.view)) {
            navController.view.tintColor = [UIColor whiteColor];
        }
    }

}

-(void) menuBarButtonItemPushed:(id) sender {

    [self.rdm_rootViewController openMenuViewControllerAnimated:YES
                                                 withCompletion:nil];
}

-(NSFetchedResultsController*) groupsFetchedResultsController {

    if (_groupsFetchedResultsController) {
        return _groupsFetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"RDMGroup"];

    NSPredicate *userPredicate = [NSPredicate predicateWithFormat:@"user == %@", self.dataController.currentUser];
    NSPredicate *notDeletedPredicate = [NSPredicate predicateWithFormat:@"hasBeenDeleted == %@", @NO];
    NSPredicate *andPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[ userPredicate, notDeletedPredicate ] ];
    [fetchRequest setPredicate:andPredicate];

    NSSortDescriptor *sortOrderDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES selector:@selector(localizedStandardCompare:)];
    [fetchRequest setSortDescriptors:@[ sortOrderDescriptor] ];

    _groupsFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                           managedObjectContext:self.dataController.managedObjectContext
                                                                             sectionNameKeyPath:nil
                                                                                cacheName:nil];
    _groupsFetchedResultsController.delegate = self;

    NSError *error = nil;
    if (![_groupsFetchedResultsController performFetch:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return _groupsFetchedResultsController;

}

#pragma mark - Collection View Datasource

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView {
    // _data is a class member variable that contains one array per section.
    return [[self.groupsFetchedResultsController sections] count];
}

- (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section {
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.groupsFetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects] + 1;

}

-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    if (indexPath.row == 0) {
        RDMNewGroupCell *cell = (RDMNewGroupCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"RDMNewGroupCellID"
                                                                                forIndexPath:indexPath];
        return cell;
    }

    RDMSetupGroupCell *cell = (RDMSetupGroupCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"RDMSetupGroupCellID"
                                                                                forIndexPath:indexPath];

    [self configureCell:cell atIndexPath:indexPath];

    return cell;

}

-(void) configureCell:(RDMSetupGroupCell*)cell atIndexPath:(NSIndexPath*)indexPath {

    NSIndexPath *adjustIndexPath = [NSIndexPath indexPathForItem:indexPath.row - 1
                                                       inSection:indexPath.section];
    RDMGroup *group = [self.groupsFetchedResultsController objectAtIndexPath:adjustIndexPath];

    cell.groupNameLabel.text = group.name;

    NSUInteger numberOfDevices = [group.devices count];
    NSString *deviceText = numberOfDevices == 1 ? @"Device" : @"Devices";
    cell.numberOfDevicesLabel.text = [NSString stringWithFormat:@"%lu %@", (unsigned long)[group.devices count], deviceText];

}

#pragma mark - Collection View Flow Delegate

#define padding 30.0

-(CGSize) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

    int viewWidth = CGRectGetWidth(self.view.bounds);
    int availableViewWidth = viewWidth - 4 * padding;
    CGFloat rawWidth = (float)availableViewWidth/3.0;
    int roundedDownCellWidth = floor(rawWidth);
    int remainder = availableViewWidth % roundedDownCellWidth;
    int widthToReturn = roundedDownCellWidth;

    switch (indexPath.row) {
        case 0:
            if (remainder == 2) {
                widthToReturn = roundedDownCellWidth + 1;
            }
            break;
        case 1:

            if (remainder == 1) {
                widthToReturn = roundedDownCellWidth + 1;
            }

            break;
        case 2:

            if (remainder == 2) {
                widthToReturn = roundedDownCellWidth + 1;
            }
            break;
        default:
            break;
    }

    return CGSizeMake(widthToReturn, 140.0);

}

-(UIEdgeInsets) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(padding, padding, 0, padding);
}

-(CGFloat) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
    return padding;
}

-(CGFloat) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
    return padding;
}

#pragma mark - Collection View Delegate

-(void) collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {

    [collectionView deselectItemAtIndexPath:indexPath animated:YES];

    if (indexPath.row == 0) {
        [self performSegueWithIdentifier:@"groupCreateSegueID"
                                  sender:indexPath];
        return;
    }

    [self performSegueWithIdentifier:@"editGroupSegueID"
                              sender:[collectionView cellForItemAtIndexPath:indexPath]];

}

#pragma mark - Fetched Results Delegate

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {

}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath
{

    NSMutableDictionary *change = [NSMutableDictionary new];

    NSIndexPath *adjustedIndexPath = [NSIndexPath indexPathForItem:indexPath.row + 1 inSection:indexPath.section];
    NSIndexPath *adjustedNewIndexPath = [NSIndexPath indexPathForItem:newIndexPath.row + 1 inSection:newIndexPath.section];

    switch(type)
    {
        case NSFetchedResultsChangeInsert:
            change[@(type)] = adjustedNewIndexPath;
            break;
        case NSFetchedResultsChangeDelete:
            change[@(type)] = adjustedIndexPath;
            break;
        case NSFetchedResultsChangeUpdate:
            change[@(type)] = adjustedIndexPath;
            break;
        case NSFetchedResultsChangeMove:
            change[@(type)] = @[adjustedIndexPath, adjustedNewIndexPath];
            break;
    }
    [_objectChanges addObject:change];
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{

    if ([_sectionChanges count] > 0)
    {
        [self.collectionView performBatchUpdates:^{

            for (NSDictionary *change in _sectionChanges)
            {
                [change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) {

                    NSFetchedResultsChangeType type = [key unsignedIntegerValue];
                    switch (type)
                    {
                        case NSFetchedResultsChangeInsert:
                            [self.collectionView insertSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
                            break;
                        case NSFetchedResultsChangeDelete:
                            [self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
                            break;
                        case NSFetchedResultsChangeUpdate:
                            [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
                            break;
                    }
                }];
            }
        } completion:nil];
    }

    if ([_objectChanges count] > 0 && [_sectionChanges count] == 0)
    {

        if ([self shouldReloadCollectionViewToPreventKnownIssue] || self.collectionView.window == nil) {
            // This is to prevent a bug in UICollectionView from occurring.
            // The bug presents itself when inserting the first object or deleting the last object in a collection view.
            // http://stackoverflow.com/questions/12611292/uicollectionview-assertion-failure
            // This code should be removed once the bug has been fixed, it is tracked in OpenRadar
            // http://openradar.appspot.com/12954582
            [self.collectionView reloadData];

        } else {

            [self.collectionView performBatchUpdates:^{

                for (NSDictionary *change in _objectChanges)
                {
                    [change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) {

                        NSFetchedResultsChangeType type = [key unsignedIntegerValue];
                        switch (type)
                        {
                            case NSFetchedResultsChangeInsert:
                                [self.collectionView insertItemsAtIndexPaths:@[obj]];
                                break;
                            case NSFetchedResultsChangeDelete:
                                [self.collectionView deleteItemsAtIndexPaths:@[obj]];
                                break;
                            case NSFetchedResultsChangeUpdate:
                                [self.collectionView reloadItemsAtIndexPaths:@[obj]];
                                break;
                            case NSFetchedResultsChangeMove: {

                                RDMSetupGroupCell *cell = (RDMSetupGroupCell*)[self.collectionView cellForItemAtIndexPath:obj[0]];
                                [self configureCell:cell atIndexPath:obj[1]];
                                [self.collectionView moveItemAtIndexPath:obj[0] toIndexPath:obj[1]];
                            }
                                break;
                        }
                    }];
                }
            } completion:nil];
        }
    }

    [_sectionChanges removeAllObjects];
    [_objectChanges removeAllObjects];
}

- (BOOL)shouldReloadCollectionViewToPreventKnownIssue {
    __block BOOL shouldReload = NO;
    for (NSDictionary *change in _objectChanges) {
        [change enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            NSFetchedResultsChangeType type = [key unsignedIntegerValue];
            NSIndexPath *indexPath = obj;
            switch (type) {
                case NSFetchedResultsChangeInsert:
                    if ([self.collectionView numberOfItemsInSection:indexPath.section] == 0) {
                        shouldReload = YES;
                    } else {
                        shouldReload = NO;
                    }
                    break;
                case NSFetchedResultsChangeDelete:
                    if ([self.collectionView numberOfItemsInSection:indexPath.section] == 1) {
                        shouldReload = YES;
                    } else {
                        shouldReload = NO;
                    }
                    break;
                case NSFetchedResultsChangeUpdate:
                    shouldReload = NO;
                    break;
                case NSFetchedResultsChangeMove:
                    shouldReload = NO;
                    break;
            }
        }];
    }

    return shouldReload;
}

- (IBAction)refreshButtonPushed:(id)sender {
    [[RDMSyncEngine sharedEngine] startSync];

    UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    self.activityIndicatorBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:indicatorView];

    self.navigationItem.rightBarButtonItem = self.activityIndicatorBarButtonItem;

    [indicatorView startAnimating];
}
@end