/*
 * 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.mixnet.commons.beans;

import static org.junit.Assert.assertTrue;

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

import org.junit.Test;
import org.junit.runner.RunWith;

import com.scytl.cryptolib.api.exceptions.CryptoLibException;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.elgamal.bean.CiphertextImpl;
import com.scytl.cryptolib.elgamal.cryptoapi.Ciphertext;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.products.ov.mixnet.commons.ballots.ElGamalEncryptedBallots;
import com.scytl.products.ov.mixnet.commons.homomorphic.Randomness;
import com.scytl.products.ov.mixnet.commons.homomorphic.impl.GjosteenElGamalRandomness;
import com.scytl.products.ov.mixnet.commons.tools.ExponentTools;

import mockit.Mocked;
import mockit.integration.junit4.JMockit;

@RunWith(JMockit.class)
public class ShuffleOutputTest {

    private static ShuffleOutput<ElGamalEncryptedBallots> _target;

    final BigInteger _p = new BigInteger("23");

    final BigInteger _q = new BigInteger("11");

    @Test
    public void return_the_expected_permutation(@Mocked final Permutation permutation,
            @Mocked final Randomness[] randomness, @Mocked final ElGamalEncryptedBallots encryptedBallots) {

        _target = new ShuffleOutput<>(permutation, randomness, encryptedBallots);

        Permutation returnedPermutation = _target.getPermutation();

        assertTrue(permutation.equals(returnedPermutation));
    }

    @Test
    public void return_a_permutation_that_is_not_equal_to_a_different_permutation(@Mocked final Permutation permutation,
            @Mocked final Randomness[] randomness, @Mocked final ElGamalEncryptedBallots encryptedBallots) {

        _target = new ShuffleOutput<>(permutation, randomness, encryptedBallots);

        Permutation otherPermutation = new Permutation(new int[] {0, 1, 2 });

        Permutation returnedPermutation = _target.getPermutation();

        assertTrue(!returnedPermutation.equals(otherPermutation));
    }

    @Test
    public void return_the_expected_randomness(@Mocked final Permutation permutation,
            @Mocked final Randomness[] randomness, @Mocked final ElGamalEncryptedBallots encryptedBallots) {

        _target = new ShuffleOutput<>(permutation, randomness, encryptedBallots);

        Randomness[] returnedRandomness = _target.getExponents();

        assertTrue(randomness == returnedRandomness);
    }

    @Test
    public void return_a_randomness_that_is_not_equal_to_a_different_randomness(@Mocked final Permutation permutation,
            @Mocked final Randomness[] randomness, @Mocked final ElGamalEncryptedBallots encryptedBallots) {

        _target = new ShuffleOutput<>(permutation, randomness, encryptedBallots);

        Randomness[] otherRandomness = new GjosteenElGamalRandomness[1];
        otherRandomness[0] = new GjosteenElGamalRandomness(ExponentTools.getRandomExponent(_q));

        Randomness[] returnedRandomness = _target.getExponents();

        assertTrue(!Arrays.equals(otherRandomness, returnedRandomness));
    }

    @Test
    public void return_the_expected_encrypted_ballots(@Mocked final Permutation permutation,
            @Mocked final Randomness[] randomness, @Mocked final ElGamalEncryptedBallots encryptedBallots) {

        _target = new ShuffleOutput<>(permutation, randomness, encryptedBallots);

        ElGamalEncryptedBallots returnedElGamalEncryptedBallots = _target.getReEncryptedBallots();

        assertTrue(encryptedBallots.equals(returnedElGamalEncryptedBallots));
    }

    @Test
    public void return_the_expected_encrypted_ballots_that_is_not_equal_to_a_different_encrypted_ballots(
            @Mocked final Permutation permutation, @Mocked final Randomness[] randomness,
            @Mocked final ElGamalEncryptedBallots encryptedBallots) {

        _target = new ShuffleOutput<>(permutation, randomness, encryptedBallots);

        ElGamalEncryptedBallots other = createElGamalEncryptedBallots();

        ElGamalEncryptedBallots returnedElGamalEncryptedBallots = _target.getReEncryptedBallots();

        assertTrue(!other.equals(returnedElGamalEncryptedBallots));
    }

    private ElGamalEncryptedBallots createElGamalEncryptedBallots() {

        List<Ciphertext> list = createListOfElGamalEncryptedBallot();

        return new ElGamalEncryptedBallots(list);
    }

    private List<Ciphertext> createListOfElGamalEncryptedBallot() {

        try {

            final List<ZpGroupElement> elements = new ArrayList<>();
            elements.add(new ZpGroupElement(new BigInteger("2"), _p, _q));
            elements.add(new ZpGroupElement(new BigInteger("3"), _p, _q));
            CiphertextImpl ballot = new CiphertextImpl(elements.get(0), elements.subList(1, elements.size()));
            List<Ciphertext> list = new ArrayList<>();
            list.add(ballot);
            return list;

        } catch (GeneralCryptoLibException e) {
            throw new CryptoLibException(e);
        }
    }
}
