/*
 * 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.products.ov.sdm.domain.service.utils;

import static org.junit.Assert.assertEquals;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.elgamal.bean.ElGamalEncryptionParameters;
import com.scytl.cryptolib.elgamal.bean.ElGamalKeyPair;
import com.scytl.cryptolib.elgamal.bean.ElGamalPublicKey;
import com.scytl.cryptolib.elgamal.cryptoapi.CryptoAPIElGamalKeyPairGenerator;
import com.scytl.cryptolib.elgamal.service.ElGamalService;

public class ElGamalPublicKeyCombinerWithCompressionTest {

    public static ElGamalEncryptionParameters params;

    public static ElGamalService service;

    @Rule
    public final ExpectedException exception = ExpectedException.none();

    @BeforeClass
    public static void setUp() throws GeneralCryptoLibException {

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

        params = new ElGamalEncryptionParameters(p, q, g);

        service = new ElGamalService();
    }

    @Test
    public void whenNullPrimaryKeyThenException() throws GeneralCryptoLibException {

        exception.expect(IllegalArgumentException.class);
        exception.expectMessage("The primary public key was null");

        ElGamalPublicKey nullKey = null;

        CryptoAPIElGamalKeyPairGenerator generator = service.getElGamalKeyPairGenerator();
        ElGamalKeyPair keyPairWithTwoSubKeys = generator.generateKeys(params, 2);

        List<ElGamalPublicKey> listOfKeys = new ArrayList<>();
        listOfKeys.add(keyPairWithTwoSubKeys.getPublicKeys());

        ElGamalPublicKeyCombinerWithCompression elGamalPublicKeyMultiplerWithCompression =
            new ElGamalPublicKeyCombinerWithCompression();

        elGamalPublicKeyMultiplerWithCompression.combine(nullKey, listOfKeys);
    }

    @Test
    public void givenTwoAdditionalKeysLargrThanPrimaryKey_whenCombineThenExpectedSize()
            throws GeneralCryptoLibException {

        CryptoAPIElGamalKeyPairGenerator generator = service.getElGamalKeyPairGenerator();

        ElGamalKeyPair keyPairWithThreeSubKeys = generator.generateKeys(params, 3);

        ElGamalKeyPair keyPairWithSevenSubKeys = generator.generateKeys(params, 7);
        ElGamalKeyPair keyPairWithFiveSubKeys = generator.generateKeys(params, 5);

        List<ElGamalPublicKey> listOfKeys = new ArrayList<>();
        listOfKeys.add(keyPairWithSevenSubKeys.getPublicKeys());
        listOfKeys.add(keyPairWithFiveSubKeys.getPublicKeys());

        ElGamalPublicKeyCombinerWithCompression elGamalPublicKeyMultiplerWithCompression =
            new ElGamalPublicKeyCombinerWithCompression();

        ElGamalPublicKey combined =
            elGamalPublicKeyMultiplerWithCompression.combine(keyPairWithThreeSubKeys.getPublicKeys(), listOfKeys);

        assertEquals(3, combined.getKeys().size());
    }

    @Test
    public void givenTwoAdditionalKeysOfSameSizeAsPrimaryKey_whenCombineThenExpectedSize()
            throws GeneralCryptoLibException {

        CryptoAPIElGamalKeyPairGenerator generator = service.getElGamalKeyPairGenerator();

        ElGamalKeyPair keyPairWithThreeSubKeys1 = generator.generateKeys(params, 3);

        ElGamalKeyPair keyPairWithThreeSubKeys2 = generator.generateKeys(params, 3);
        ElGamalKeyPair keyPairWithThreeSubKeys3 = generator.generateKeys(params, 3);

        List<ElGamalPublicKey> listOfKeys = new ArrayList<>();
        listOfKeys.add(keyPairWithThreeSubKeys2.getPublicKeys());
        listOfKeys.add(keyPairWithThreeSubKeys3.getPublicKeys());

        ElGamalPublicKeyCombinerWithCompression elGamalPublicKeyMultiplerWithCompression =
            new ElGamalPublicKeyCombinerWithCompression();

        ElGamalPublicKey combined =
            elGamalPublicKeyMultiplerWithCompression.combine(keyPairWithThreeSubKeys1.getPublicKeys(), listOfKeys);

        assertEquals(3, combined.getKeys().size());
    }

    @Test
    public void whenListIsEmptyThenOutputSizeEqualToPrimaryElGamalKeySize() throws GeneralCryptoLibException {

        CryptoAPIElGamalKeyPairGenerator generator = service.getElGamalKeyPairGenerator();

        ElGamalKeyPair keyPairWithThreeSubKeys = generator.generateKeys(params, 3);

        List<ElGamalPublicKey> listOfKeys = new ArrayList<>();

        ElGamalPublicKeyCombinerWithCompression elGamalPublicKeyMultiplerWithCompression =
            new ElGamalPublicKeyCombinerWithCompression();

        ElGamalPublicKey combined =
            elGamalPublicKeyMultiplerWithCompression.combine(keyPairWithThreeSubKeys.getPublicKeys(), listOfKeys);

        assertEquals(3, combined.getKeys().size());
    }

    @Test
    public void whenPrimaryKeyLargerThenOtherKeys() throws GeneralCryptoLibException {

        exception.expect(IllegalArgumentException.class);
        exception
            .expectMessage("The primary key has more elements than one of the additional keys. Primary: 6, other: 3");

        CryptoAPIElGamalKeyPairGenerator generator = service.getElGamalKeyPairGenerator();

        ElGamalKeyPair keyPairWithSevenSubKeys = generator.generateKeys(params, 6);

        ElGamalKeyPair keyPairWithFourSubKeys = generator.generateKeys(params, 3);
        ElGamalKeyPair keyPairWithFiveSubKeys = generator.generateKeys(params, 2);

        List<ElGamalPublicKey> listOfKeys = new ArrayList<>();
        listOfKeys.add(keyPairWithFourSubKeys.getPublicKeys());
        listOfKeys.add(keyPairWithFiveSubKeys.getPublicKeys());

        ElGamalPublicKeyCombinerWithCompression elGamalPublicKeyMultiplerWithCompression =
            new ElGamalPublicKeyCombinerWithCompression();

        elGamalPublicKeyMultiplerWithCompression.combine(keyPairWithSevenSubKeys.getPublicKeys(), listOfKeys);
    }
}
