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 is SignedData)
            {
                return (SignedData) obj;
            }

			if (obj is Asn1Sequence)
            {
                return new SignedData((Asn1Sequence) obj);
            }

			throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "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);
        }
    }
}
