package cz.fidentis.analyst.visitors.mesh;

import cz.fidentis.analyst.mesh.core.CornerTableRow;
import cz.fidentis.analyst.mesh.core.MeshFacet;
import cz.fidentis.analyst.mesh.core.MeshFacetImpl;
import cz.fidentis.analyst.mesh.core.MeshModel;
import cz.fidentis.analyst.mesh.core.MeshPointImpl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;

public class PrioritySphereTest {
    
    private static final double DELTA = 1e-15;
    
    protected MeshFacet getTrivialFacet(int count, double xSpace, double ySpace, double zSpace, int offset) {
        final MeshFacet facet = new MeshFacetImpl();
        for (int i = offset; i < count + offset; i++) {
            facet.addVertex(new MeshPointImpl(new Point3d(xSpace * i, ySpace * i, zSpace * i),
                    new Vector3d(0, 0, 1),
                    new Vector3d()));
            facet.getCornerTable().addRow(new CornerTableRow(i - offset, -1));
        }

        return facet;
    }
    
    protected void performTest(Map<MeshFacet, List<Double>> facetsWithExpectedPriorities,
            PrioritySphere visitor,
            boolean concurrently) {

        final Set<MeshFacet> facets = facetsWithExpectedPriorities.keySet();
        
        MeshModel model = new MeshModel();
//        model.addFacets(new HashSet<>(facets));
        for (final MeshFacet facet: facets) {
            model.addFacet(facet);
        }
        
        model.compute(visitor, concurrently);
        
        final Map<MeshFacet, List<Double>> priorities = visitor.getPriorities();
        assertEquals(facetsWithExpectedPriorities.size(), priorities.size());
        
        for (final MeshFacet facet: facets) {
            assertTrue(priorities.containsKey(facet));
            
            final List<Double> expectedPriorities = facetsWithExpectedPriorities.get(facet);
            final List<Double> actualPriorities = priorities.get(facet);
            assertEquals(expectedPriorities.size(), actualPriorities.size());
            
            for (int i = 0; i < expectedPriorities.size(); i++) {
                assertEquals(expectedPriorities.get(i), actualPriorities.get(i), DELTA);
            }
        }
    }
    
    @Test
    public void variousPrioritiesTest() {
        final List<Double> priorities = List.of(1d, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0d, 0d);
        final Map<MeshFacet, List<Double>> expected = new HashMap<>();
        expected.put(getTrivialFacet(12, 1, 0, 0, 0), priorities);
        expected.put(getTrivialFacet(12, -1, 0, 0, 0), priorities);
        expected.put(getTrivialFacet(12, 0, 1, 0, 0), priorities);
        expected.put(getTrivialFacet(12, 0, -1, 0, 0), priorities);
        expected.put(getTrivialFacet(12, 0, 0, 1, 0), priorities);
        expected.put(getTrivialFacet(12, 0, 0, -1, 0), priorities);
        
        final PrioritySphere visitor = new PrioritySphere(new Point3d(0, 0, 0), 10);
        
        performTest(expected, visitor, false);
    }
    
    @Test
    public void allZeroPrioritiesTest() {
        final Map<MeshFacet, List<Double>> expected = new HashMap<>();
        expected.put(getTrivialFacet(1, 3, 0, 0, 1), List.of(0d));
        expected.put(getTrivialFacet(5, 0, 3, 0, 1), List.of(0d, 0d, 0d, 0d, 0d));
        expected.put(getTrivialFacet(3, -3, -3, -3, 1), List.of(0d, 0d, 0d));
        
        final PrioritySphere visitor = new PrioritySphere(new Point3d(0, 0, 0), 3);
        
        performTest(expected, visitor, false);
    }
    
    @Test
    public void noZeroPriorityTest() {
        final Map<MeshFacet, List<Double>> expected = new HashMap<>();
        expected.put(getTrivialFacet(1, 10, 0, 0, 1), List.of(0.9));
        expected.put(getTrivialFacet(5, 0, -20, 0, 0), List.of(1d, 0.8, 0.6, 0.4, 0.2));
        expected.put(getTrivialFacet(100, 0, 0, -1, 0), DoubleStream.iterate(1, d -> d > 0d, d -> d - 0.01)
                .boxed()
                .collect(Collectors.toList()));
        
        final PrioritySphere visitor = new PrioritySphere(new Point3d(0, 0, 0), 100);
        
        performTest(expected, visitor, false);
    }
    
    @Test
    public void concurrencyTest() {
        final List<Double> priorities = List.of(1d, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0d, 0d);
        final Map<MeshFacet, List<Double>> expected = new HashMap<>();
        expected.put(getTrivialFacet(12, 1, 0, 0, 0), priorities);
        expected.put(getTrivialFacet(12, -1, 0, 0, 0), priorities);
        expected.put(getTrivialFacet(12, 0, 1, 0, 0), priorities);
        expected.put(getTrivialFacet(12, 0, -1, 0, 0), priorities);
        expected.put(getTrivialFacet(12, 0, 0, 1, 0), priorities);
        expected.put(getTrivialFacet(12, 0, 0, -1, 0), priorities);
        
        final PrioritySphere visitor = new PrioritySphere(new Point3d(0, 0, 0), 10);
        
        performTest(expected, visitor, true);
    }
}
