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

import java.util.Arrays;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * Represents a permutation that can be used when shuffling (or "permutating") a list.
 * <P>
 * A permutation is comprised of a list of integer values, where each values indicates the index of the element in the
 * unshuffled list which should be in that position in the shuffled array.
 * <P>
 * For example, given the following list of values and the following permutation, then the result of applying this
 * permutation would be the shuffled list shown:
 * <ul>
 * <li>Unshuffled list: [777, 888, 666, 999]</li>
 * <li>Permutation: [2, 0, 1, 3]</li>
 * <li>Shuffled list: [666, 777, 888, 999]</li>
 * </ul>
 */
public final class Permutation {

    private final int[] permutation;

    /**
     * Create a Permutation setting the received permutation.
     * 
     * @param permutation
     *            the permutation to set in this Permutation.
     */
    public Permutation(final int[] permutation) {

        validateInput(permutation);

        this.permutation = Arrays.copyOf(permutation, permutation.length);
    }

    public int getLength() {
        return permutation.length;
    }

    public int getCorrespondingIndexInSourceList(final int i) {
        return permutation[i];
    }

    @Override
    public String toString() {

        StringBuilder builder = new StringBuilder();

        builder.append("Permutation=[");
        builder.append(permutation[0]);

        for (int i = 1; i < permutation.length; i++) {

            builder.append(",");
            builder.append(permutation[i]);
        }
        builder.append("]");

        return builder.toString();
    }

	private static void validateInput(final int[] permutation) {

		if ((permutation == null) || (permutation.length == 0)) {
			throw new IllegalArgumentException("The permutation must be initialized and contain at least 1 element");
		}

		List<Integer> permutationAsList = Arrays.stream(permutation).boxed().collect(Collectors.toList());

		final TreeSet<Integer> uniqueIndexes = new TreeSet<>(permutationAsList);

		if (permutation.length != uniqueIndexes.size()) {
			throw new IllegalArgumentException("The given permutation contains repeated elements");
		}

		if ((uniqueIndexes.first() != 0) || (uniqueIndexes.last() != (permutation.length - 1))) {
			throw new IllegalArgumentException(
					"The given permutation contains values which are outside the valid range");
		}
    }
}
