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

import static junitparams.JUnitParamsRunner.$;

import java.security.Security;
import java.util.ArrayList;
import java.util.List;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.AfterClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;

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.elgamal.cryptoapi.Witness;
import com.scytl.cryptolib.elgamal.utils.ElGamalTestDataGenerator;
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.proofs.bean.ProofPreComputedValues;
import com.scytl.cryptolib.proofs.proof.Proof;
import com.scytl.cryptolib.proofs.utils.ProofsTestDataGenerator;

import junitparams.JUnitParamsRunner;
import junitparams.Parameters;

/**
 * Tests of Proofs service API input validation for plaintext exponent equality
 * zero knowledge proofs of knowledge.
 */
@RunWith(JUnitParamsRunner.class)
public class PlaintextExponentEqualityProofValidationTest {

    private static final int NUM_BASE_ELEMENTS = 7;

    private static ProofsService _proofsServiceForDefaultPolicy;

    private static ZpSubgroup _zpSubgroup;

    private static Witness _witness1;

    private static Witness _witness2;

    private static List<ZpGroupElement> _baseElements;

    private static Ciphertext _ciphertext;

    private static Proof _proof;

    private static List<ZpGroupElement> _emptyBaseElementList;

    private static List<ZpGroupElement> _baseElementListWithNullValue;

    private static List<ZpGroupElement> _shorterBaseElementList;

    private static int _shorterBaseElementListLength;

    private static int _expectedBaseElementListLength;

    static {
        try {
            setUp();
        } catch (GeneralCryptoLibException e) {
            throw new CryptoLibException("Setup failed for class "
                + PlaintextExponentEqualityProofValidationTest.class
                    .getSimpleName());
        }
    }

    public static void setUp() throws GeneralCryptoLibException {

        Security.addProvider(new BouncyCastleProvider());

        _proofsServiceForDefaultPolicy = new ProofsService();

        _zpSubgroup = MathematicalTestDataGenerator.getZpSubgroup();

        _witness1 = ElGamalTestDataGenerator.getWitness(_zpSubgroup);
        _witness2 = ElGamalTestDataGenerator.getWitness(_zpSubgroup);

        _baseElements = MathematicalTestDataGenerator
            .getZpGroupElements(_zpSubgroup, NUM_BASE_ELEMENTS);

        _ciphertext = ProofsTestDataGenerator
            .getPlaintextExponentEqualityProofCiphertext(_zpSubgroup,
                _baseElements, _witness1, _witness2);

        _proof = _proofsServiceForDefaultPolicy
            .createProofProverAPI(_zpSubgroup)
            .createPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, _witness1, _witness2);

        _emptyBaseElementList = new ArrayList<>();

        _baseElementListWithNullValue = new ArrayList<>(_baseElements);
        _baseElementListWithNullValue.set(0, null);

        _shorterBaseElementList = MathematicalTestDataGenerator
            .getZpGroupElements(_zpSubgroup, (NUM_BASE_ELEMENTS - 1));

