/*
 * 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.ArrayList;
import java.util.List;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.elgamal.bean.ElGamalPublicKey;
import com.scytl.cryptolib.mathematical.groups.GroupElement;
import com.scytl.cryptolib.mathematical.groups.MathematicalGroup;
import com.scytl.cryptolib.primitives.messagedigest.configuration.ConfigMessageDigestAlgorithmAndProvider;
import com.scytl.cryptolib.proofs.bean.ProofPreComputedValues;
import com.scytl.cryptolib.proofs.maurer.configuration.ConfigProofHashCharset;
import com.scytl.cryptolib.proofs.maurer.function.PhiFunctionSimplePlaintextEquality;
import com.scytl.cryptolib.securerandom.cryptoapi.CryptoAPIRandomInteger;

/**
 * Simple plaintext equality proof pre-computer.
 * 
 * @param <T>
 *            the type which parameterizes the group elements of the
 *            {@link MathematicalGroup} that will be used during this exchange.
 * @param <E>
 *            the type of the group elements of the {@link MathematicalGroup}
 *            that will be used during this exchange. E must be a type which
 *            extends GroupElement{@code <T>}.
 */
public class SimplePlaintextEqualityProofPreComputer<T, E extends GroupElement<T>> {

    private final ElGamalPublicKey _primaryPublicKey;

    private final ElGamalPublicKey _secondaryPublicKey;

    private final MathematicalGroup<E> _group;

    private final ConfigMessageDigestAlgorithmAndProvider _configMessageDigestAlgorithmAndProvider;

    private final ConfigProofHashCharset _configProofHashCharset;

    private final PhiFunctionSimplePlaintextEquality<T> _phiFunctionSimplePlaintextEquality;

    /**
     * Default constructor.
     * 
     * @param primaryPublicKey
     *            the primary ElGamal public key.
     * @param secondaryPublicKey
     *            the secondary ElGamal public key.
     * @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.
     * @throws GeneralCryptoLibException
     *             if the phi function cannot be created.
     */
    SimplePlaintextEqualityProofPreComputer(
            final ElGamalPublicKey primaryPublicKey,
            final ElGamalPublicKey secondaryPublicKey,
            final MathematicalGroup<E> group,
            final ConfigMessageDigestAlgorithmAndProvider configMessageDigestAlgorithmAndProvider,
            final ConfigProofHashCharset configProofHashCharset)
            throws GeneralCryptoLibException {

        _primaryPublicKey = primaryPublicKey;
        _secondaryPublicKey = secondaryPublicKey;
        _group = group;

        _configMessageDigestAlgorithmAndProvider =
            configMessageDigestAlgorithmAndProvider;
        _configProofHashCharset = configProofHashCharset;

        _phiFunctionSimplePlaintextEquality =
            new PhiFunctionSimplePlaintextEquality<>(_group,
                buildListOfBaseElements());
    }

    /**
     * Pre-computes the values needed for proof generation.
     * 
     * @param cryptoRandomInteger
     *            the random number generator used to create the proof secrets.
     * @return the pre-computed values.
     * @throws GeneralCryptoLibException
     *             if an error occurs during the pre-computation process.
     */
    public ProofPreComputedValues preCompute(
            final CryptoAPIRandomInteger cryptoRandomInteger)
            throws GeneralCryptoLibException {

        Prover<T, E> prover =
            new Prover<>(_group, _phiFunctionSimplePlaintextEquality,
                _configMessageDigestAlgorithmAndProvider,
                _configProofHashCharset);

        return prover.preCompute(cryptoRandomInteger);
    }

    @SuppressWarnings("unchecked")
    private List<E> buildListOfBaseElements() {

        List<E> invertedSecondaryKeyValues =
            buildSecondaryInvertedPublicKey();

        List<E> baseElements = new ArrayList<>();
        baseElements.add(_group.getGenerator());
        baseElements.addAll((List<? extends E>) _primaryPublicKey
            .getKeys());
        baseElements.addAll(invertedSecondaryKeyValues);

        return baseElements;
    }

    @SuppressWarnings("unchecked")
    private List<E> buildSecondaryInvertedPublicKey() {

        List<E> listInvertedElements = new ArrayList<>();
        List<? extends E> originalList =
            (List<? extends E>) _secondaryPublicKey.getKeys();
        for (E e : originalList) {
            listInvertedElements.add((E) e.invert());
        }

        return listInvertedElements;
    }
}
