//
//  courbures.cpp
//  code_courb
//
//  Created by Bac Alexandra on 07/04/2022.
//

#include <fstream>
#include "courbures.h"
#include "mesh_functions.hpp"
#include "distrib.hpp"


// ////////////////////////////////////////////////////////////////////
// MyMesh_Norm
// ////////////////////////////////////////////////////////////////////

MyMesh_Norm::MyMesh_Norm(MyMesh &m) : MyMesh(m), vnormal(*(new Mesh::Property_map<vertex_descriptor,K::Vector_3>)), vcolor(*(new Mesh::Property_map<vertex_descriptor,CGAL::Color>))
{
	// Allocation de vnormal et initialisation
	bool created_vnorm;
	boost::tie(vnormal, created_vnorm) = _m.add_property_map<vertex_descriptor,K::Vector_3>("v:norm",K::Vector_3(0.,0.,0.));
	assert(created_vnorm);
	normales_locales() ;
    
    // Allocation de vcolor
    bool created_vcol;
    boost::tie(vcolor, created_vcol) = _m.add_property_map<vertex_descriptor,CGAL::Color>("v:color",CGAL::white());
    assert(created_vcol);
}

MyMesh_Norm::MyMesh_Norm(Mesh &m) : MyMesh(m), vnormal(*(new Mesh::Property_map<vertex_descriptor,K::Vector_3>)), vcolor(*(new Mesh::Property_map<vertex_descriptor,CGAL::Color>))
{
    // Allocation de vnormal et initialisation
    bool created_vnorm;
    boost::tie(vnormal, created_vnorm) = _m.add_property_map<vertex_descriptor,K::Vector_3>("v:norm",K::Vector_3(0.,0.,0.));
    assert(created_vnorm);
    normales_locales() ;
    
    // Allocation de vcolor
    bool created_vcol;
    boost::tie(vcolor, created_vcol) = _m.add_property_map<vertex_descriptor,CGAL::Color>("v:color",CGAL::white());
    assert(created_vcol);
    
    // Init local normals
    normales_locales() ;
}

MyMesh_Norm::~MyMesh_Norm() {
    std::optional<Mesh::Property_map<vertex_descriptor,K::Vector_3> > res_norm = _m.property_map<vertex_descriptor,K::Vector_3>("v:norm");
    if (res_norm.has_value()) // the property exists
    {
        _m.remove_property_map(vnormal) ;
        delete &vnormal ;
    }
    else
        cerr << "normal property already removed" << endl ;
}

void MyMesh_Norm::normales_locales()
{
	Mesh::Vertex_range vr = _m.vertices() ;
	for (Mesh::Vertex_range::iterator v_it = vr.begin() ; v_it != vr.end(); ++v_it)
	{
		vnormal[*v_it] = compute_normal(*v_it, _m) ;
	}
}

void MyMesh_Norm::set_fixed_colors()
{
    Mesh::Vertex_range vr = _m.vertices() ;

    int i = 0 ;
    for (Mesh::Vertex_range::iterator v_it = vr.begin(); v_it != vr.end(); ++v_it)
    {
        i++ ;
        if (i % 2)
            vcolor[*v_it] = CGAL::green() ;
        else
            vcolor[*v_it] = CGAL::blue() ;
    }
}

void MyMesh_Norm::output_mesh(const string &filename)
{
    CGAL::IO::write_polygon_mesh(filename, _m, CGAL::parameters::vertex_color_map(vcolor)) ;
}

// ////////////////////////////////////////////////////////////////////
// Tools
// ////////////////////////////////////////////////////////////////////

std::vector<Mesh::Vertex_index> get_two_vneighborhood(Mesh::Vertex_index v, Mesh &m)
{
	// Ajout d'un flag booléen
	Mesh::Property_map<vertex_descriptor,bool> vflag;
	bool created_vflag;
	boost::tie(vflag, created_vflag) = m.add_property_map<vertex_descriptor,bool>("v:flag",false);
	assert(created_vflag);
	
	// Circulateur sur le premier cercle
	std::vector<Mesh::Vertex_index> neigh = first_vneighbors(v, m), neigh2 ;

	// Parcours du premier cercle et ajout du second cercle par circulateurs
	for (int i=0; i<neigh.size(); i++)
	{
		CGAL::Vertex_around_target_circulator<Mesh> v_it(m.halfedge(neigh.at(i)),m), v_end(v_it) ;
		do
		{
			if (!vflag[*v_it]) // sommet non encore rencontré
			neigh2.push_back(*v_it) ; // ajout du point à la liste
			vflag[*v_it] = true ;
			++v_it ;
		} while (v_it != v_end) ;
	}

	// Concaténation des deux cercles
	neigh.insert(neigh.end(), neigh2.begin(), neigh2.end()) ;

	m.remove_property_map(vflag) ;

	return neigh ;
}

K::Vector_3 compute_normal(Mesh::Vertex_index v, Mesh &m)
{
	K::Vector_3 n(0.,0.,0.) ;

	int i = 0 ;

    if (!(m.halfedge(v) == Mesh::null_halfedge())) // otherwise v is an isolated point
    {
        CGAL::Halfedge_around_target_circulator<Mesh> he_it(m.halfedge(v),m), he_end(he_it) ;
        do {
            if (!m.is_border(*he_it))
            {
                Mesh::Face_index face(m.face(*he_it));
                i++ ;
                Mesh::Halfedge_index fhe = m.halfedge(face) ;
                K::Point_3 p1(m.point(m.target(fhe))), p2, p3 ;
                p2 =m.point(m.target(m.next(fhe))) ;
                p3 =m.point(m.target(m.opposite(fhe))) ;
                
                n += CGAL::normal(p1, p2, p3) ;
            }
            ++he_it ;
        } while (he_it != he_end) ;
        
        if (i!=0)
        {
            n /= i ;
            n = n / sqrt(n.squared_length()) ;
        }
    }
	return -n ; // Normals point out
}
