package com.sunyard.chsm.utils;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.cms.EncryptedContentInfo;

import java.util.Enumeration;

/**
 * <pre>
 * SignedAndEnvelopedData ::=SEQUENCE {
 *             version                      Version,
 *             recipientInfos               RecipientInfos,
 *             digestAlgorithms             DigestAlgorithmIdentifiers
 *             encryptedContentInfo         EncryptedContentInfo,
 *             certificates    [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
 *             crls            [1] IMPLICIT CertificateRevocationLists OPTIONAL,
 *             signerInfos                  SignerInfos
 *     }
 * </pre>
 *
 * @author liulu
 * @since 2024/12/25
 */
@Getter
@AllArgsConstructor
public class SignedAndEnvelopedData extends ASN1Object {

    private ASN1Integer version;
    private ASN1Set recipientInfos;
    private ASN1Set digestAlgorithms;
    private EncryptedContentInfo encryptedContentInfo;
    private ASN1Set certificates;
    private ASN1Set crls;
    private ASN1Set signerInfos;


    public static SignedAndEnvelopedData getInstance(
            Object obj) {
        if (obj instanceof SignedAndEnvelopedData) {
            return (SignedAndEnvelopedData) obj;
        }

        if (obj != null) {
            return new SignedAndEnvelopedData(ASN1Sequence.getInstance(obj));
        }

        return null;
    }

    private SignedAndEnvelopedData(ASN1Sequence seq) {
        Enumeration e = seq.getObjects();

        version = ASN1Integer.getInstance(e.nextElement());
        recipientInfos = ((ASN1Set) e.nextElement());
        digestAlgorithms = ((ASN1Set) e.nextElement());
        encryptedContentInfo = EncryptedContentInfo.getInstance(e.nextElement());

        ASN1Set sigInfs = null;
        while (e.hasMoreElements()) {
            ASN1Primitive o = (ASN1Primitive) e.nextElement();
            if (o instanceof ASN1TaggedObject) {
                ASN1TaggedObject tagged = (ASN1TaggedObject) o;
                switch (tagged.getTagNo()) {
                    case 0:
                        certificates = ASN1Set.getInstance(tagged, false);
                        break;
                    case 1:
                        crls = ASN1Set.getInstance(tagged, false);
                        break;
                    default:
                        throw new IllegalArgumentException("unknown tag value " + tagged.getTagNo());
                }
            } else {
                if (!(o instanceof ASN1Set)) {
                    throw new IllegalArgumentException("SET expected, not encountered");
                }
                sigInfs = (ASN1Set) o;
            }
        }

        if (sigInfs == null) {
            throw new IllegalArgumentException("signerInfos not set");
        }
        signerInfos = sigInfs;
    }


    @Override
    public ASN1Primitive toASN1Primitive() {
        ASN1EncodableVector v = new ASN1EncodableVector(7);
        v.add(version);
        v.add(recipientInfos);
        v.add(digestAlgorithms);
        v.add(encryptedContentInfo);
        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 DLSequence(v);
    }
}
