/*
 * 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.channel.cc.commons.exponentiation;

import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.math.BigInteger;
import java.util.List;

import org.junit.Before;
import org.junit.Test;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.api.proofs.ProofsServiceAPI;
import com.scytl.cryptolib.elgamal.bean.ElGamalComputationsValues;
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.cryptolib.proofs.cryptoapi.ProofVerifierAPI;
import com.scytl.cryptolib.proofs.proof.Proof;
import com.scytl.cryptolib.proofs.service.ProofsService;

/**
 * Tests of {@link ExponentiationServiceImpl}.
 */
public class ExponentiationServiceImplTest {
    private ProofsServiceAPI proofsService;

    private ZpSubgroup group;

    private Exponent exponent;

    private ZpGroupElement element1;

    private ZpGroupElement element2;

    private ZpGroupElement element3;

    private ExponentiationServiceImpl exponentiationService;

    @Before
    public void setUp() throws GeneralCryptoLibException {
        proofsService = new ProofsService();
        group = new ZpSubgroup(BigInteger.valueOf(2),
            BigInteger.valueOf(23), BigInteger.valueOf(11));
        exponent = new Exponent(group.getQ(), BigInteger.TEN);
        element1 = new ZpGroupElement(BigInteger.valueOf(3), group);
        element2 = new ZpGroupElement(BigInteger.valueOf(4), group);
        element3 = new ZpGroupElement(BigInteger.valueOf(6), group);
        exponentiationService =
            new ExponentiationServiceImpl(proofsService);
    }

    @Test
    public void testExponentiateCiphertexts()
            throws GeneralCryptoLibException {
        ElGamalComputationsValues ciphertext1 =
            new ElGamalComputationsValues(asList(element1, element2));
        ElGamalComputationsValues ciphertext2 =
            new ElGamalComputationsValues(asList(element1, element3));
        List<ElGamalComputationsValues> ciphertexts =
            asList(ciphertext1, ciphertext2);

        PowersAndProof<ElGamalComputationsValues> powersAndProof =
            exponentiationService.exponentiateCiphertexts(ciphertexts,
                exponent, group);

        assertEquals(2, powersAndProof.powers().size());

        ElGamalComputationsValues power1 = powersAndProof.powers().get(0);
        assertEquals(BigInteger.valueOf(8), power1.getGamma().getValue());
        assertEquals(BigInteger.valueOf(6),
            power1.getPhis().get(0).getValue());

        ElGamalComputationsValues power2 = powersAndProof.powers().get(1);
        assertEquals(BigInteger.valueOf(8), power2.getGamma().getValue());
        assertEquals(BigInteger.valueOf(4),
            power2.getPhis().get(0).getValue());

        List<ZpGroupElement> exponentiatedElements =
            asList(group.getGenerator().exponentiate(exponent),
                power1.getGamma(), power1.getPhis().get(0),
                power2.getGamma(), power2.getPhis().get(0));
        List<ZpGroupElement> baseElements = asList(group.getGenerator(),
            element1, element2, element1, element3);
        Proof proof = powersAndProof.proof();
        ProofVerifierAPI verifier =
            proofsService.createProofVerifierAPI(group);
        assertTrue(verifier.verifyExponentiationProof(
            exponentiatedElements, baseElements, proof));
    }

    @Test
    public void testExponentiateCleartexts()
            throws GeneralCryptoLibException {
        List<BigInteger> cleartexts = asList(element1.getValue(),
            element2.getValue(), element3.getValue());

        PowersAndProof<BigInteger> powersAndProof = exponentiationService
            .exponentiateCleartexts(cleartexts, exponent, group);

        List<BigInteger> powers = powersAndProof.powers();
        assertEquals(3, powers.size());
        assertEquals(BigInteger.valueOf(8), powers.get(0));
        assertEquals(BigInteger.valueOf(6), powers.get(1));
        assertEquals(BigInteger.valueOf(4), powers.get(2));

        List<ZpGroupElement> exponentiatedElements =
            asList(group.getGenerator().exponentiate(exponent),
                new ZpGroupElement(BigInteger.valueOf(8), group),
                new ZpGroupElement(BigInteger.valueOf(6), group),
                new ZpGroupElement(BigInteger.valueOf(4), group));
        List<ZpGroupElement> baseElements =
            asList(group.getGenerator(), element1, element2, element3);
        Proof proof = powersAndProof.proof();
        ProofVerifierAPI verifier =
            proofsService.createProofVerifierAPI(group);
        assertTrue(verifier.verifyExponentiationProof(
            exponentiatedElements, baseElements, proof));
    }
}
