package cz.fidentis.analyst.face.events;

import cz.fidentis.analyst.face.HumanFace;
import cz.fidentis.analyst.mesh.core.MeshFacet;
import cz.fidentis.analyst.visitors.face.HausdorffDistancePrioritized;
import java.util.DoubleSummaryStatistics;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * New Hausdorff distance has been computed.
 * 
 * @author Radek Oslejsek
 */
public class HausdorffDistanceComputed extends HumanFaceEvent {
    
    private final HumanFace targetFace;
    private final HausdorffDistancePrioritized hdVisitor;
    
    /**
     * Constructor.
     * @param sourceFace Human face from which the HD has been computed
     * @param targetFace Human face towards which the HD has been computed
     * @param hdVisitor Visitor with HD results
     * @param name Event name provided by issuer
     * @param issuer The issuer
     */
    public HausdorffDistanceComputed(
            HumanFace sourceFace, 
            HumanFace targetFace, 
            HausdorffDistancePrioritized hdVisitor,
            String name,
            Object issuer) {
        
        super(sourceFace, name, issuer);
        this.targetFace = targetFace;
        this.hdVisitor = hdVisitor;
    }
    
    /**
     * Returns human face from which the HD has been computed. This method
     * is identical to {@link #getFace()}
     * 
     * @return human face from which the HD has been computed.
     */
    public HumanFace getSourceFace() {
        return getFace();
    }

    /**
     * Returns human face towards which the HD has been computed.
     * 
     * @return human face towards which the HD has been computed.
     */
    public HumanFace getTargetFace() {
        return targetFace;
    }

    /**
     * Returns a visitor object storing the results of HD calculations.
     * @return a visitor object storing the results of HD calculations.
     */
    public HausdorffDistancePrioritized getHdVisitor() {
        return hdVisitor;
    }
    
    /**
     * Returns statistics of (standard) Hausdorff distance.
     * @return statistics of (standard) Hausdorff distance.
     */
    public DoubleSummaryStatistics getHusdorffDistStats() {
        return hdVisitor.getDistances()
                .values()
                .stream()
                .flatMap(List::stream)
                .mapToDouble(Double::doubleValue)
                .filter(v -> Double.isFinite(v))
                .summaryStatistics();
    }
    
    /**
     * Returns statistics of weighted Hausdorff distance.
     * @return statistics of weighted Hausdorff distance.
     */
    public DoubleSummaryStatistics getWeightedHusdorffDistStats() {
        final Map<MeshFacet, List<Double>> weightedDistances = new HashMap<>(hdVisitor.getDistances());
        final Map<MeshFacet, List<Double>> mergedPriorities = hdVisitor.getMergedPriorities().get(getFace());

        // Merge the map of distances with the map of priorities
        for (final Map.Entry<MeshFacet, List<Double>> facetPriorities: mergedPriorities.entrySet()) {
            weightedDistances.merge(
                    facetPriorities.getKey(),
                    facetPriorities.getValue(),
                    (distancesList, prioritiesList) ->
                            IntStream.range(0, distancesList.size())
                                    .mapToDouble(i -> distancesList.get(i) * prioritiesList.get(i))
                                    .boxed()
                                    .collect(Collectors.toList()));
        }
        
        return weightedDistances
                .values()
                .stream()
                .flatMap(List::stream)
                .mapToDouble(Double::doubleValue)
                .filter(v -> Double.isFinite(v))
                .summaryStatistics();
    }
    
}
