/*
 * 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.mathematical.groups.impl;

import static junitparams.JUnitParamsRunner.$;

import java.math.BigInteger;

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.test.tools.configuration.ZpSubgroupLoader;

import junitparams.JUnitParamsRunner;
import junitparams.Parameters;

/**
 * Tests of the Zp group element input validation.
 */
@RunWith(JUnitParamsRunner.class)
public class ZpGroupElementValidationTest {

    private static ZpSubgroup _zpSubgroup;

    private static BigInteger _p;

    private static BigInteger _q;

    private static BigInteger _g;

    private static BigInteger _pMinusOne;

    private static BigInteger _elementValue;

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

    public static void setUp() throws GeneralCryptoLibException {

        ZpSubgroupLoader zpSubgroupLoader = new ZpSubgroupLoader();
        _zpSubgroup = new ZpSubgroup(zpSubgroupLoader.getG(),
            zpSubgroupLoader.getP(), zpSubgroupLoader.getQ());

        _p = _zpSubgroup.getP();
        _q = _zpSubgroup.getQ();
        _g = _zpSubgroup.getG();

        _pMinusOne = _p.subtract(BigInteger.ONE);

        _elementValue = BigInteger.ONE;
    }

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

    @Test
    @Parameters(method = "createZpGroupElementFromGroup")
    public void testZpGroupElementCreationFromGroupValidation(
            BigInteger value, ZpSubgroup group, String errorMsg)
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        new ZpGroupElement(value, group);
    }

    public static Object[] createZpGroupElementFromGroup() {

        return $($(null, _zpSubgroup, "Zp group element value is null."),
            $(_elementValue, null, "Zp subgroup is null."),
            $(BigInteger.ZERO, _zpSubgroup,
                "Zp group element value must be greater than or equal to : 1; Found 0"),
            $(_p, _zpSubgroup,
                "Zp group element value must be less than or equal to Zp subgroup p parameter minus 1: "
                    + _pMinusOne + "; Found " + _p));
    }

    @Test
    @Parameters(method = "createZpGroupElementFromParams")
    public void testZpGroupElementCreationFromParamsValidation(
            BigInteger value, BigInteger p, BigInteger q, String errorMsg)
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        new ZpGroupElement(value, p, q);
    }

    public static Object[] createZpGroupElementFromParams() {

        return $($(null, _p, _q, "Zp group element value is null."),
            $(_elementValue, null, _q, "Zp subgroup p parameter is null."),
            $(_elementValue, _p, null, "Zp subgroup q parameter is null."),
            $(_g, _p, BigInteger.ZERO,
                "Zp subgroup q parameter must be greater than or equal to : 1; Found 0"),
            $(_elementValue, _p, _p,
                "Zp subgroup q parameter must be less than or equal to Zp subgroup p parameter minus 1: "
                    + _pMinusOne + "; Found " + _p),
            $(BigInteger.ZERO, _p, _q,
                "Zp group element value must be greater than or equal to : 1; Found 0"),
            $(_p, _p, _q,
                "Zp group element value must be less than or equal to Zp subgroup p parameter minus 1: "
                    + _pMinusOne + "; Found " + _p));
    }

    @Test
    @Parameters(method = "deserializeZpGroupElement")
    public void testZpGroupElementDeserializationValidation(String jsonStr,
            String errorMsg) throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        ZpGroupElement.fromJson(jsonStr);
    }

    public static Object[] deserializeZpGroupElement() {

        String nullQParamJsonStr =
            "{\"zpGroupElement\":{\"q\":null,\"p\":\"Fw==\",\"value\":\"Ag==\"}}";
        String nullElementValueJsonStr =
            "{\"zpGroupElement\":{\"q\":\"Cw==\",\"p\":\"Fw==\",\"value\":null}}";
        String zeroQParamJsonStr =
            "{\"zpGroupElement\":{\"q\":\"AA==\",\"p\":\"Fw==\",\"value\":\"Ag==\"}}";
        String tooLargeQParamJsonStr =
            "{\"zpGroupElement\":{\"q\":\"Fw==\",\"p\":\"Fw==\",\"value\":\"Ag==\"}}";
        String zeroElementValueJsonStr =
            "{\"zpGroupElement\":{\"q\":\"Cw==\",\"p\":\"Fw==\",\"value\":\"AA==\"}}";
        String tooLargeElementValueJsonStr =
            "{\"zpGroupElement\":{\"q\":\"Cw==\",\"p\":\"Fw==\",\"value\":\"Fw==\"}}";

        return $($(null, "ZpGroupElement JSON string is null."),
            $("", "ZpGroupElement JSON string is blank."),
            $("   ", "ZpGroupElement JSON string is blank."),
            $(nullQParamJsonStr, "Zp subgroup q parameter is null."),
            $(nullElementValueJsonStr, "Zp group element value is null."),
            $(zeroQParamJsonStr,
                "Zp subgroup q parameter must be greater than or equal to : 1; Found 0"),
            $(tooLargeQParamJsonStr,
                "Zp subgroup q parameter must be less than or equal to Zp subgroup p parameter minus 1: "),
            $(zeroElementValueJsonStr,
                "Zp group element value must be greater than or equal to : 1; Found 0"),
            $(tooLargeElementValueJsonStr,
                "Zp group element value must be less than or equal to Zp subgroup p parameter minus 1: "));
    }
}
