using System;

using Org.BouncyCastle.Asn1;

namespace Org.BouncyCastle.Asn1.Cms
{
    public class RecipientInfo
        : Asn1Encodable, IAsn1Choice
    {
        internal Asn1Encodable info;

		public RecipientInfo(
            KeyTransRecipientInfo info)
        {
            this.info = info;
        }

		public RecipientInfo(
            KeyAgreeRecipientInfo info)
        {
            this.info = new DerTaggedObject(false, 1, info);
        }

		public RecipientInfo(
            KekRecipientInfo info)
        {
            this.info = new DerTaggedObject(false, 2, info);
        }

		public RecipientInfo(
            PasswordRecipientInfo info)
        {
            this.info = new DerTaggedObject(false, 3, info);
        }

		public RecipientInfo(
            OtherRecipientInfo info)
        {
            this.info = new DerTaggedObject(false, 4, info);
        }

		public RecipientInfo(
            Asn1Object   info)
        {
            this.info = info;
        }

		public static RecipientInfo GetInstance(
            object o)
        {
            if (o == null || o is RecipientInfo)
                return (RecipientInfo) o;

			if (o is Asn1Sequence)
                return new RecipientInfo((Asn1Sequence) o);

			if (o is Asn1TaggedObject)
                return new RecipientInfo((Asn1TaggedObject) o);

			throw new ArgumentException("unknown object in factory: " + o.GetType().Name);
        }

		public DerInteger Version
        {
			get
			{
				if (info is Asn1TaggedObject)
				{
					Asn1TaggedObject o = (Asn1TaggedObject) info;

					switch (o.TagNo)
					{
						case 1:
							return KeyAgreeRecipientInfo.GetInstance(o, false).Version;
						case 2:
							return GetKekInfo(o).Version;
						case 3:
							return PasswordRecipientInfo.GetInstance(o, false).Version;
						case 4:
							return new DerInteger(0);    // no syntax version for OtherRecipientInfo
						default:
							throw new InvalidOperationException("unknown tag");
					}
				}

				return KeyTransRecipientInfo.GetInstance(info).Version;
			}
        }

		public bool IsTagged
		{
			get { return info is Asn1TaggedObject; }
		}

		public Asn1Encodable Info
        {
			get
			{
				if (info is Asn1TaggedObject)
				{
					Asn1TaggedObject o = (Asn1TaggedObject) info;

					switch (o.TagNo)
					{
						case 1:
							return KeyAgreeRecipientInfo.GetInstance(o, false);
						case 2:
							return GetKekInfo(o);
						case 3:
							return PasswordRecipientInfo.GetInstance(o, false);
						case 4:
							return OtherRecipientInfo.GetInstance(o, false);
						default:
							throw new InvalidOperationException("unknown tag");
					}
				}

				return KeyTransRecipientInfo.GetInstance(info);
			}
        }

		private KekRecipientInfo GetKekInfo(
			Asn1TaggedObject o)
		{
			// For compatibility with erroneous version, we don't always pass 'false' here
			return KekRecipientInfo.GetInstance(o, o.IsExplicit());
		}

		/**
         * Produce an object suitable for an Asn1OutputStream.
         * <pre>
         * RecipientInfo ::= CHOICE {
         *     ktri KeyTransRecipientInfo,
         *     kari [1] KeyAgreeRecipientInfo,
         *     kekri [2] KekRecipientInfo,
         *     pwri [3] PasswordRecipientInfo,
         *     ori [4] OtherRecipientInfo }
         * </pre>
         */
        public override Asn1Object ToAsn1Object()
        {
            return info.ToAsn1Object();
        }
    }
}
