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

import static org.junit.Assert.assertEquals;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;

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

import com.scytl.cryptolib.api.exceptions.CryptoLibException;
import com.scytl.cryptolib.commons.utils.system.OperatingSystem;
import com.scytl.cryptolib.elgamal.configuration.ConfigGroupType;
import com.scytl.cryptolib.elgamal.configuration.ElGamalPolicy;
import com.scytl.cryptolib.mathematical.groups.utils.GroupUtils;
import com.scytl.cryptolib.primitives.securerandom.configuration.ConfigSecureRandomAlgorithmAndProvider;
import com.scytl.cryptolib.primitives.securerandom.factory.CryptoRandomInteger;
import com.scytl.cryptolib.primitives.securerandom.factory.SecureRandomFactory;

public class QuadResParamsGeneratorTest {

    private QuadResParamsGenerator _quadResParamsGenerator;

    private static final int CERTAINTY_LEVEL = 80;

    private ConfigGroupType _subgroup;

    @Before
    public void beforeTest() {

        ElGamalPolicy policy = getPolicy();
        CryptoRandomInteger cryptoRandomInteger =
            new SecureRandomFactory(policy).createIntegerRandom();

        _subgroup = policy.getGroupType();

        _quadResParamsGenerator =
            new QuadResParamsGenerator(_subgroup.getL(), CERTAINTY_LEVEL,
                cryptoRandomInteger);
    }

    /**
     * In this test we expect that a CryptoLibException will be thrown by the
     * method being tested, however, because we are using reflection to test a
     * private method, the expected CryptoLibException is not at the top of the
     * exception stack. Instead we receive a InvocationTargetException within
     * this test.
     */
    @Test(expected = InvocationTargetException.class)
    public void givenNegativePWhenCallFindGeneratorThenException1()
            throws SecurityException, NoSuchMethodException,
            IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {

        BigInteger p = new BigInteger("11");
        BigInteger q = BigInteger.ONE;

        Method method =
            QuadResParamsGenerator.class.getDeclaredMethod(
                "findSmallestGenerator", BigInteger.class,
                BigInteger.class);
        method.setAccessible(true);
        method.invoke(_quadResParamsGenerator, p, q);
    }

    /**
     * In this test we expect that a CryptoLibException will be thrown by the
     * method being tested, however, because we are using reflection to test a
     * private method, the expected CryptoLibException is not at the top of the
     * exception stack. Instead we receive a InvocationTargetException within
     * this test.
     */
    @Test(expected = InvocationTargetException.class)
    public void givenNegativePWhenCallFindGeneratorThenException()
            throws SecurityException, NoSuchMethodException,
            IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {

        BigInteger p = new BigInteger("-23");
        BigInteger q = new BigInteger("11");

        Method method =
            QuadResParamsGenerator.class.getDeclaredMethod(
                "findSmallestGenerator", BigInteger.class,
                BigInteger.class);
        method.setAccessible(true);
        method.invoke(_quadResParamsGenerator, p, q);
    }

    @Test
    public void givenGroupParametersWhenCallFindGeneratorThenExpectedValue()
            throws SecurityException, NoSuchMethodException,
            IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {

        BigInteger p = new BigInteger("23");
        BigInteger q = new BigInteger("11");

        Method method =
            QuadResParamsGenerator.class.getDeclaredMethod(
                "findSmallestGenerator", BigInteger.class,
                BigInteger.class);
        method.setAccessible(true);
        BigInteger foundGenerator =
            (BigInteger) method.invoke(_quadResParamsGenerator, p, q);

        String errorMsg = "Failed to find the expected generator";
        assertEquals(errorMsg, GroupUtils.BIG_INTEGER_TWO, foundGenerator);
    }

    private static ElGamalPolicy getPolicy() {

        return new ElGamalPolicy() {

            @Override
            public ConfigGroupType getGroupType() {
                return ConfigGroupType.QR_2048;
            }

            @Override
            public ConfigSecureRandomAlgorithmAndProvider getSecureRandomAlgorithmAndProvider() {

                switch (OperatingSystem.current()) {

                case WINDOWS:
                    return ConfigSecureRandomAlgorithmAndProvider.PRNG_SUN_MSCAPI;
                case LINUX:
                    return ConfigSecureRandomAlgorithmAndProvider.NATIVE_PRNG_SUN;
                default:
                    throw new CryptoLibException("OS not supported");
                }
            }
        };
    }
}
