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

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Collections;
import java.util.UUID;

import javax.crypto.SecretKey;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.BeforeClass;
import org.junit.Test;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.api.primitives.PrimitivesServiceAPI;
import com.scytl.cryptolib.api.symmetric.SymmetricServiceAPI;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.cryptolib.primitives.service.PrimitivesService;
import com.scytl.cryptolib.symmetric.service.SymmetricService;
import com.scytl.products.ov.constants.Constants;

public class ChoicesCodesGenerationTest {

    private static ChoicesCodesGenerator _choicesCodesGenerator;

    private static SymmetricServiceAPI _symmetricService;

    private static PrimitivesServiceAPI _primitivesService;

    private static String _electionEventId;

    private static String _verificationCardId;

    @BeforeClass
    public static void setUp() throws GeneralCryptoLibException {

        Security.addProvider(new BouncyCastleProvider());

        _symmetricService = new SymmetricService();

        _primitivesService = new PrimitivesService();

        _choicesCodesGenerator = new ChoicesCodesGenerator(_primitivesService, _symmetricService);

        _electionEventId = generateID();
        _verificationCardId = generateID();
    }

    @Test
    public void generate_valid_vote_casting_code() {

        int NUM_TESTS = 1000;

        String voteCastingCode;
        for (int i = 0; i < NUM_TESTS; i++) {

            voteCastingCode = _choicesCodesGenerator.generateVoteCastingCode();

            String errorMsg = "The generated vote casting code did not have " + Constants.NUM_DIGITS_VOTE_CASTING_CODE
                + " digits, it was: " + voteCastingCode;
            assertEquals(errorMsg, Constants.NUM_DIGITS_VOTE_CASTING_CODE, voteCastingCode.length());
        }
    }

    @Test
    public void generate_valid_short_choice_code() {

        int NUM_TESTS = 1000;

        String shortChoiceCode;
        for (int i = 0; i < NUM_TESTS; i++) {

            shortChoiceCode = _choicesCodesGenerator.generateShortChoiceCode();

            String errorMsg = "The generated short choice code did not have " + Constants.NUM_DIGITS_SHORT_CHOICE_CODE
                + " digits, it was: " + shortChoiceCode;
            assertEquals(errorMsg, 4, shortChoiceCode.length());
        }
    }

    @Test
    public void generate_valid_long_choice_code() throws GeneralCryptoLibException {

        ZpGroupElement partialCode =
            new ZpGroupElement(new BigInteger("2"), new BigInteger("23"), new BigInteger("11"));
        byte[] longChoiceCode = _choicesCodesGenerator.generateLongChoiceCode(_electionEventId, _verificationCardId,
            partialCode, Collections.emptyList());
        assertNotNull(longChoiceCode);
    }

    @Test
    public void generate_mapping() throws GeneralCryptoLibException {

        byte[] mData = "01234567".getBytes(StandardCharsets.UTF_8);

        SecretKey secretKey = _symmetricService.getSecretKeyForEncryption();
        ZpGroupElement partialCode =
            new ZpGroupElement(new BigInteger("2"), new BigInteger("23"), new BigInteger("11"));
        byte[] longCode = _choicesCodesGenerator.generateLongChoiceCode(_electionEventId, _verificationCardId,
            partialCode, Collections.emptyList());

        ChoicesCodesMappingEntry choicesCodesMappingEntry =
            _choicesCodesGenerator.generateMapping(mData, longCode, secretKey);

        assertNotNull(choicesCodesMappingEntry);
    }

    private static String generateID() {

        return UUID.randomUUID().toString().replaceAll("-", "");
    }

}
