/**
 * C++ fast marching on point cloud Library
 * Copyright (c) 2000-2026 Eric Bechet <eric at bechet dot ca>
 * 
 * This file is part of the C++ Mesh Generation Library.
 *  See the NOTICE & LICENSE files for conditions.
 * 
 * Version 2.0 (March 2025)
 */

 #ifndef FM2PC_H
 #define FM2PC_H
 
 #include "FMPCAlgo.h"

#include "nutil.h"
#include <cmath>

//  #define DBUG
 
 namespace FM
 {
 
 class FM2PC : public FMPCAlgo12
 {
    protected :

    std::vector<npoint3> normals;

    std::vector<npoint3> approxNormals;

    // double computeDataMain(int& idx1, int& idx2, int& idx3, npoint3& grad, npoint3*& norm) override;

    // double computeDataTrig(int& idx1, int& idx2, int& idx3, npoint3& grad, npoint3& norm);

    // double computeDataTrig(int& idx1, int& idx2, npoint3& pt3, npoint3& grad, npoint3& norm);

    double computeDataTrig(int idx1, int idx2, int idx3, resultComputation& result, double* dTp = nullptr, int* state=nullptr);

    npoint3 getNormal2pts(int& idx1, int& idx2) override;

    npoint3 getNormal3pts(int& idx1, int& idx2, npoint3& pt3);

    npoint3 getNormal3pts(int& idx1, int& idx2, int& idx3)
    { npoint3 pt3; PC->getNode(idx3,pt3); return getNormal3pts(idx1,idx2,pt3); }

    // npoint3 rotate2D(npoint3 &pt1, npoint3 &pt2, npoint3 &norm);

    /// @brief 
    /// @param idx1 
    /// @param v1 
    /// @param v2 
    /// @param grad New gradient (rotated correctly)
    /// @return Normal
    //npoint3 rotateGradient(int& idx1, npoint3& v1, npoint3& v2, npoint3& grad);

    void rotateGradient(int& idx1, npoint3& grad1, npoint3& normTri);

    //void setNormalsDirection(std::vector<npoint3>& norms, std::vector<int>& idxs);

    // double distanceCorrection2(int idx1, int idx2, int idx3, npoint3 grad, double dist);

    void estimateError(std::vector<resultComputation>& dataTrigs);

    bool validSelection(int iRef, resultComputation& pCand) override;

    double valCylinder(double x, double y, double z){
        double dDist = sqrt((x-1)*(x-1) + y*y);
	    double theta = (dDist*dDist/2 < 1e-3) ? 2.*asin(dDist/2): acos(1-dDist*dDist/2);

	    return sqrt(theta*theta + z*z);
    }

    bool updatable(int idx1, int idx3) override;

    public :

    /// @brief Build FM0 solver
    /// @param PC_ Point cloud
    /// @param data_ Data field (to fill)
    /// @param correctCurv Enable correction for the surface curvature
    FM2PC(FMPointCloud* PC_, FMPCField* data_, bool correctCurv = false) : FMPCAlgo12(PC_,data_,correctCurv) {
        // fixing node already computed
        FMPCAlgo::preFixData(data_,1);
        data_->setOrder(2);
        normals.resize(PC_->getNbrNodes());
        approxNormals.resize(PC_->getNbrNodes());
        PC->computeApproximatedNormals(approxNormals);
    }

    /* INITIALISATION FUNCTIONS */

    void setNormals(std::vector<int>& idxs, npoint3 pt0) override;

    void setNormals(std::vector<int>& idxs) override;

    void setNormals(std::map<int,double>& pts, npoint3& normVec) override;

    /* PROPAGATION FUNCTIONS */

    void setInfoPtNB(int& idx, resultComputation& infos) override
    { FMPCAlgo12::setInfoPtNB(idx,infos); normals[idx] = infos.normal;}

    // void categorizeTriangle(int& idx3, int& idx1, std::vector<int>& idxs, std::vector<int>& inside, std::vector<int>& outside) override;

    void categorizeTriangle(int& idx3, int& idx1, std::vector<int>& idxs, std::vector<std::pair<double,int>>& candidates) override;

    resultComputation computeTriangles(int& idx1, int& idx3, std::vector<int>& sndPoints) override;

    bool isFMpossible(int& idx1, int& idx2, int& idx3) override;

    /* INTERPOLATION FUNCTIONS */

    // double interpolate(npoint3& pt){ return FMPCAlgo12::interpolate(pt); }

    /// @brief Interpolate point from computed data according to algorithm used
    /// @param pt Point whose distance msut be computed
    /// @return Distance interpolated at point pt
    // double interpolate(npoint3& pt, npoint3& grad) override;

    double computeInterpolation(int& idx1, int& idx2, npoint3& pt, npoint3& grad) override;

    /* DEBUG FUNCTIONS */

    void printNormals() const;

    void displayApproximatedNormals(data_container &data, bool dispNode = true) const;

    void displayComputedNormals(data_container &data, bool dispNode = true) const;

    void displayNormals(data_container &data) const;

    void displayFrontCurvature(data_container &data) const;
    
 };
 }
 #endif // FM2PC_H