/*
 * 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.ov.mixing.core.shuffle;

import java.security.SecureRandom;

import com.scytl.ov.mixing.shuffle.Permutation;

/**
 * Allows the generation of a {@link Permutation}, which can be used for permutating (or shuffling) a list.
 */
public final class PermutationGenerator {

    private final SecureRandom secureRandom;

    /**
     * Create a {@link PermutationGenerator}, setting the received SecureRandom as its source of randomness.
     *
     * @param secureRandom
     *            the source of randomness to be set.
     * @throws IllegalArgumentException
     *             if the received SecureRandom is null.
     */
    public PermutationGenerator(SecureRandom secureRandom) {

        if (secureRandom == null) {
            throw new IllegalArgumentException("The received source of randomness was null");
        }

        this.secureRandom = new SecureRandom();
    }

    /**
     * Generate a {@link Permutation} with numElements elements.
     * <P>
     * The Permutation is generated by first creating a list containing the identity permutation and then shuffling that
     * list using a Fisher-Yates shuffle (also known as a Knuth shuffle).
     * 
     * @param numElements
     *            the size of the desired Permutation.
     */
    public Permutation generate(final int numElements) {

        if (numElements < 1) {
            throw new IllegalArgumentException(
                "The size of the permutation should be at least 1, but received: " + numElements);
        }

        final int[] identityPerm = generateIdentityPermutation(numElements);

        return new Permutation(permutateArray(identityPerm));
    }

    private static int[] generateIdentityPermutation(final int numElements) {

        final int[] identityPermutation = new int[numElements];
        for (int i = 0; i < numElements; i++) {
            identityPermutation[i] = i;
        }
        return identityPermutation;
    }

    private int[] permutateArray(final int[] intArray) {

        for (int i = intArray.length - 1; i > 0; i--) {
            final int randomIndex = secureRandom.nextInt(i + 1);
            final int temp = intArray[randomIndex];
            intArray[randomIndex] = intArray[i];
            intArray[i] = temp;
        }

        return intArray;
    }
}
