/*
 * 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.elgamal.factory;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.elgamal.bean.ElGamalEncryptionParameters;
import com.scytl.cryptolib.elgamal.bean.ElGamalPrivateKey;
import com.scytl.cryptolib.elgamal.bean.ElGamalPublicKey;
import com.scytl.cryptolib.elgamal.configuration.ConfigGroupType;
import com.scytl.cryptolib.elgamal.configuration.ElGamalPolicy;
import com.scytl.cryptolib.elgamal.configuration.Group;
import com.scytl.cryptolib.elgamal.encrytionparams.ElGamalEncryptionParamsGenerator;
import com.scytl.cryptolib.elgamal.encrytionparams.QuadResParamsGenerator;
import com.scytl.cryptolib.elgamal.encrytionparams.SubgroupParamsGenerator;
import com.scytl.cryptolib.primitives.securerandom.factory.CryptoRandomBytes;
import com.scytl.cryptolib.primitives.securerandom.factory.CryptoRandomInteger;
import com.scytl.cryptolib.primitives.securerandom.factory.SecureRandomFactory;

/**
 * Factory class for generators, encryptor and decryptor.
 */
public final class ElGamalFactory {

    private static final int CERTAINTY_LEVEL = 100;

    private final CryptoRandomInteger _cryptoRandomInteger;

    private final CryptoRandomBytes _cryptoRandomBytes;

    private final ConfigGroupType _configGroupType;

    /**
     * Constructs a CryptoElGamalFactory.
     * 
     * @param elGamalPolicy
     *            the policy to set in this factory.
     */
    public ElGamalFactory(final ElGamalPolicy elGamalPolicy) {

        _cryptoRandomInteger =
                new SecureRandomFactory(elGamalPolicy).createIntegerRandom();

        _cryptoRandomBytes =
                new SecureRandomFactory(elGamalPolicy).createByteRandom();

        _configGroupType = elGamalPolicy.getGroupType();
    }

    /**
     * Creates a {@link CryptoElGamalKeyPairGenerator} which can be used to
     * generate a {@link com.scytl.cryptolib.elgamal.bean.ElGamalKeyPair}.
     * <P>
     * Both keys of the pair (the public and the private keys) making up a
     * CryptoElGamalKeyPair will themselves be composed of a number of parts.
     *
     * @return a new CryptoElGamalKeyPairGenerator.
     */
    public CryptoElGamalKeyPairGenerator createCryptoElGamalKeyPairGenerator() {

        return new CryptoElGamalKeyPairGenerator(_cryptoRandomInteger);
    }

    /**
     * Creates a {@link CryptoElGamalEncrypter}.
     * 
     * @param elGamalPublicKey
     *            the public key that should be set in the created
     *            CryptoElGamalEncrypter.
     * @return a new {@link CryptoElGamalEncrypter}.
     * @throws GeneralCryptoLibException
     *             if not all of the public keys belong to the same group.
     */
    public CryptoElGamalEncrypter createEncrypter(
            final ElGamalPublicKey elGamalPublicKey)
            throws GeneralCryptoLibException {

        return new CryptoElGamalEncrypter(elGamalPublicKey,
            _cryptoRandomInteger, _configGroupType);
    }

    /**
     * Creates a {@link CryptoElGamalDecrypter}.
     * 
     * @param elGamalPrivateKey
     *            the private keys that should be set in the created
     *            CryptoElGamalDecrypter.
     * @return a new CryptoElGamalDecrypter.
     */
    public CryptoElGamalDecrypter createDecrypter(
            final ElGamalPrivateKey elGamalPrivateKey) {

        return new CryptoElGamalDecrypter(elGamalPrivateKey);
    }

    /**
     * Creates a {@link CryptoElGamalReEncrypter}.
     * 
     * @param elGamalPublicKey
     *            the public keys that should be set in the created
     *            CryptoElGamalReEncrypter.
     * @return a new CryptoElGamalReEncrypter.
     * @throws GeneralCryptoLibException
     *             if not all of the public keys belong to the same group.
     */
    public CryptoElGamalReEncrypter createReEncrypter(
            final ElGamalPublicKey elGamalPublicKey)
            throws GeneralCryptoLibException {

        return new CryptoElGamalReEncrypter(elGamalPublicKey,
            _cryptoRandomInteger);
    }

    /**
     * Creates an {@link ElGamalEncryptionParamsGenerator} that can be used
     * for generating {@link ElGamalEncryptionParameters}.
     *
     * @return a generator of {@link ElGamalEncryptionParameters}.
     */
    public SubgroupParamsGenerator createElGamalEncryptionParametersGenerator() {

        if (_configGroupType.getGroup() == Group.QR) {
            return new QuadResParamsGenerator(
                    _configGroupType.getL(), CERTAINTY_LEVEL,
                    _cryptoRandomInteger);
        } else {
            return new ElGamalEncryptionParamsGenerator(
                    _configGroupType.getL(),
                    _configGroupType.getN(), CERTAINTY_LEVEL,
                    _cryptoRandomBytes);
        }
    }
}
