package org.bouncycastle.crypto.util;

import java.io.IOException;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;

/**
 * Builder and holder class for preparing SP 800-56A compliant OtherInfo. The data is ultimately encoded as a DER SEQUENCE.
 * Empty octet strings are used to represent nulls in compulsory fields.
 */
public class DEROtherInfo
{
    /**
     * Builder to create OtherInfo
     */
    public static final class Builder
    {
        private final AlgorithmIdentifier algorithmID;
        private final ASN1OctetString partyUInfo;
        private final ASN1OctetString partyVInfo;

        private ASN1TaggedObject suppPubInfo;
        private ASN1TaggedObject suppPrivInfo;

        /**
         * Create a basic builder with just the compulsory fields.
         *
         * @param algorithmID the algorithm associated with this invocation of the KDF.
         * @param partyUInfo  sender party info.
         * @param partyVInfo  receiver party info.
         */
        public Builder(AlgorithmIdentifier algorithmID, byte[] partyUInfo, byte[] partyVInfo)
        {
            this.algorithmID = algorithmID;
            this.partyUInfo = DerUtil.getOctetString(partyUInfo);
            this.partyVInfo = DerUtil.getOctetString(partyVInfo);
        }

        /**
         * Add optional supplementary public info (DER tagged, implicit, 0).
         *
         * @param suppPubInfo supplementary public info.
         * @return  the current builder instance.
         */
        public Builder withSuppPubInfo(byte[] suppPubInfo)
        {
            this.suppPubInfo = new DERTaggedObject(false, 0, DerUtil.getOctetString(suppPubInfo));

            return this;
        }

        /**
         * Add optional supplementary private info (DER tagged, implicit, 1).
         *
         * @param suppPrivInfo supplementary private info.
         * @return the current builder instance.
         */
        public Builder withSuppPrivInfo(byte[] suppPrivInfo)
        {
            this.suppPrivInfo = new DERTaggedObject(false, 1, DerUtil.getOctetString(suppPrivInfo));

            return this;
        }

        /**
         * Build the KTSOtherInfo.
         *
         * @return an KTSOtherInfo containing the data.
         */
        public DEROtherInfo build()
        {
            ASN1EncodableVector v = new ASN1EncodableVector();

            v.add(algorithmID);
            v.add(partyUInfo);
            v.add(partyVInfo);

            if (suppPubInfo != null)
            {
                v.add(suppPubInfo);
            }

            if (suppPrivInfo != null)
            {
                v.add(suppPrivInfo);
            }

            return new DEROtherInfo(new DERSequence(v));
        }
    }

    private final DERSequence sequence;

    private DEROtherInfo(DERSequence sequence)
    {
        this.sequence = sequence;
    }

    public byte[] getEncoded()
        throws IOException
    {
        return sequence.getEncoded();
    }
}
