package cz.fidentis.analyst.icp;

import cz.fidentis.analyst.mesh.core.MeshPoint;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * Random undersampling when the mesh vertices to be removed are selected randomly.
 * 
 * @author Radek Oslejsek
 */
public class RandomStrategy extends UndersamplingStrategy {
    
    /**
     * Constructor for PERCENTAGE undersampling type.
     * 
     * @param perc Percentage - a value in (0.0, 1.0&gt;
     * @throws IllegalArgumentException if the input parameter is wrong
     */
    public RandomStrategy(double perc) {
        super(perc);
    }
    
    /**
     * Constructor for PERCENTAGE undersampling type.
     * 
     * @param max Maximal number of vertices. Must be bigger than zero
     * @throws IllegalArgumentException if the input parameter is wrong
     */
    public RandomStrategy(int max) {
        super(max);
    }

    @Override
    public List<MeshPoint> reduceMeshVertices(List<MeshPoint> meshPoints) {
        if (meshPoints == null) {
            return null;
        }
        
        int numVertices = getNumUndersampledVertices(meshPoints.size());
        
        if (meshPoints.size() == numVertices) {
            return meshPoints;
        }
        
        // generate randomly ordered indexes:
        List<Integer> range = IntStream.range(0, meshPoints.size()).boxed().collect(Collectors.toCollection(ArrayList::new));
        Collections.shuffle(range);
        
        if (numVertices < meshPoints.size() / 2) { // copy indices
            MeshPoint[] array = new MeshPoint[meshPoints.size()];
            range.stream().limit(numVertices).forEach(
                    i -> array[i] = meshPoints.get(i)
            );
            return Arrays.stream(array).filter(
                    p -> p != null
            ).collect(Collectors.<MeshPoint>toList());
        } else { // remove indices
            List<MeshPoint> copy = new ArrayList<>(meshPoints);
            range.stream().limit(meshPoints.size() - numVertices).forEach(
                    i -> copy.set(i, null)
            );
            return copy.parallelStream().filter(
                    p -> p != null
            ).collect(Collectors.<MeshPoint>toList());
        }
    }
    
    @Override
    public String toString() {
        return "random " + super.toString();
    }
}
