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

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.Test;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.mathematical.groups.impl.Exponent;
import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup;

public class ExponentCompressorTest {

    private static BigInteger _p;

    private static BigInteger _q;

    private static BigInteger _g;

    private static ZpSubgroup _group;

    private static ExponentCompressor<ZpSubgroup> _compressor;

    @BeforeClass
    public static void setUp() throws GeneralCryptoLibException {

        _p = new BigInteger("23");

        _q = new BigInteger("11");

        _g = new BigInteger("2");

        _group = new ZpSubgroup(_g, _p, _q);

        _compressor = new ExponentCompressor<ZpSubgroup>(_group);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void attemptToCreateCompressorWithNullGroupThenException()
            throws GeneralCryptoLibException {

        new ExponentCompressor<ZpSubgroup>(null);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void attempToCompressNullListThenException()
            throws GeneralCryptoLibException {

        _compressor.compress(null);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void attempToCompressEmptyListThenException()
            throws GeneralCryptoLibException {

        List<Exponent> emptyListExponents = new ArrayList<Exponent>();

        _compressor.compress(emptyListExponents);
    }

    @Test
    public void givenListExponentsTotalValueLessThanQWhenCompressThenOK()
            throws GeneralCryptoLibException {

        List<Exponent> listExponents =
            getListExponentsTotalValuesLessThenQ();

        Exponent result = _compressor.compress(listExponents);

        Exponent expectedResult = new Exponent(_q, new BigInteger("9"));

        String errorMsg =
            "The compressed exponent did not have the expected value";
        assertEquals(errorMsg, expectedResult, result);
    }

    @Test
    public void givenListExponentsTotalValueGreaterThanQWhenCompressThenOK()
            throws GeneralCryptoLibException {

        List<Exponent> listExponents =
            getListExponentsTotalValuesGreaterThenQ();

        Exponent result = _compressor.compress(listExponents);

        Exponent expectedResult = new Exponent(_q, new BigInteger("3"));

        String errorMsg =
            "The compressed exponent did not have the expected value";
        assertEquals(errorMsg, expectedResult, result);

    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenListContainingExponentFromOtherGroupWhenCompressThenException()
            throws GeneralCryptoLibException {

        BigInteger otherQ = new BigInteger("3");
        Exponent exponentOtherGroup =
            new Exponent(otherQ, new BigInteger("2"));

        List<Exponent> listExponents =
            getListExponentsTotalValuesGreaterThenQ();
        listExponents.add(exponentOtherGroup);

        _compressor.compress(listExponents);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenNegativeNumRequiredExponentsWhenBuildNewListThenException()
            throws GeneralCryptoLibException {

        List<Exponent> originalList = getListExponents();
        int numRequired = -1;

        _compressor.buildListWithCompressedFinalElement(numRequired,
            originalList);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenNullListWhenBuildNewListThenException()
            throws GeneralCryptoLibException {

        List<Exponent> nullList = null;
        int numRequired = 2;

        _compressor.buildListWithCompressedFinalElement(numRequired,
            nullList);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenEmptyListWhenBuildNewListThenException()
            throws GeneralCryptoLibException {

        List<Exponent> emptyList = new ArrayList<Exponent>();
        int numRequired = 2;

        _compressor.buildListWithCompressedFinalElement(numRequired,
            emptyList);
    }

    @Test
    public void givenListAndNumRequiredExponentsWhenBuildNewListThenOK()
            throws GeneralCryptoLibException {

        List<Exponent> originalList = getListExponents();
        int numRequired = 2;

        List<Exponent> newList =
            _compressor.buildListWithCompressedFinalElement(numRequired,
                originalList);

        List<Exponent> expectedList = getExpectedCompressedList();

        String errorMsg =
            "The created compressed list was not the expected size, expected: "
                + numRequired + ", but was: " + newList.size();
        assertEquals(errorMsg, numRequired, newList.size());

        errorMsg =
            "The created compressed did not have the expected values, expected: "
                + expectedList + ", but was: " + newList;
        assertEquals(errorMsg, expectedList, newList);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void moreElementsThanExisting()
            throws GeneralCryptoLibException {

        List<Exponent> originalList = getListExponents();
        int numRequired = 102;

        _compressor.buildListWithCompressedFinalElement(numRequired,
            originalList);

    }

    @Test(expected = GeneralCryptoLibException.class)
    public void differentGroupElementTest()
            throws GeneralCryptoLibException {

        BigInteger otherQ = new BigInteger("4");
        Exponent exponentOtherGroup =
            new Exponent(otherQ, new BigInteger("2"));

        List<Exponent> lst = new ArrayList<>();

        List<Exponent> originalList = getListExponents();
        lst.add(exponentOtherGroup);
        lst.addAll(originalList);
        int numRequired = 2;

        _compressor.buildListWithCompressedFinalElement(numRequired, lst);
    }

    private List<Exponent> getListExponentsTotalValuesLessThenQ()
            throws GeneralCryptoLibException {

        Exponent exponent2 = new Exponent(_q, new BigInteger("2"));
        Exponent exponent3 = new Exponent(_q, new BigInteger("3"));
        Exponent exponent4 = new Exponent(_q, new BigInteger("4"));

        List<Exponent> listExponents = new ArrayList<Exponent>();

        listExponents.add(exponent2);
        listExponents.add(exponent3);
        listExponents.add(exponent4);

        return listExponents;
    }

    private List<Exponent> getListExponentsTotalValuesGreaterThenQ()
            throws GeneralCryptoLibException {

        Exponent exponent2 = new Exponent(_q, new BigInteger("2"));
        Exponent exponent3 = new Exponent(_q, new BigInteger("3"));
        Exponent exponent4 = new Exponent(_q, new BigInteger("4"));
        Exponent exponent5 = new Exponent(_q, new BigInteger("5"));

        List<Exponent> listExponents = new ArrayList<Exponent>();

        listExponents.add(exponent2);
        listExponents.add(exponent3);
        listExponents.add(exponent4);
        listExponents.add(exponent5);

        return listExponents;
    }

    private List<Exponent> getListExponents()
            throws GeneralCryptoLibException {

        Exponent exponent2 = new Exponent(_q, new BigInteger("2"));
        Exponent exponent3 = new Exponent(_q, new BigInteger("3"));
        Exponent exponent4 = new Exponent(_q, new BigInteger("4"));
        Exponent exponent5 = new Exponent(_q, new BigInteger("5"));
        Exponent exponent6 = new Exponent(_q, new BigInteger("6"));

        List<Exponent> listExponents = new ArrayList<Exponent>();

        listExponents.add(exponent2);
        listExponents.add(exponent3);
        listExponents.add(exponent4);
        listExponents.add(exponent5);
        listExponents.add(exponent6);

        return listExponents;
    }

    private List<Exponent> getExpectedCompressedList()
            throws GeneralCryptoLibException {

        Exponent exponent2 = new Exponent(_q, new BigInteger("2"));
        Exponent exponent7 = new Exponent(_q, new BigInteger("7"));

        List<Exponent> listExponents = new ArrayList<Exponent>();

        listExponents.add(exponent2);
        listExponents.add(exponent7);

        return listExponents;
    }
}
