/*
 * 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.primitives.securerandom.factory;

import static mockit.Deencapsulation.getField;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;

import org.junit.Assume;
import org.junit.Test;

import com.scytl.cryptolib.api.exceptions.CryptoLibException;
import com.scytl.cryptolib.commons.configuration.Provider;
import com.scytl.cryptolib.commons.utils.system.OperatingSystem;
import com.scytl.cryptolib.primitives.securerandom.configuration.ConfigSecureRandomAlgorithmAndProvider;
import com.scytl.cryptolib.primitives.securerandom.configuration.SecureRandomPolicy;
import com.scytl.cryptolib.primitives.securerandom.constants.SecureRandomConstants;

import mockit.Expectations;

public class SecureRandomFactoryTest {

    @Test
    public void whenCreateSecureRandomFactoryUsingAGivenPolicy() {

        Assume.assumeTrue(OperatingSystem.LINUX.isCurrent());

        SecureRandomFactory secureRandomFactoryByPolicy =
            new SecureRandomFactory(getSecureRandomPolicyNativePrngSun());

        SecureRandomPolicy expectedSecureRandomPolicy =
            getField(secureRandomFactoryByPolicy, "_secureRandomPolicy");

        assertEquals(getSecureRandomPolicyNativePrngSun()
            .getSecureRandomAlgorithmAndProvider(),
            expectedSecureRandomPolicy
                .getSecureRandomAlgorithmAndProvider());

    }

    @Test
    public void whenCreateCryptoSecureRandomFromLinux()
            throws NoSuchAlgorithmException, NoSuchProviderException {

        Assume.assumeTrue(OperatingSystem.LINUX.isCurrent());

        SecureRandomFactory secureRandomFactory =
            new SecureRandomFactory(getSecureRandomPolicyNativePrngSun());

        final String algorithm =
            getSecureRandomPolicyNativePrngSun()
                .getSecureRandomAlgorithmAndProvider().getAlgorithm();
        final Provider provider =
            getSecureRandomPolicyNativePrngSun()
                .getSecureRandomAlgorithmAndProvider().getProvider();

        new Expectations() {
            {
                SecureRandom.getInstance(algorithm,
                    provider.getProviderName());
            }
        };

        assertNotNull(secureRandomFactory.createByteRandom());
    }

    @Test
    public void whenCreateCryptoSecureRandomFromWindows()
            throws NoSuchAlgorithmException, NoSuchProviderException {

        Assume.assumeTrue(OperatingSystem.WINDOWS.isCurrent());

        SecureRandomFactory secureRandomFactory =
            new SecureRandomFactory(getSecureRandomPolicyWindowsPrng());

        final String algorithm =
            getSecureRandomPolicyWindowsPrng()
                .getSecureRandomAlgorithmAndProvider().getAlgorithm();
        final Provider provider =
            getSecureRandomPolicyWindowsPrng()
                .getSecureRandomAlgorithmAndProvider().getProvider();

        new Expectations() {
            {
                SecureRandom.getInstance(algorithm,
                    provider.getProviderName());
            }
        };

        assertNotNull(secureRandomFactory.createByteRandom());
    }

    @Test
    public void whenCreateSecureRandomFactoryUsingLinuxBasedAlgFromWindows() {

        Assume.assumeTrue(OperatingSystem.LINUX.isCurrent());

        new SecureRandomFactory(getSecureRandomPolicyNativePrngSun())
            .createByteRandom();
    }

    @Test
    public void whenCreateSecureRandomFactoryUsingWindowsAlgFromLinux() {

        Assume.assumeTrue(OperatingSystem.WINDOWS.isCurrent());
        
        new SecureRandomFactory(getSecureRandomPolicyWindowsPrng())
            .createByteRandom();
    }

    @Test(expected = CryptoLibException.class)
    public void whenCreateSecureRandomFactoryUsingAlgFromWrongOperatingSystem() {

        Assume.assumeTrue(OperatingSystem.LINUX.isCurrent());
        
        new SecureRandomFactory(getSecureRandomPolicyWindowsPrng())
            .createByteRandom();
    }

    @Test
    public void whenCreateCryptoByteRandom() {

        Assume.assumeTrue(OperatingSystem.LINUX.isCurrent());

        CryptoRandomBytes cryptoByteRandom =
            new SecureRandomFactory(getSecureRandomPolicyNativePrngSun())
                .createByteRandom();

        assertTrue(cryptoByteRandom.getClass() == CryptoRandomBytes.class);
        assertNotNull(cryptoByteRandom);
    }

    @Test
    public void whenCreateCryptoIntegerRandom() {

        Assume.assumeTrue(OperatingSystem.LINUX.isCurrent());

        CryptoRandomInteger cryptoIntegerRandom =
            new SecureRandomFactory(getSecureRandomPolicyNativePrngSun())
                .createIntegerRandom();

        assertTrue(cryptoIntegerRandom.getClass() == CryptoRandomInteger.class);
        assertNotNull(cryptoIntegerRandom);
    }

    @Test
    public void whenCreateCryptoStringRandom() {

        Assume.assumeTrue(OperatingSystem.LINUX.isCurrent());

        CryptoRandomString cryptoStringRandom =
            new SecureRandomFactory(getSecureRandomPolicyNativePrngSun())
                .createStringRandom(SecureRandomConstants.ALPHABET_BASE32);

        assertTrue(cryptoStringRandom.getClass() == CryptoRandomString.class);
        assertNotNull(cryptoStringRandom);

    }

    @Test(expected = CryptoLibException.class)
    public void whenCreateCryptoStringRandomGivenANullAlphabet() {

        Assume.assumeTrue(OperatingSystem.LINUX.isCurrent());

        new SecureRandomFactory(getSecureRandomPolicyNativePrngSun())
            .createStringRandom(null);
    }

    private SecureRandomPolicy getSecureRandomPolicyNativePrngSun() {
        return new SecureRandomPolicy() {

            @Override
            public ConfigSecureRandomAlgorithmAndProvider getSecureRandomAlgorithmAndProvider() {
                return ConfigSecureRandomAlgorithmAndProvider.NATIVE_PRNG_SUN;
            }
        };
    }

    private SecureRandomPolicy getSecureRandomPolicyWindowsPrng() {
        return new SecureRandomPolicy() {

            @Override
            public ConfigSecureRandomAlgorithmAndProvider getSecureRandomAlgorithmAndProvider() {
                return ConfigSecureRandomAlgorithmAndProvider.PRNG_SUN_MSCAPI;
            }
        };
    }
}
