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

import java.util.List;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.commons.utils.validations.Validate;
import com.scytl.cryptolib.elgamal.bean.ElGamalPublicKey;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup;
import com.scytl.cryptolib.primitives.messagedigest.configuration.ConfigMessageDigestAlgorithmAndProvider;
import com.scytl.cryptolib.primitives.securerandom.factory.CryptoRandomInteger;
import com.scytl.cryptolib.proofs.bean.ProofPreComputedValues;
import com.scytl.cryptolib.proofs.cryptoapi.ProofPreComputerAPI;
import com.scytl.cryptolib.proofs.maurer.configuration.ConfigProofHashCharset;

/**
 * Implementation of {@link ProofPreComputerAPI}, which allows for the
 * pre-computation of values needed to generate various types of zero knowledge
 * proofs of knowledge.
 */
public class ZpSubgroupProofPreComputer implements ProofPreComputerAPI {

    private final ZpSubgroup _zpSubgroup;

    private final ConfigMessageDigestAlgorithmAndProvider _configMessageDigestAlgorithmAndProvider;

    private final ConfigProofHashCharset _configProofHashCharset;

    private final CryptoRandomInteger _cryptoRandomInteger;

    /**
     * Default constructor.
     * 
     * @param group
     *            the Zp subgroup used for ElGamal encryption.
     * @param configMessageDigestAlgorithmAndProvider
     *            the algorithm and provider of the message digest used to
     *            generate the proof.
     * @param configProofHashCharset
     *            the character set of the hash used to generate the proof.
     * @param cryptoRandomInteger
     *            the random number generator used to generate the proof.
     */
    ZpSubgroupProofPreComputer(final ZpSubgroup group,
            final ConfigMessageDigestAlgorithmAndProvider configMessageDigestAlgorithmAndProvider,
            final ConfigProofHashCharset configProofHashCharset,
            final CryptoRandomInteger cryptoRandomInteger) {

        _zpSubgroup = group;
        _configMessageDigestAlgorithmAndProvider =
            configMessageDigestAlgorithmAndProvider;
        _configProofHashCharset = configProofHashCharset;
        _cryptoRandomInteger = cryptoRandomInteger;
    }

    @Override
    public ProofPreComputedValues preComputeSchnorrProof()
            throws GeneralCryptoLibException {

        return new SchnorrProofPreComputer<>(_zpSubgroup,
            _configMessageDigestAlgorithmAndProvider,
            _configProofHashCharset).preCompute(_cryptoRandomInteger);
    }

    @Override
    public ProofPreComputedValues preComputeExponentiationProof(
            final List<ZpGroupElement> baseElements)
            throws GeneralCryptoLibException {

        Validate.notNullOrEmptyAndNoNulls(baseElements,
            "List of base elements");

        return new ExponentiationProofPreComputer<>(baseElements,
            _zpSubgroup, _configMessageDigestAlgorithmAndProvider,
            _configProofHashCharset).preCompute(_cryptoRandomInteger);
    }

    @Override
    public ProofPreComputedValues preComputeDecryptionProof(
            final int privateKeyLength, final ZpGroupElement gamma)
            throws GeneralCryptoLibException {

        Validate.isPositive(privateKeyLength,
            "Length of ElGamal private key");
        Validate.notNull(gamma, "Ciphertext gamma element");

        return new DecryptionProofPreComputer<>(privateKeyLength, gamma,
            _zpSubgroup, _configMessageDigestAlgorithmAndProvider,
            _configProofHashCharset).preCompute(_cryptoRandomInteger);
    }

    @Override
    public ProofPreComputedValues preComputePlaintextProof(
            final ElGamalPublicKey publicKey)
            throws GeneralCryptoLibException {

        Validate.notNull(publicKey, "ElGamal public key");

        return new PlaintextProofPreComputer<>(publicKey, _zpSubgroup,
            _configMessageDigestAlgorithmAndProvider,
            _configProofHashCharset).preCompute(_cryptoRandomInteger);
    }

    @Override
    public ProofPreComputedValues preComputeSimplePlaintextEqualityProof(
            final ElGamalPublicKey primaryPublicKey,
            final ElGamalPublicKey secondaryPublicKey)
            throws GeneralCryptoLibException {

        Validate.notNull(primaryPublicKey, "Primary ElGamal public key");
        Validate.notNull(secondaryPublicKey,
            "Secondary ElGamal public key");
        Validate.equals(primaryPublicKey.getKeys().size(),
            secondaryPublicKey.getKeys().size(),
            "Primary ElGamal public key length",
            "secondary ElGamal public key length");

        return new SimplePlaintextEqualityProofPreComputer<>(
            primaryPublicKey, secondaryPublicKey, _zpSubgroup,
            _configMessageDigestAlgorithmAndProvider,
            _configProofHashCharset).preCompute(_cryptoRandomInteger);
    }

    @Override
    public ProofPreComputedValues preComputePlaintextEqualityProof(
            final ElGamalPublicKey primaryPublicKey,
            final ElGamalPublicKey secondaryPublicKey)
            throws GeneralCryptoLibException {

        Validate.notNull(primaryPublicKey, "Primary ElGamal public key");
        Validate.notNull(secondaryPublicKey,
            "Secondary ElGamal public key");
        Validate.equals(primaryPublicKey.getKeys().size(),
            secondaryPublicKey.getKeys().size(),
            "Primary ElGamal public key length",
            "secondary ElGamal public key length");

        return new PlaintextEqualityProofPreComputer<>(primaryPublicKey,
            secondaryPublicKey, _zpSubgroup,
            _configMessageDigestAlgorithmAndProvider,
            _configProofHashCharset).preCompute(_cryptoRandomInteger);
    }

    @Override
    public ProofPreComputedValues preComputePlaintextExponentEqualityProof(
            final List<ZpGroupElement> baseElements)
            throws GeneralCryptoLibException {

        Validate.notNullOrEmptyAndNoNulls(baseElements,
            "List of base elements");

        return new PlaintextExponentEqualityProofPreComputer<>(
            baseElements, _zpSubgroup,
            _configMessageDigestAlgorithmAndProvider,
            _configProofHashCharset).preCompute(_cryptoRandomInteger);
    }

    @Override
    public ProofPreComputedValues preComputeORProof(
            ElGamalPublicKey publicKey, int elementCount)
            throws GeneralCryptoLibException {
        Validate.notNull(publicKey, "ElGamal public key");
        Validate.isPositive(elementCount, "Element count");
        Validate.equals(publicKey.getGroup(), _zpSubgroup, "key group",
            "prover's group");
        return new ORProofPreComputer(publicKey, elementCount)
            .preCompute(_cryptoRandomInteger);
    }
}
