package cz.fidentis.analyst.symmetry;

/**
 * Representation of configuration for symmetry estimate.
 * Default numbers are given due to the best results on tested data.
 * On many different 3D models, it exists other values of config that will have 
 * better impact on results in estimate of symmetry. 
 * 
 * @author Natalia Bebjakova
 */
public class SymmetryConfig {
    
    private static final double DEFAULT_MIN_CURV_RATIO = 0.5;
    private static final double DEFAULT_MIN_ANGLE_COS = 0.985;
    private static final double DEFAULT_MIN_NORM_ANGLE_COS = 0.985;
    private static final double DEFAULT_MAX_REL_DISTANCE = 1.0 / 100.0;
    private static final int DEFAULT_SIGNIFICANT_POINT_COUNT = 200;
    private static final boolean DEFAULT_AVERAGING = true;
    private static final CurvatureAlg DEFAULT_CURVATURE_ALGORITHM = CurvatureAlg.GAUSSIAN;
    
    private double minCurvRatio;
    private double minAngleCos;
    private double minNormAngleCos;
    private double maxRelDistance;
    private int significantPointCount;
    private boolean averaging;
    private CurvatureAlg curvatureAlg;
    
    /**
     * Creates configuration with default values 
     */
    public SymmetryConfig() {
        minCurvRatio = DEFAULT_MIN_CURV_RATIO;
        minAngleCos = DEFAULT_MIN_ANGLE_COS;
        minNormAngleCos = DEFAULT_MIN_NORM_ANGLE_COS;
        maxRelDistance = DEFAULT_MAX_REL_DISTANCE;
        significantPointCount = DEFAULT_SIGNIFICANT_POINT_COUNT;
        averaging = DEFAULT_AVERAGING;
        curvatureAlg = DEFAULT_CURVATURE_ALGORITHM;
    }
    
    /**
     * Copy constructor.
     * 
     * @param conf Original configuration
     */
    public SymmetryConfig(SymmetryConfig conf) {
        copy(conf);
    }
    
    /**
     * Copies values from another configuration object.
     * 
     * @param conf Another configuration
     */
    public void copy(SymmetryConfig conf) {
        minCurvRatio = conf.minCurvRatio;
        minAngleCos = conf.minAngleCos;
        minNormAngleCos = conf.minNormAngleCos;
        maxRelDistance = conf.maxRelDistance;
        significantPointCount = conf.significantPointCount;
        averaging = conf.averaging;
        curvatureAlg = conf.curvatureAlg;
    }
    
    /**
     * Parameter which denotes how similar the Gaussian curvatures in the two vertices
     * must be for this criteria to be satisfied.
     * The higher the value is the more similar they must be.
     * 
     * @return minimal similarity of curvatures to satisfy the criteria  
     */
    public double getMinCurvRatio() {
        return minCurvRatio;
    }

    /**
     * 
     * @param minCurvRatio new minimal similarity of curvatures to satisfy the criteria  
     */
    public void setMinCurvRatio(double minCurvRatio) {
        this.minCurvRatio = minCurvRatio;
    }

    /**
     * MinAngleCos ∈ (0,1)
     * It is the angle between the vector (xk − xl) and the normal vector nij of the plane ρij
     * (which is the vector (xi −xj))
     * Returns parameter which denotes how large the angle αij can be for this criteria to be satisfied.
     * 
     * @return minimal angle satisfy criteria 
     */
    public double getMinAngleCos() {
        return minAngleCos;
    }

    /**
     * 
     * @param minAngleCos new minimal angle to satisfy the criteria 
     */
    public void setMinAngleCos(double minAngleCos) {
        this.minAngleCos = minAngleCos;
    }

    /**
     * MinNormAngleCos ∈ (0,1)
     * It is angle between vectors (xi − xj) and (ni − nj), where xi, xj are vertices and ni, nj its normals
     * Returns parameter which denotes how large the angle αij can be for this criteria to be satisfied.
     * 
     * @return minimal angle to satisfy criteria 
     */
    public double getMinNormAngleCos() {
        return minNormAngleCos;
    }

    /**
     * 
     * @param minNormAngleCos new minimal angle to satisfy the criteria 
     */
    public void setMinNormAngleCos(double minNormAngleCos) {
        this.minNormAngleCos = minNormAngleCos;
    }

    /**
     * Parameter which denotes how far (relatively to the length of the bounding box diagonal)
     * the middle point of the two vertices can be from the plane in order to satisfy this criteria.
     * 
     * @return relative distance
     */
    public double getMaxRelDistance() {
        return maxRelDistance;
    }

    /**
     * 
     * @param maxRelDistance new relative distance 
     */
    public void setMaxRelDistance(double maxRelDistance) {
        this.maxRelDistance = maxRelDistance;
    }

    /**
     * Returns number of vertices with the highest Gaussian curvature.
     * 
     * @return number of significant points for computing the symmetry 
     */
    public int getSignificantPointCount() {
        return significantPointCount;
    }

    /**
     * 
     * @param significantPointCount new number of significant points for computing the symmetry 
     */
    public void setSignificantPointCount(int significantPointCount) {
        this.significantPointCount = significantPointCount;
    }

    /**
     * If there are more planes with the same highest number of votes while computing symmetry,
     * we can average them all together. 
     * Returns parameter that decides whether to average them or not.
     * 
     * @return true if planes will be averaged 
     */
    public boolean isAveraging() {
        return averaging;
    }

    /**
     * 
     * @param averaging new averaging flag  
     */
    public void setAveraging(boolean averaging) {
        this.averaging = averaging;
    }
    
    /**
     * Returns curvature algorithm.
     * @return curvature algorithm 
     */
    public CurvatureAlg getCurvatureAlg() {
        return this.curvatureAlg;
    }
    
    /**
     * Sets  curvature algorithm.
     * @param alg curvature algorithm
     */
    public void setCurvatureAlg(CurvatureAlg alg) {
        this.curvatureAlg = alg;
    }

    /**
     * 
     * @return String representation of configuration
     */
    @Override 
    public String toString() {
        String str = "PARAMETERS: ";
        str += "\n";
        str += "Min curvature ratio: " + minCurvRatio + "\n";
        str += "Min angle cosine: " + minAngleCos + "\n";
        str += "Min norm angle cosine: " + minNormAngleCos + "\n";
        str += "Max relative distance: " + maxRelDistance + "\n";
        str += "Significant points: " + significantPointCount + "\n";
        str += "Averaging: " + averaging + "\n";
        str += "Curvature: " + curvatureAlg + "\n";
        return str;
    }
}