        _shorterBaseElementListLength = _shorterBaseElementList.size();
        _expectedBaseElementListLength = (2 * _ciphertext.size()) - 1;
    }

    @AfterClass
    public static void tearDown() {

        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
    }

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    @Parameters(method = "createPlaintextExponentEqualityProof")
    public void testPlaintextExponentEqualityProofCreationValidation(
            ZpSubgroup group, Ciphertext ciphertext,
            List<ZpGroupElement> baseElements, Witness witness1,
            Witness witness2, String errorMsg)
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        _proofsServiceForDefaultPolicy.createProofProverAPI(group)
            .createPlaintextExponentEqualityProof(ciphertext, baseElements,
                witness1, witness2);
    }

    public static Object[] createPlaintextExponentEqualityProof() {

        return $(
            $(null, _ciphertext, _baseElements, _witness1, _witness2,
                "Zp subgroup is null."),
            $(_zpSubgroup, null, _baseElements, _witness1, _witness2,
                "Ciphertext is null."),
            $(_zpSubgroup, _ciphertext, null, _witness1, _witness2,
                "List of base elements is null."),
            $(_zpSubgroup, _ciphertext, _emptyBaseElementList, _witness1,
                _witness2, "List of base elements is empty."),
            $(_zpSubgroup, _ciphertext, _baseElementListWithNullValue,
                _witness1, _witness2,
                "List of base elements contains one or more null elements."),
            $(_zpSubgroup, _ciphertext, _baseElements, null, _witness2,
                "First witness is null."),
            $(_zpSubgroup, _ciphertext, _baseElements, _witness1, null,
                "Second witness is null."),
            $(_zpSubgroup, _ciphertext, _shorterBaseElementList, _witness1,
                _witness2,
                "Length of list of base elements must be equal to twice the ciphertext length minus 1: "
                    + _expectedBaseElementListLength + "; Found "
                    + _shorterBaseElementListLength));
    }

    @Test
    @Parameters(method = "preComputePlaintextExponentEqualityProof")
    public void testPlaintextExponentEqualityProofPreComputationValidation(
            ZpSubgroup group, List<ZpGroupElement> baseElements,
            String errorMsg) throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        _proofsServiceForDefaultPolicy.createProofPreComputerAPI(group)
            .preComputePlaintextExponentEqualityProof(baseElements);
    }

    public static Object[] preComputePlaintextExponentEqualityProof() {

        return $($(null, _baseElements, "Zp subgroup is null."),
            $(_zpSubgroup, null, "List of base elements is null."),
            $(_zpSubgroup, _emptyBaseElementList,
                "List of base elements is empty."),
            $(_zpSubgroup, _baseElementListWithNullValue,
                "List of base elements contains one or more null elements."));
    }

    @Test
    @Parameters(method = "createPlaintextExponentEqualityProofUsingPreComputedValues")
    public void testPlaintextExponentEqualityProofCreationUsingPreComputedValuesValidation(
            ZpSubgroup group, Ciphertext ciphertext,
            List<ZpGroupElement> baseElements, Witness witness1,
            Witness witness2, ProofPreComputedValues preComputedValues,
            String errorMsg) throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        _proofsServiceForDefaultPolicy.createProofProverAPI(group)
            .createPlaintextExponentEqualityProof(ciphertext, baseElements,
                witness1, witness2, preComputedValues);
    }

    public static Object[] createPlaintextExponentEqualityProofUsingPreComputedValues()
            throws GeneralCryptoLibException {

        ProofPreComputedValues preComputedValues =
            _proofsServiceForDefaultPolicy
                .createProofPreComputerAPI(_zpSubgroup)
                .preComputePlaintextExponentEqualityProof(_baseElements);

        return $(
            $(null, _ciphertext, _baseElements, _witness1, _witness2,
                preComputedValues, "Zp subgroup is null."),
            $(_zpSubgroup, null, _baseElements, _witness1, _witness2,
                preComputedValues, "Ciphertext is null."),
            $(_zpSubgroup, _ciphertext, null, _witness1, _witness2,
                preComputedValues, "List of base elements is null."),
            $(_zpSubgroup, _ciphertext, _emptyBaseElementList, _witness1,
                _witness2, preComputedValues,
                "List of base elements is empty."),
            $(_zpSubgroup, _ciphertext, _baseElementListWithNullValue,
                _witness1, _witness2, preComputedValues,
                "List of base elements contains one or more null elements."),
            $(_zpSubgroup, _ciphertext, _baseElements, null, _witness2,
                preComputedValues, "First witness is null."),
            $(_zpSubgroup, _ciphertext, _baseElements, _witness1, null,
                preComputedValues, "Second witness is null."),
            $(_zpSubgroup, _ciphertext, _shorterBaseElementList, _witness1,
                _witness2, preComputedValues,
                "Length of list of base elements must be equal to twice the ciphertext length minus 1: "
                    + _expectedBaseElementListLength + "; Found "
                    + _shorterBaseElementListLength),
            $(_zpSubgroup, _ciphertext, _baseElements, _witness1,
                _witness2, null, "Pre-computed values object is null."));
    }

    @Test
    @Parameters(method = "verifyPlaintextExponentEqualityProof")
    public void testPlaintextExponentEqualityProofVerificationValidation(
            ZpSubgroup group, Ciphertext ciphertext,
            List<ZpGroupElement> baseElements, Proof proof,
            String errorMsg) throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        _proofsServiceForDefaultPolicy.createProofVerifierAPI(group)
            .verifyPlaintextExponentEqualityProof(ciphertext, baseElements,
                proof);
    }

    public static Object[] verifyPlaintextExponentEqualityProof()
            throws GeneralCryptoLibException {

        Proof proof = _proofsServiceForDefaultPolicy
            .createProofProverAPI(_zpSubgroup)
            .createPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, _witness1, _witness2);

        return $(
            $(null, _ciphertext, _baseElements, proof,
                "Zp subgroup is null."),
            $(_zpSubgroup, null, _baseElements, proof,
                "Ciphertext is null."),
            $(_zpSubgroup, _ciphertext, null, proof,
                "List of base elements is null."),
            $(_zpSubgroup, _ciphertext, _emptyBaseElementList, proof,
                "List of base elements is empty."),
            $(_zpSubgroup, _ciphertext, _baseElementListWithNullValue,
                proof,
                "List of base elements contains one or more null elements."),
            $(_zpSubgroup, _ciphertext, _baseElements, null,
                "Plaintext exponent equality proof is null."),
            $(_zpSubgroup, _ciphertext, _shorterBaseElementList, _proof,
                "Length of list of base elements must be equal to twice the ciphertext length minus 1: "
                    + _expectedBaseElementListLength + "; Found "
                    + _shorterBaseElementListLength));
    }
}
