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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.scytl.cryptolib.api.exceptions.CryptoLibException;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.elgamal.bean.CiphertextImpl;
import com.scytl.cryptolib.elgamal.cryptoapi.Ciphertext;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup;
import com.scytl.products.ov.mixnet.commons.concurrent.ParallelProcessTask;
import com.scytl.products.ov.mixnet.commons.homomorphic.Cryptosystem;
import com.scytl.products.ov.mixnet.commons.homomorphic.Randomness;
import com.scytl.products.ov.mixnet.commons.homomorphic.impl.GjosteenElGamalPlaintext;
import com.scytl.products.ov.mixnet.commons.homomorphic.impl.GjosteenElGamalRandomness;
import com.scytl.products.ov.mixnet.commons.tools.LoopTools.Range;

/**
 * Base case to be parallelized using {@code LoopParallelizerTask}
 */
public class EncryptConcurrentCalculatorProcessor implements ParallelProcessTask<List<Ciphertext>> {

    private final Randomness[] randomExponents;

    private final List<Ciphertext> inputEncryptedBallots;

    private final Cryptosystem cryptoSystem;

    private final ZpSubgroup group;

    public EncryptConcurrentCalculatorProcessor(final Randomness[] randomExponents,
            final List<Ciphertext> inputEncryptedBallots, final Cryptosystem cryptoSystem, final ZpSubgroup group) {

        this.randomExponents = randomExponents;
        this.inputEncryptedBallots = inputEncryptedBallots;
        this.cryptoSystem = cryptoSystem;
        this.group = group;
    }

    @Override
    public List<Ciphertext> run(final Range range) throws GeneralCryptoLibException {

        List<Ciphertext> reEncryptedBallots = new ArrayList<>();

        for (int i = range.getStart(), rangeEnd = range.getEnd(); i < rangeEnd; i++) {

            final Ciphertext encryptedBallot = inputEncryptedBallots.get(i);

            final ZpGroupElement[] arrayOfIdentityElement = getArrayOfIdentityElement(encryptedBallot.getPhis().size());

            final Ciphertext arrayOfIdentityElementEncrypted =
                cryptoSystem.encrypt(new GjosteenElGamalPlaintext(arrayOfIdentityElement),
                    new GjosteenElGamalRandomness(randomExponents[i].getExponent()));

            Ciphertext multiplicationResult;
            try {
                multiplicationResult = encryptedBallot.multiply(arrayOfIdentityElementEncrypted);
            } catch (GeneralCryptoLibException e) {
                throw new CryptoLibException(e);
            }

            final CiphertextImpl multiplicationResultAsEncryptedBallot =
                new CiphertextImpl(multiplicationResult.getGamma(), multiplicationResult.getPhis());

            reEncryptedBallots.add(multiplicationResultAsEncryptedBallot);
        }

        return reEncryptedBallots;

    }

    private ZpGroupElement[] getArrayOfIdentityElement(final int numRequiredElements) {

        final ZpGroupElement identityElement = group.getIdentity();
        final ZpGroupElement[] arrayOfIdentityElement = new ZpGroupElement[numRequiredElements];
        Arrays.fill(arrayOfIdentityElement, identityElement);
        return arrayOfIdentityElement;
    }
}
