package com.sunyard.chsm.utils;

import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.EncryptedContentInfo;
import org.bouncycastle.asn1.cms.OriginatorInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cms.CMSException;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;


public class SydCmsUtil {

    public static Object readContentInfo(
            byte[] input) {
        ASN1InputStream is = new ASN1InputStream(input);
        Object v = null;
        try {
            v = is.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return v;
    }


    public static Map<String, Object> getEncryptedContentInfo(ASN1Encodable    obj){
        Map<String, Object>map = new HashMap<String, Object>();
        ASN1Sequence seq = ASN1Sequence.getInstance(obj );
        if (seq.size() < 2)
        {
            throw new IllegalArgumentException("Truncated Sequence Found");
        }

        ASN1ObjectIdentifier contentType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
        map.put( "contentType", contentType );

        AlgorithmIdentifier contentEncryptionAlgorithm = AlgorithmIdentifier.getInstance(
                seq.getObjectAt(1));

        map.put( "contentEncryptionAlgorithm", contentEncryptionAlgorithm );

        if (seq.size() > 2)
        {
            ASN1OctetString encryptedContent = ASN1OctetString.getInstance(
                    (ASN1TaggedObject)seq.getObjectAt(2), false);
            map.put( "encryptedContent", encryptedContent );
        }
        return map;
    }

    public static Map<String, Object> getOriginatorInfo(ASN1TaggedObject    obj,
                                                        boolean             explicit){
        Map<String, Object>map = new HashMap<String, Object>();
        ASN1Sequence seq = ASN1Sequence.getInstance(obj, explicit);
        switch (seq.size())
        {
            case 0:     // empty
                break;
            case 1:
                ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(0);
                switch (o.getTagNo())
                {
                    case 0 :
                        ASN1Set certs = ASN1Set.getInstance(o, false);
                        map.put("certs", certs);
                        break;
                    case 1 :
                        ASN1Set crls = ASN1Set.getInstance(o, false);
                        map.put("crls", crls);
                        break;
                    default:
                        throw new IllegalArgumentException("Bad tag in OriginatorInfo: " + o.getTagNo());
                }
                break;
            case 2:
                ASN1Set certs = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(0), false);
                map.put("certs", certs);
                ASN1Set crls  = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(1), false);
                map.put("crls", crls);
                break;
            default:
                throw new IllegalArgumentException("OriginatorInfo too big");
        }

        return map;
    }

//    public static ContentInfo getContentInfo( byte[] input ) {
//        Object content = readContentInfo( input );
//        ASN1Sequence seq = ASN1Sequence.getInstance( content );
//        return new ContentInfo(seq);
//
//    }

    public static Map<String, Object> readCmsInfo( byte[] input ) throws CMSException {
        Map<String, Object>map = new HashMap<String, Object>();

        Object content = readContentInfo( input );
        ASN1Sequence seq = ASN1Sequence.getInstance( content );
        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) seq.getObjectAt(0);
        map.put("oid", oid);

        seq = ASN1Sequence.getInstance((ASN1TaggedObject) seq.getObjectAt(1), true);

        int     index = 0;

        ASN1Integer version = (ASN1Integer)seq.getObjectAt(index++);
        map.put( "version", version );

        Object  tmp = seq.getObjectAt(index++);

        if (tmp instanceof ASN1TaggedObject)
        {
            OriginatorInfo originatorInfo = OriginatorInfo.getInstance((ASN1TaggedObject)tmp, false);
//            Map<String, Object>originatorInfo = getOriginatorInfo((ASN1TaggedObject)tmp, false);
            map.put( "originatorInfo", originatorInfo );
            tmp = seq.getObjectAt(index++);
        }

        ASN1Set recipientInfos = ASN1Set.getInstance(tmp);
        map.put( "recipientInfos", recipientInfos );

        EncryptedContentInfo encryptedContentInfo = EncryptedContentInfo.getInstance( seq.getObjectAt(index++) );
//        Map<String, Object> encryptedContentInfo = getEncryptedContentInfo(seq.getObjectAt(index++));
        map.put( "encryptedContentInfo", encryptedContentInfo );

        if(seq.size() > index)
        {
            ASN1Set unprotectedAttrs = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(index), false);
            map.put( "unprotectedAttrs", unprotectedAttrs );
        }

        return map;
    }

}
