/*
 * 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.commons.concurrent.processor;

import java.math.BigInteger;

import com.scytl.cryptolib.api.exceptions.CryptoLibException;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.elgamal.cryptoapi.Ciphertext;
import com.scytl.cryptolib.mathematical.groups.impl.Exponent;
import com.scytl.ov.mixing.commons.concurrent.ParallelProcessAction;
import com.scytl.ov.mixing.commons.proofs.bg.commitments.PrivateCommitment;
import com.scytl.ov.mixing.commons.tools.LoopTools.Range;

/**
 * Base case to be parallelized using {@code LoopParallelizerAction}
 */
public class NextCiphertextsAndCAPrimeProcessor implements ParallelProcessAction {

    private final Ciphertext[][] nextCiphertexts;

    private final PrivateCommitment[] cAprime;

    private final Ciphertext[][] ciphertexts;

    private final int mu;

    private final PrivateCommitment[] cA;

    private final Exponent challengeX;

    private final BigInteger order;

    private final int commitmentLength;

    public NextCiphertextsAndCAPrimeProcessor(final Ciphertext[][] nextCiphertexts, final PrivateCommitment[] cAprime,
            final Ciphertext[][] ciphertexts, final int mu, final PrivateCommitment[] cA, final Exponent challengeX,
            final BigInteger order, final int commitmentLength) {

        this.nextCiphertexts = nextCiphertexts;
        this.cAprime = cAprime;
        this.ciphertexts = ciphertexts;
        this.mu = mu;
        this.cA = cA;
        this.challengeX = challengeX;
        this.order = order;
        this.commitmentLength = commitmentLength;
    }

    /**
     * @see com.scytl.products.ov.mixnet.commons.concurrent.ParallelProcessAction#run(com.scytl.products.ov.mixnet.commons.tools.LoopTools.Range)
     */
    @Override
    public void run(final Range range) {

        try {
            Exponent one = new Exponent(order, BigInteger.ONE);
            for (int l = range.getStart(); l < range.getEnd(); l++) {
                Exponent xacum = one;

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

                    // compute Cl' component by component (ciphertexts)
                    for (int k = 0; k < commitmentLength; k++) {

                        final Ciphertext aux = ciphertexts[mu * l + mu - 1 - i][k].exponentiate(xacum);
                        nextCiphertexts[l][k] = i == 0 ? aux : nextCiphertexts[l][k].multiply(aux);
                    }

                    // compute ca'l (commitments, implicitly computes al'
                    // and rl'
                    final PrivateCommitment aux = cA[mu * l + i].exponentiate(xacum);

                    cAprime[l] = i == 0 ? aux : cAprime[l].multiply(aux);

                    xacum = xacum.multiply(challengeX);
                }
            }

        } catch (GeneralCryptoLibException e) {
            throw new CryptoLibException(e);
        }
    }
}
