/*
 * 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.impl.Exponent;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup;
import com.scytl.cryptolib.mathematical.groups.utils.GroupUtils;
import com.scytl.cryptolib.proofs.bean.ProofPreComputedValues;
import com.scytl.cryptolib.securerandom.cryptoapi.CryptoAPIRandomInteger;

/**
 * OR proof pre-computer.
 * <p>
 * Instances of this class are not reusable;
 */
public final class ORProofPreComputer {
    private final ElGamalPublicKey publicKey;

    private final int elementCount;

    /**
     * Constructor.
     * 
     * @param publicKey
     *            the public key
     * @param elementCount
     *            the element count
     */
    public ORProofPreComputer(ElGamalPublicKey publicKey,
            int elementCount) {
        this.publicKey = publicKey;
        this.elementCount = elementCount;
    }

    /**
     * Pre-computes the values needed for proof generation. The returned values
     * contains {@code elementCount} exponents {@code (z1,...,zk)} and
     * {@code elementCount * 2} exponents {@code (g^z1, h^z1,...,g^zk,h^zk)}
     * where {@code g, h} are the group generator and the public key
     * correspondingly.
     * 
     * @param random
     *            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(CryptoAPIRandomInteger random)
            throws GeneralCryptoLibException {
        List<Exponent> exponents = generateExponents(random);
        List<ZpGroupElement> phiOutputs = generatePhiOutputs(exponents);
        return new ProofPreComputedValues(exponents, phiOutputs);
    }

    private List<Exponent> generateExponents(CryptoAPIRandomInteger random)
            throws GeneralCryptoLibException {
        List<Exponent> exponents = new ArrayList<>(elementCount);
        ZpSubgroup group = publicKey.getGroup();
        GroupUtils utils = new GroupUtils();
        for (int i = 0; i < elementCount; i++) {
            exponents.add(utils.getRandomExponent(group, random));
        }
        return exponents;
    }

    private List<ZpGroupElement> generatePhiOutputs(
            List<Exponent> exponents) throws GeneralCryptoLibException {
        List<ZpGroupElement> outputs =
            new ArrayList<>(exponents.size() * 2);
        ZpGroupElement g = publicKey.getGroup().getGenerator();
        ZpGroupElement h = publicKey.getKeys().get(0);
        for (Exponent exponent : exponents) {
            outputs.add(g.exponentiate(exponent));
            outputs.add(h.exponentiate(exponent));
        }
        return outputs;
    }
}
