package cz.fidentis.analyst.tests;

import cz.fidentis.analyst.face.HumanFace;
import cz.fidentis.analyst.icp.Icp;
import cz.fidentis.analyst.kdtree.KdTree;
import cz.fidentis.analyst.mesh.core.MeshFacet;
import cz.fidentis.analyst.symmetry.Config;
import cz.fidentis.analyst.symmetry.Plane;
import cz.fidentis.analyst.symmetry.SignificantPoints;
import cz.fidentis.analyst.symmetry.SymmetryEstimator;
import cz.fidentis.analyst.visitors.mesh.HausdorffDistance;
import cz.fidentis.analyst.visitors.mesh.HausdorffDistance.Strategy;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * A class for testing the efficiency of algorithms.
 * 
 * @author Radek Oslejsek
 */
public class EfficiencyTests {
    
    private static File girlFile = new File("src/test/resources/cz/fidentis/analyst/average_girl_17-20.obj");
    private static File boyFile = new File("src/test/resources/cz/fidentis/analyst/average_boy_17-20.obj");
    private static File faceFile2 = new File("src/test/resources/cz/fidentis/analyst/00002_01_ECA.obj");
    private static File faceFile4 = new File("src/test/resources/cz/fidentis/analyst/00004_01_ECA.obj");
    private static File basicFaceFile = new File("src/test/resources/cz/fidentis/analyst/basic-model-04.obj");
    
    private static HumanFace face1;
    private static HumanFace face2;
    
    /**
     * Main method 
     * @param args Input arguments 
     * @throws IOException on IO error
     */
    public static void main(String[] args) throws IOException {
        face1 = new HumanFace(boyFile);
        face2 = new HumanFace(faceFile4);
        
        face1.getMeshModel().simplifyModel();
        face2.getMeshModel().simplifyModel();
        
        boolean relativeDist = false;
        boolean printDetails = false;
        
        //face1.getMeshModel().compute(new GaussianCurvature()); // initialize everything, then measure
        
        System.out.println("ICP:");
        Icp icp = new Icp();
        icp.getTransformedFacet(face1.getMeshModel().getFacets().get(0), face2.getMeshModel().getFacets().get(0));
        
        System.out.println("Symmetry plane calculation:");
        printSymmetryPlane(face1, true, SignificantPoints.CurvatureAlg.MEAN);
        printSymmetryPlane(face1, true, SignificantPoints.CurvatureAlg.GAUSSIAN);
        printSymmetryPlane(face1, false, SignificantPoints.CurvatureAlg.MEAN);
        printSymmetryPlane(face1, false, SignificantPoints.CurvatureAlg.GAUSSIAN);
        
        System.out.println();
        System.out.println(measureKdTreeCreation(face1) + "\tmsec:\tKd-tree creation of first face");
        System.out.println(measureKdTreeCreation(face2) + "\tmsec:\tKd-tree creation of second face");
        
        System.out.println();
        System.out.println("Hausdorff distance sequentially:");
        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false), printDetails);
        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, false), printDetails);
        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false), printDetails);
        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, false), printDetails);

        System.out.println();
        System.out.println("Hausdorff distance concurrently:");
        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, true), printDetails);
        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, true), printDetails);
        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, true), printDetails);
    }
    
    protected static void testAndPrint(HumanFace face, HausdorffDistance vis, boolean printDetails) {
        long startTime = System.currentTimeMillis();
        face.getMeshModel().compute(vis);
        long endTime = System.currentTimeMillis();
        
        System.out.println(
                (endTime-startTime) + 
                "\tmsec: " + 
                //"\t" + (vis.inParallel() ? "concurrently" : "sequentially") +
                "\t" + (vis.relativeDistance()? "relative" : "absolute") +
                "\t" + vis.getStrategy()
        );
        
        if (printDetails) {
            Map<MeshFacet, List<Double>> results = vis.getDistances();
            for (MeshFacet facet: results.keySet()) {
                System.out.println(results.get(facet));
            }
        }
    }
    
    private static long measureKdTreeCreation(HumanFace face) {
        long startTime = System.currentTimeMillis();
        KdTree kdTree = new KdTree(face.getMeshModel().getFacets());
        return System.currentTimeMillis() - startTime;
    }
    
    private static void printSymmetryPlane(HumanFace face, boolean concurrently, SignificantPoints.CurvatureAlg curvatureAlg) {
        long startTime = System.currentTimeMillis();
        SymmetryEstimator est = new SymmetryEstimator(Config.getDefault(), curvatureAlg);
        face.getMeshModel().compute(est, false);
        Plane plane = est.getSymmetryPlane(concurrently);
        long endTime =  System.currentTimeMillis();
        
        System.out.println(
                (endTime-startTime) + 
                "\tmsec: " + 
                "\t" + (concurrently ? "concurrently" : "sequentially") +
                "\t" + curvatureAlg + " curvature    " +
                "\t" + plane.getNormal() + ", " + plane.getDistance()
        );
    }
    
}
