/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.cryptolib.certificates.service;

import java.security.PrivateKey;

import com.scytl.cryptolib.api.certificates.CertificatesServiceAPI;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.certificates.bean.CertificateData;
import com.scytl.cryptolib.certificates.bean.RootCertificateData;
import com.scytl.cryptolib.certificates.bean.X509CertificateType;
import com.scytl.cryptolib.certificates.configuration.X509CertificateGeneratorPolicyFromProperties;
import com.scytl.cryptolib.certificates.cryptoapi.CryptoAPIX509Certificate;
import com.scytl.cryptolib.certificates.factory.CryptoX509CertificateGenerator;
import com.scytl.cryptolib.certificates.factory.X509CertificateGeneratorFactory;
import com.scytl.cryptolib.commons.constants.Constants;
import com.scytl.cryptolib.commons.utils.validations.Validate;

/**
 * Class which implements {@link CertificatesServiceAPI}.
 * <p>
 * Instances of this class are immutable.
 */
public class CertificatesService implements CertificatesServiceAPI {

    private final CryptoX509CertificateGenerator _x509certificateGenerator;

    /**
     * Default constructor which initializes all properties to default values.
     * These default values are obtained from the path indicated by
     * {@link Constants#CRYPTOLIB_POLICY_PROPERTIES_FILE_PATH}.
     * 
     * @throws GeneralCryptoLibException
     *             if the default properties are invalid.
     */
    public CertificatesService() throws GeneralCryptoLibException {

        this(Constants.CRYPTOLIB_POLICY_PROPERTIES_FILE_PATH);
    }

    /**
     * Constructor which initializes its internal state using the properties
     * file located at the specified path.
     * 
     * @param path
     *            the path of the properties file to be used to configure the
     *            service.
     * @throws GeneralCryptoLibException
     *             if the path is invalid or the properties are invalid.
     */
    public CertificatesService(final String path)
            throws GeneralCryptoLibException {

        Validate.notNullOrBlank(path, "Properties file path");

        X509CertificateGeneratorFactory x509CertificateGeneratorFactory =
            new X509CertificateGeneratorFactory(
                new X509CertificateGeneratorPolicyFromProperties(path));

        _x509certificateGenerator =
            x509CertificateGeneratorFactory.create();
    }

    @Override
    public CryptoAPIX509Certificate createRootAuthorityX509Certificate(
            final RootCertificateData rootCertificateData,
            final PrivateKey rootPrivateKey)
            throws GeneralCryptoLibException {

        validate(rootCertificateData, rootPrivateKey);

        return _x509certificateGenerator.generate(rootCertificateData,
            X509CertificateType.CERTIFICATE_AUTHORITY.getExtensions(),
            rootPrivateKey);
    }

    @Override
    public CryptoAPIX509Certificate createIntermediateAuthorityX509Certificate(
            final CertificateData certificateData,
            final PrivateKey issuerPrivateKey)
            throws GeneralCryptoLibException {

        validate(certificateData, issuerPrivateKey);

        return _x509certificateGenerator.generate(certificateData,
            X509CertificateType.CERTIFICATE_AUTHORITY.getExtensions(),
            issuerPrivateKey);
    }

    @Override
    public CryptoAPIX509Certificate createSignX509Certificate(
            final CertificateData certificateData,
            final PrivateKey issuerPrivateKey)
            throws GeneralCryptoLibException {

        validate(certificateData, issuerPrivateKey);

        return _x509certificateGenerator.generate(certificateData,
            X509CertificateType.SIGN.getExtensions(), issuerPrivateKey);
    }

    @Override
    public CryptoAPIX509Certificate createEncryptionX509Certificate(
            final CertificateData certificateData,
            final PrivateKey issuerPrivateKey)
            throws GeneralCryptoLibException {

        validate(certificateData, issuerPrivateKey);

        return _x509certificateGenerator.generate(certificateData,
            X509CertificateType.ENCRYPT.getExtensions(), issuerPrivateKey);
    }

    private void validate(final RootCertificateData certificateData,
            final PrivateKey privateKey) throws GeneralCryptoLibException {

        Validate.notNull(certificateData, "Certificate data");
        Validate.notNull(certificateData.getIssuerDn(),
            "Issuer distinguished name");
        Validate.notNull(certificateData.getSubjectDn(),
            "Subject distinguished name");
        Validate.notNull(certificateData.getSubjectPublicKey(),
            "Subject public key");
        Validate.notNull(certificateData.getValidityDates(),
            "Validity dates object");
        Validate.notNull(privateKey, "Issuer private key");
        Validate.notNullOrEmpty(privateKey.getEncoded(),
            "Issuer private key content");
    }
}
