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

import java.math.BigInteger;
import java.util.Random;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.primitives.securerandom.constants.SecureRandomConstants;
import com.scytl.cryptolib.primitives.service.PrimitivesService;
import com.scytl.cryptolib.test.tools.utils.CommonTestDataGenerator;

/**
 * Utility to generate various types of primitives data needed by tests.
 */
public class PrimitivesTestDataGenerator {
    private static final Random RANDOM = new Random();

    /**
     * Randomly generates a big integer of a specified length in digits.
     * 
     * @param length
     *            the length in digits of the big integer.
     * @return the randomly generated big integer.
     * @throws GeneralCryptoLibException
     *             if the generation process fails.
     */
    public static BigInteger getBigInteger(final int length)
            throws GeneralCryptoLibException {

        if (length <= SecureRandomConstants.MAXIMUM_GENERATED_BIG_INTEGER_DIGIT_LENGTH) {
            throw new GeneralCryptoLibException(
                "Length in digits for randomly generated BigInteger test data must be less than or equal to "
                    + SecureRandomConstants.MAXIMUM_GENERATED_BIG_INTEGER_DIGIT_LENGTH
                    + "; Found " + length);
        }

        return new PrimitivesService().getCryptoRandomInteger()
            .nextRandom(length);
    }

    /**
     * Randomly generates a byte array of a specified length.
     * 
     * @param length
     *            the length of the byte array.
     * @return the randomly generated byte array.
     * @throws GeneralCryptoLibException
     *             if the generation process fails.
     */
    public static byte[] getByteArray(final int length)
            throws GeneralCryptoLibException {

        if (length <= SecureRandomConstants.MAXIMUM_GENERATED_BYTE_ARRAY_LENGTH) {
            return new PrimitivesService().getCryptoRandomBytes()
                .nextRandom(length);
        } else {
            byte[] bytes = new byte[length];
            RANDOM.nextBytes(bytes);
            return bytes;
        }
    }

    /**
     * Randomly generates an array of byte arrays of equal specified length.
     * 
     * @param byteArrayLength
     *            the length of each byte array.
     * @param numByteArrays
     *            the number of byte arrays.
     * @return the randomly generated array of byte arrays.
     * @throws GeneralCryptoLibException
     *             if the generation process fails.
     */
    public static byte[][] getByteArrayArray(final int byteArrayLength,
            final int numByteArrays) throws GeneralCryptoLibException {

        byte[][] bytesArray = new byte[numByteArrays][byteArrayLength];
        for (int i = 0; i < numByteArrays; i++) {
            byte[] bytes = getByteArray(byteArrayLength);
            bytesArray[i] = bytes;
        }

        return bytesArray;
    }

    /**
     * Randomly generates a byte object array of a specified length.
     * 
     * @param length
     *            the length of the byte object array.
     * @return the randomly generated byte object array.
     * @throws GeneralCryptoLibException
     *             if the generation process fails.
     */
    public static Byte[] getByteObjectArray(final int length)
            throws GeneralCryptoLibException {

        byte[] bytes = new byte[length];
        if (length <= SecureRandomConstants.MAXIMUM_GENERATED_BYTE_ARRAY_LENGTH) {
            bytes =
                new PrimitivesService().getCryptoRandomBytes().nextRandom(
                    length);
        } else {
            bytes = new byte[length];
            RANDOM.nextBytes(bytes);
        }

        Byte[] byteObjects = new Byte[length];
        for (int i = 0; i < length; i++) {
            byteObjects[i] = bytes[i];
        }

        return byteObjects;
    }

    /**
     * Randomly generates a string of a specified length in characters, from the
     * 32 character alphabet defined by cryptoLib.
     * 
     * @param length
     *            the length in characters of the string.
     * @return the randomly generated string.
     * @throws GeneralCryptoLibException
     *             if the generation process fails.
     */
    public static String getString32(final int length)
            throws GeneralCryptoLibException {

        if (length <= SecureRandomConstants.MAXIMUM_GENERATED_STRING_LENGTH) {
            return new PrimitivesService()
                .get32CharAlphabetCryptoRandomString().nextRandom(length);
        } else {
            return CommonTestDataGenerator.getAlphanumeric(length);
        }
    }

    /**
     * Randomly generates a string of a specified length in characters, from the
     * 64 character alphabet defined by cryptoLib.
     * 
     * @param length
     *            the length in characters of the string.
     * @return the randomly generated string.
     * @throws GeneralCryptoLibException
     *             if the generation process fails.
     */
    public static String getString64(final int length)
            throws GeneralCryptoLibException {

        if (length <= SecureRandomConstants.MAXIMUM_GENERATED_STRING_LENGTH) {
            return new PrimitivesService()
                .get64CharAlphabetCryptoRandomString().nextRandom(length);
        } else {
            return CommonTestDataGenerator.getAlphanumeric(length);
        }
    }

    /**
     * Randomly generates a character array of a specified length, from the 32
     * character alphabet defined by cryptoLib.
     * 
     * @param length
     *            the length of the character array.
     * @return the randomly generated character array.
     * @throws GeneralCryptoLibException
     *             if the generation process fails.
     */
    public static char[] getCharArray32(final int length)
            throws GeneralCryptoLibException {

        return getString32(length).toCharArray();
    }

    /**
     * Randomly generates a character array of a specified length, from the 64
     * character alphabet defined by cryptoLib.
     * 
     * @param length
     *            the length of the character array.
     * @return the randomly generated character array.
     * @throws GeneralCryptoLibException
     *             if the generation process fails.
     */
    public static char[] getCharArray64(final int length)
            throws GeneralCryptoLibException {

        return getString64(length).toCharArray();
    }
}
