/*
 * 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.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.products.ov.mixnet.commons.concurrent.ParallelProcessAction;
import com.scytl.products.ov.mixnet.commons.tools.CiphertextTools;
import com.scytl.products.ov.mixnet.commons.tools.ExponentTools;
import com.scytl.products.ov.mixnet.commons.tools.LoopTools.Range;

public class EvaluatorProcessor implements ParallelProcessAction {

    private final Ciphertext[] evalC;

    private final int m;

    private final int n;

    private final Exponent[][] a;

    private final Ciphertext[][] vecC;

    private final BigInteger order;

    private final CiphertextTools t;

    private final Exponent[][] vandermondeOmega;

    public EvaluatorProcessor(final Ciphertext[] evalC, final int m, final int n, final Exponent[][] a,
            final Ciphertext[][] vecC, final BigInteger order, final CiphertextTools t,
            final Exponent[][] vandermondeOmega) {

        this.evalC = evalC;
        this.m = m;
        this.n = n;
        this.a = a;
        this.vecC = vecC;
        this.order = order;
        this.t = t;
        this.vandermondeOmega = vandermondeOmega;
    }

    @Override
    public void run(final Range range) {

        Exponent[] exp = ExponentTools.get0Vector(n, order);
        final int rangeStart = range.getStart();

        for (int j = 0; j < m; j++) {
            exp = ExponentTools.addTwoVectors(exp,
                ExponentTools.multiplyByScalar(a[j], vandermondeOmega[rangeStart][j]));
        }

        final Exponent firstVandermondeOmegaExponent = vandermondeOmega[rangeStart][0];
        final Ciphertext[] base = new Ciphertext[n];

        for (int i = -1; ++i < n;) {

            try {
                base[i] = vecC[m - 1][i].exponentiate(firstVandermondeOmegaExponent);
            } catch (GeneralCryptoLibException e) {
                throw new CryptoLibException(e);
            }
        }

        // now compute the base
        for (int i = m - 1; i > 0; i--) {

            final Exponent vandermondeOmegaExponent = vandermondeOmega[rangeStart][m - i];

            for (int j = 0; j < n; j++) {

                try {
                    final Ciphertext aux = vecC[i - 1][j].exponentiate(vandermondeOmegaExponent);
                    base[j] = base[j].multiply(aux);
                } catch (GeneralCryptoLibException e) {
                    throw new CryptoLibException(e);
                }
            }
        }

        // now compute the result
        evalC[rangeStart] = t.compVecCiphVecExp(base, exp);
    }

}
