/*
 * 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.products.ov.mixnet.proofs.bg.shuffle;

import java.math.BigInteger;

import com.scytl.cryptolib.mathematical.groups.impl.Exponent;
import com.scytl.products.ov.mixnet.commons.proofs.bg.commitments.CommitmentParams;
import com.scytl.products.ov.mixnet.commons.proofs.bg.commitments.PrivateCommitment;
import com.scytl.products.ov.mixnet.commons.proofs.bg.commitments.PublicCommitment;
import com.scytl.products.ov.mixnet.commons.tools.ExponentTools;
import com.scytl.products.ov.mixnet.commons.tools.MultiExponentiation;
import com.scytl.products.ov.mixnet.commons.tools.RandomOracleHash;
import com.scytl.products.ov.mixnet.proofs.bg.state.StateHarvester;
import com.scytl.products.ov.mixnet.proofs.bg.state.StateLabel;

/**
 * Class which defines the core behaviour of any specific commitment generator.
 */
public abstract class BaseCommitmentGenerator {

    private final int m;

    private final BigInteger order;

    private final CommitmentParams comParams;

    private final PrivateAndPublicCommitmentsGenerator privateAndPublicCommitmentsGenerator;

    private final MultiExponentiation limMultiExpo;

    private final int concurrencyLevel;

    public BaseCommitmentGenerator(final int m, final BigInteger order, final CommitmentParams comParams,
            final PrivateAndPublicCommitmentsGenerator privateAndPublicCommitmentsGenerator,
            final MultiExponentiation limMultiExpo, final int concurrencyLevel) {

        super();
        this.comParams = comParams;
        this.m = m;
        this.order = order;
        this.privateAndPublicCommitmentsGenerator = privateAndPublicCommitmentsGenerator;
        this.limMultiExpo = limMultiExpo;
        this.concurrencyLevel = concurrencyLevel;
    }

    /**
     * Generates {@code m} {@link PrivateCommitment} and {@link PublicCommitment}, and allows a {@link StateHarvester}
     * to collect them.
     *
     * @param harvester
     */
    public void generate(final StateHarvester harvester) {

        final Exponent[][] exponents = initExponents(harvester);

        harvester.collect(Produced.EXPONENTS, exponents);

        final Exponent[] r = ExponentTools.getVectorRandomExponent(m, order);

        final PrivateCommitment[] privateCommitments = new PrivateCommitment[m];
        final PublicCommitment[] publicCommitments = new PublicCommitment[m];

        privateAndPublicCommitmentsGenerator.generateAndSet(exponents, r, m, comParams, privateCommitments,
            publicCommitments, limMultiExpo, concurrencyLevel);

        final RandomOracleHash randomOracle =
            harvester.offer(ShuffleProofGenerator.Produced.RANDOM_ORACLE, RandomOracleHash.class);

        randomOracle.addDataToRO(publicCommitments);

        publishCommitments(privateCommitments, publicCommitments, harvester);
    }

    /**
     * Provides the commitments to the {@link StateHarvester}, so that it can collect them.
     *
     * @param privateCommitments
     * @param publicCommitments
     * @param harvester
     */
    protected abstract void publishCommitments(final PrivateCommitment[] privateCommitments,
            final PublicCommitment[] publicCommitments, final StateHarvester harvester);

    /**
     * Provides a matrix of exponents.
     *
     * @param harvester
     * @return
     * @throws StateRetrievalException
     */
    protected abstract Exponent[][] initExponents(final StateHarvester harvester);

    public enum Produced implements StateLabel {
        EXPONENTS
    }

}
