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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * Class which generates the first answer.
 */
public class SecondCommitmentGenerator extends BaseCommitmentGenerator {

    private static final Logger LOG = LoggerFactory.getLogger("std");

    private final int m;

    private final int n;

    public SecondCommitmentGenerator(final ProofsGeneratorConfigurationParams config, final CommitmentParams comParams,
            final PrivateAndPublicCommitmentsGenerator privateAndPublicCommitmentsGenerator,
            final MultiExponentiation limMultiExpo) {

        super(config.getMatrixDimensions().getNumberOfRows(), config.getZpGroupParams().getQ(), comParams,
            privateAndPublicCommitmentsGenerator, limMultiExpo, config.getConcurrencyLevel());
        this.m = config.getMatrixDimensions().getNumberOfRows();
        this.n = config.getMatrixDimensions().getNumberOfColumns();
    }

    @Override
    protected Exponent[][] initExponents(final StateHarvester harvester) {

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

        final Exponent[][] aExponents = harvester.offer(BaseCommitmentGenerator.Produced.EXPONENTS, Exponent[][].class);

        final Exponent challengeX = randomOracle.getHash();

        final int power = m * n;

        final Exponent[] vecX = MathUtils.productsSequence(challengeX, power);

        return computeB(vecX, aExponents, m, n);
    }

    private static Exponent[][] computeB(final Exponent[] vecX, final Exponent[][] aExponents, final int m, final int n) {

        final Exponent[][] b = new Exponent[m][n];

        for (int i = 0; i < m; i++) {

            for (int j = 0; j < n; j++) {
                final int k = aExponents[i][j].getValue().intValue();
                b[i][j] = vecX[k - 1];
            }
        }

        return b;
    }

    @Override
    protected void publishCommitments(final PrivateCommitment[] privateCommitments,
            final PublicCommitment[] publicCommitments, final StateHarvester harvester) {

        LOG.info("Publishing second commitment");

        harvester.collect(Produced.PUBLIC_COMMITS, publicCommitments);
        harvester.collect(Produced.PRIVATE_COMMITS, privateCommitments);
    }

    public enum Produced implements StateLabel {
        PUBLIC_COMMITS, PRIVATE_COMMITS
    }

}
