using System;
using System.Collections;

using Org.BouncyCastle.Asn1;

namespace Org.BouncyCastle.Asn1.Pkcs
{
    /**
     * a Pkcs#7 signed data object.
     */
    public class SignedData
        : Asn1Encodable
    {
        private readonly DerInteger		version;
        private readonly Asn1Set		digestAlgorithms;
        private readonly ContentInfo	contentInfo;
        private readonly Asn1Set		certificates;
        private readonly Asn1Set		crls;
        private readonly Asn1Set		signerInfos;

        public static SignedData GetInstance(object obj)
        {
            if (obj == null)
                return null;
            SignedData existing = obj as SignedData;
            if (existing != null)
                return existing;
            return new SignedData(Asn1Sequence.GetInstance(obj));
        }

        public SignedData(
            DerInteger        _version,
            Asn1Set           _digestAlgorithms,
            ContentInfo       _contentInfo,
            Asn1Set           _certificates,
            Asn1Set           _crls,
            Asn1Set           _signerInfos)
        {
            version          = _version;
            digestAlgorithms = _digestAlgorithms;
            contentInfo      = _contentInfo;
            certificates     = _certificates;
            crls             = _crls;
            signerInfos      = _signerInfos;
        }

        private SignedData(
            Asn1Sequence seq)
        {
            IEnumerator e = seq.GetEnumerator();

            e.MoveNext();
            version = (DerInteger) e.Current;

            e.MoveNext();
            digestAlgorithms = (Asn1Set) e.Current;

            e.MoveNext();
            contentInfo = ContentInfo.GetInstance(e.Current);

            while (e.MoveNext())
            {
                Asn1Object o = (Asn1Object) e.Current;

                //
                // an interesting feature of SignedData is that there appear to be varying implementations...
                // for the moment we ignore anything which doesn't fit.
                //
                if (o is DerTaggedObject)
                {
                    DerTaggedObject tagged = (DerTaggedObject) o;

                    switch (tagged.TagNo)
                    {
                        case 0:
                            certificates = Asn1Set.GetInstance(tagged, false);
                            break;
                        case 1:
                            crls = Asn1Set.GetInstance(tagged, false);
                            break;
                        default:
                            throw new ArgumentException("unknown tag value " + tagged.TagNo);
                    }
                }
                else
                {
                    signerInfos = (Asn1Set) o;
                }
            }
        }

        public DerInteger Version
        {
            get { return version; }
        }

        public Asn1Set DigestAlgorithms
        {
            get { return digestAlgorithms; }
        }

        public ContentInfo ContentInfo
        {
            get { return contentInfo; }
        }

        public Asn1Set Certificates
        {
            get { return certificates; }
        }

        public Asn1Set Crls
        {
            get { return crls; }
        }

        public Asn1Set SignerInfos
        {
            get { return signerInfos; }
        }

        /**
         * Produce an object suitable for an Asn1OutputStream.
         * <pre>
         *  SignedData ::= Sequence {
         *      version Version,
         *      digestAlgorithms DigestAlgorithmIdentifiers,
         *      contentInfo ContentInfo,
         *      certificates
         *          [0] IMPLICIT ExtendedCertificatesAndCertificates
         *                   OPTIONAL,
         *      crls
         *          [1] IMPLICIT CertificateRevocationLists OPTIONAL,
         *      signerInfos SignerInfos }
         * </pre>
         */
        public override Asn1Object ToAsn1Object()
        {
            Asn1EncodableVector v = new Asn1EncodableVector(
                version, digestAlgorithms, contentInfo);

            if (certificates != null)
            {
                v.Add(new DerTaggedObject(false, 0, certificates));
            }

            if (crls != null)
            {
                v.Add(new DerTaggedObject(false, 1, crls));
            }

            v.Add(signerInfos);

            return new BerSequence(v);
        }
    }
}
