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

import com.scytl.products.ov.mixnet.commons.beans.Permutation;

import java.security.SecureRandom;

/**
 * Permits the permutation of an array of elements.
 */
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 used by the created
     *                     PermutationGenerator.
     * @throws IllegalArgumentException if the received SecureRandom is null.
     */
    public PermutationGenerator() {

        _secureRandom = new SecureRandom();
    }

    /**
     * Performs a Fisher-Yates shuffle (also known as a Knuth shuffle) on an
     * array of integers.
     * <p>
     * The shuffle is performed directly on the received array.
     *
     * @param intArray the array of integers to be shuffled.
     * @throws IllegalArgumentException if the received array is not an initialized array with at
     *                                  least two elements.
     */
    public Permutation generate(final int numElements) {

        if (numElements < 1) {
            throw new IllegalArgumentException(
                    "A size of at least 1 must be received.");
        }

        final int[] identityPerm = generateIdentityPermutation(numElements);

        return new Permutation(permutateArray(identityPerm));
    }

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

    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;
    }
}
