package cz.fidentis.analyst.visitors.mesh;

import cz.fidentis.analyst.mesh.MeshVisitor;
import cz.fidentis.analyst.mesh.core.MeshFacet;
import cz.fidentis.analyst.mesh.core.MeshPoint;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.vecmath.Point3d;

/**
 * This visitor computes priorities of vertices of a mesh facet according to their absolute distance from
 * the given {@link PrioritySphere#sphereCenterPosition} point.
 * <ul>
 *   <li>1 is maximal priority; 0 is minimal priority</li>
 *   <li>The closer a vertex is to {@link PrioritySphere#sphereCenterPosition},
 *       the higher its priority is.</li>
 *   <li>Vertices that are further than {@link PrioritySphere#sphereRadius}
 *       from {@link PrioritySphere#sphereCenterPosition} have priority equal to 0.</li>
 * </ul>
 * <p>
 *   The visitor returns all mesh facets together with priorities of all their vertices.
 * </p>
 * <p>
 *   This visitor is thread-safe.
 * </p>
 * 
 * @author Daniel Schramm
 */
public class PrioritySphere extends MeshVisitor  {
    
    private final Point3d sphereCenterPosition;
    private final double sphereRadius;
    
    private final Map<MeshFacet, List<Double>> priorities = new HashMap<>();

    /**
     * Constructor.
     * 
     * @param sphereCenterPosition Position of the center of the sphere in which
     *                             the priorities will be calculated
     * @param sphereRadius Radius of the sphere (must be greater than or equal to 0)
     */
    public PrioritySphere(Point3d sphereCenterPosition, double sphereRadius) {
        if (sphereCenterPosition == null) {
            throw new IllegalArgumentException("sphereCenterPosition");
        }
        this.sphereCenterPosition = sphereCenterPosition;
        if (sphereRadius < 0) {
            throw new IllegalArgumentException("sphereRadius");
        }
        this.sphereRadius = sphereRadius;
    }

    /**
     * Returns a map of visited mesh facets together with the list of priorities of their vertices.
     * 
     * The order of priorities corresponds to the order of vertices
     * in the visited facet, i.e., the i-th priority is a priority of the i-th vertex
     * of the visited facet.
     * 
     * @return map of visited mesh facets and priorities of their vertices
     */
    public Map<MeshFacet, List<Double>> getPriorities() {
        return Collections.unmodifiableMap(priorities);
    }

    @Override
    public void visitMeshFacet(MeshFacet facet) {
        final List<MeshPoint> vertices = facet.getVertices();
        
        final List<Double> prioritiesList = new ArrayList<>(vertices.size());
        
        for (int i = 0; i < vertices.size(); i++) { // for each vertex of visited facet
            final double distance = sphereCenterPosition.distance(vertices.get(i).getPosition());
            final double priority;
            if (distance > sphereRadius) {
                priority = 0;
            } else {
                priority = 1 - distance / sphereRadius;
            }
            
            prioritiesList.add(priority);
        }
        
        synchronized(this) {
            priorities.put(facet, prioritiesList);
        }
    }
}
