/*
 * 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.tools;

import static java.util.Arrays.asList;

import java.math.BigInteger;

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.Exponent;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup;
import com.scytl.math.BigIntegers;

/**
 *
 */
public class CiphertextTools {

    private final MultiExponentiation multiExponentiation;

    public CiphertextTools(final MultiExponentiation limMultiExpo) {
        this.multiExponentiation = limMultiExpo;
    }

    public CiphertextImpl compVecCiphVecExp(final Ciphertext[] c, final Exponent[] e) {

        try {

            ZpGroupElement x = c[0].getGamma();
            BigInteger p = x.getP();
            BigInteger q = x.getQ();

            // here we set a 2 as the generator, but it is not actually used for this calculation
            ZpSubgroup zp = new ZpSubgroup(new BigInteger("2"), p, q);

            ZpGroupElement[][] bases = new ZpGroupElement[c[0].getPhis().size() + 1][c.length];

            for (int i = 0, length = c.length; i < length; i++) {
                ZpGroupElement[] a = c[i].getElements().toArray(new ZpGroupElement[c[i].getElements().size()]);
                for (int j = 0; j < a.length; j++) {
                    bases[j][i] = a[j];
                }
            }

            ZpGroupElement[] b = new ZpGroupElement[c[0].getPhis().size() + 1];
            for (int j = 0; j < b.length; j++) {
                b[j] = multiExponentiation.computeMultiExpo(zp, bases[j], e);
            }

            return new CiphertextImpl(b[0], asList(b).subList(1, b.length));

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

    public static boolean isValidCiphertext(final Ciphertext ciphertext) {
        for (ZpGroupElement element : ciphertext.getElements()) {
            if (!isGroupMember(element)) {
                return false;
            }
        }
        return true;
    }

    private static boolean isGroupMember(final ZpGroupElement element) {
        BigInteger p = element.getP();
        BigInteger q = element.getQ();
        BigInteger modPow = BigIntegers.modPow(element.getValue(), q, p);
        return BigInteger.ONE.equals(modPow);
    }
}
