/*
 * 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.cryptolib.proofs.maurer.factory;

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

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.elgamal.bean.CiphertextImpl;
import com.scytl.cryptolib.elgamal.bean.ElGamalPublicKey;
import com.scytl.cryptolib.elgamal.bean.WitnessImpl;
import com.scytl.cryptolib.elgamal.cryptoapi.Ciphertext;
import com.scytl.cryptolib.elgamal.cryptoapi.Witness;
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.mathematical.groups.utils.MathematicalTestDataGenerator;
import com.scytl.cryptolib.primitives.messagedigest.configuration.ConfigMessageDigestAlgorithmAndProvider;
import com.scytl.cryptolib.proofs.bean.ProofPreComputedValues;
import com.scytl.cryptolib.proofs.maurer.configuration.ConfigProofHashCharset;
import com.scytl.cryptolib.proofs.proof.Proof;
import com.scytl.cryptolib.securerandom.cryptoapi.CryptoAPIRandomInteger;

/**
 * Tests of {@link ORProofGenerator}.
 */
public class ORProofGeneratorTest {
    private static final String DATA = "data";

    private static final int INDEX = 1;

    private static final BigInteger Z0 = BigInteger.ONE;

    private static final BigInteger Z1 = BigInteger.valueOf(2);

    private static final BigInteger Z2 = BigInteger.valueOf(3);

    private static final BigInteger C0 = BigInteger.valueOf(4);

    private static final BigInteger C1 = BigInteger.valueOf(5);

    private static final BigInteger C2 = BigInteger.valueOf(6);

    private ZpSubgroup group;

    private ElGamalPublicKey publicKey;

    private List<ZpGroupElement> elements;

    private Ciphertext ciphertext;

    private Witness witness;

    private CryptoAPIRandomInteger random;

    private ProofPreComputedValues preComputedValues;

    @Before
    public void setUp() throws GeneralCryptoLibException {
        group = MathematicalTestDataGenerator.getZpSubgroup();

        Exponent privateKey =
            new Exponent(group.getQ(), BigInteger.valueOf(2));
        List<ZpGroupElement> keys =
            singletonList(group.getGenerator().exponentiate(privateKey));
        publicKey = new ElGamalPublicKey(keys, group);

        elements = asList(group.getGenerator(),
            group.getGenerator().multiply(group.getGenerator()),
            group.getGenerator().multiply(group.getGenerator())
                .multiply(group.getGenerator()));

        Exponent a = new Exponent(group.getQ(), BigInteger.valueOf(3));
        
        ciphertext = new CiphertextImpl(
            group.getGenerator().exponentiate(a), publicKey.getKeys()
                .get(0).exponentiate(a).multiply(elements.get(INDEX)));

        witness = new WitnessImpl(a);

        random = mock(CryptoAPIRandomInteger.class);
        when(random.nextRandomByBits(anyInt())).thenReturn(Z0, Z1, Z2, C0,
            C1, C2);

        preComputedValues =
            new ORProofPreComputer(publicKey, elements.size())
                .preCompute(random);
    }

    @Test
    public void testGenerate() throws GeneralCryptoLibException {
        ORProofGenerator generator = new ORProofGenerator(ciphertext,
            publicKey, witness, INDEX, elements, DATA, random,
            ConfigMessageDigestAlgorithmAndProvider.SHA256_SUN,
            ConfigProofHashCharset.UTF8, preComputedValues);

        Proof proof = generator.generate();
        Exponent hash = proof.getHashValue();
        List<Exponent> exponents = proof.getValuesList();

        assertEquals(elements.size() * 2, exponents.size());
        assertEquals(hash,
            exponents.get(0).add(exponents.get(1)).add(exponents.get(2)));
        assertEquals(
            new Exponent(group.getQ(), Z1)
                .add(witness.getExponent().multiply(exponents.get(1))),
            exponents.get(4));
    }
}
