//#####################################################################
// Copyright 2003-2009, Ron Fedkiw, Eran Guendelman, Geoffrey Irving, Sergey Koltakov, Nipun Kwatra, Duc Nguyen, Andrew Selle.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class SEGMENTED_CURVE
//##################################################################### 
#ifndef __SEGMENTED_CURVE__
#define __SEGMENTED_CURVE__

#include <Core/Basic_Geometry/BASIC_GEOMETRY_POLICY.h>
#include <Core/Basic_Geometry/SEGMENT_1D.h>
#include <Core/Log/LOG.h>
#include <Core/Meshes/MESH_OBJECT_BASE.h>
#include <Core/Meshes/SEGMENT_MESH.h>
#include <Core/Spatial_Acceleration/SPATIAL_ACCELERATION_FORWARD.h>
#include <Core/Vectors/VECTOR.h>
namespace PhysBAM{

template<class TV> class GRID;

template<class TV>
class SEGMENTED_CURVE_BASE:public MESH_OBJECT_BASE<TV,SEGMENT_MESH>
{
    typedef typename TV::SCALAR T;
    typedef typename BASIC_GEOMETRY_POLICY<TV>::SEGMENT T_SEGMENT;
public:
    typedef MESH_OBJECT_BASE<TV,SEGMENT_MESH> BASE;typedef MESH_HIERARCHY<TV,SEGMENT_MESH> T_HIERARCHY;
    using BASE::mesh;using BASE::particles;using BASE::bounding_box;
    using BASE::Update_Bounding_Box;using BASE::hierarchy;

    ARRAY<T_SEGMENT>* segment_list;
    POINT_SIMPLICES_1D<T>* point_simplices_1d;

    SEGMENTED_CURVE_BASE();
    SEGMENTED_CURVE_BASE(SEGMENT_MESH& segment_mesh_input,GEOMETRY_PARTICLES<TV>& particles_input);
    virtual ~SEGMENTED_CURVE_BASE();

    T_SEGMENT Get_Element(const int aggregate_id) const
    {return T_SEGMENT(particles.X(mesh.elements(aggregate_id)(0)),particles.X(mesh.elements(aggregate_id)(1)));}

    void Rescale(const T scaling_factor) override
    {Rescale(scaling_factor*TV::All_Ones_Vector());}

    SEGMENT_MESH& Get_Segment_Mesh()
    {return mesh;}

    virtual std::string Name() const override {return Static_Name();}
    static std::string Static_Name()
    {return LOG::sprintf("SIMPLICIAL_OBJECT<T,VECTOR<T,%d>,1>",TV::m);}

    virtual std::string Extension() const override {return Static_Extension();}
    static std::string Static_Extension()
    {return TV::m==2?"curve2d":(TV::m==1?"curve1d":"curve");}

    TV Closest_Point_On_Boundary(const TV& location,const T max_depth=0,const T thickness_over_2=0,int* closest_segment=0,T* distance=0) const
    {return Closest_Point_On_Curve(location,thickness_over_2,closest_segment,distance);} // max_depth currently unused

    T Total_Size() const
    {return Total_Length(); }

    T Size(const int segment) const
    {int node1=mesh.elements(segment)(0),node2=mesh.elements(segment)(1);
    return (particles.X(node1)-particles.X(node2)).Magnitude();}

    T Signed_Size(const int segment) const
    {return Size(segment);}

//#####################################################################
    POINT_SIMPLICES_1D<T>& Get_Boundary_Object();
    void Clean_Memory() override;
    void Update_Segment_List();
    void Initialize_Straight_Mesh_And_Particles(const GRID<VECTOR<T,1> >& grid);
    void Initialize_Circle_Mesh_And_Particles(const int m,const T radius);
    TV Closest_Point_On_Curve(const TV& location,T thickness_over_two=0,int* closest_segment=0,T* distance=0) const;
    void Rescale(const TV& scaling);
    T Total_Length() const;
    bool Inside_Any_Simplex(const TV& location,int& segment_id,const T thickness_over_two) const;
private:
    void Refresh_Auxiliary_Structures_Helper() override;
//#####################################################################
};

template<class TV>
class MESH_OBJECT<TV,1>:public SEGMENTED_CURVE_BASE<TV>
{
public:
    MESH_OBJECT() = default;
    MESH_OBJECT(SEGMENT_MESH& segment_mesh_input,GEOMETRY_PARTICLES<TV>& particles_input)
        :SEGMENTED_CURVE_BASE<TV>(segment_mesh_input,particles_input)
    {
    }
    virtual ~MESH_OBJECT() = default;
};

template<class T>
class MESH_OBJECT<VECTOR<T,2>,1>:public SEGMENTED_CURVE_BASE<VECTOR<T,2> >
{
    typedef VECTOR<T,2> TV;
public:
    typedef SEGMENTED_CURVE_BASE<TV> BASE;
    using BASE::mesh;using BASE::particles;using BASE::segment_list;using BASE::bounding_box;using BASE::Update_Bounding_Box;using BASE::hierarchy;
    using BASE::Get_Element;using BASE::Closest_Point_On_Curve;using BASE::Total_Length;using BASE::Initialize_Hierarchy;

    MESH_OBJECT();
    MESH_OBJECT(SEGMENT_MESH& segment_mesh_input,GEOMETRY_PARTICLES<TV>& particles_input);

    bool Inside_Any_Simplex(const TV& point,int& segment_id,const T thickness_over_two=0) const
    {return Inside_Any_Segment(point,segment_id,thickness_over_two);}

    TV Normal(const TV& location,const int aggregate) const
    {return Normal(aggregate);}

//#####################################################################
    T Calculate_Signed_Distance(const TV& location,T thickness_over_two=0) const;
    bool Boundary(const TV& location,T thickness_over_two=0) const;
    bool Inside(const TV& location,T thickness_over_two=0) const;
    bool Outside(const TV& location,T thickness_over_two=0) const;
    bool Inside_Any_Segment(const TV& point,int& segment_id,const T thickness_over_two=0) const;
    bool Segment_Segment_Intersection(const SEGMENT_MESH& test_segment_mesh,ARRAY_VIEW<const TV> X,const T thickness_over_2=0,const bool update_bounding_boxes=true,
        ARRAY<VECTOR<int,2> >* intersecting_segment_segment_pairs=0);
    bool Find_First_Segment_Segment_Intersection(const SEGMENT_MESH& test_segment_mesh,ARRAY_VIEW<const TV> X,const T thickness_over_2,const int max_coarsening_attempts=5,
        const bool update_bounding_boxes=true);
    void Get_Segments_Near_Segments(ARRAY<ARRAY<int> >& segments_near_segments,const SEGMENT_MESH& test_segment_mesh,ARRAY_VIEW<const TV> X,const T thickness_over_2,
        const bool update_bounding_boxes);
    TV Normal(const int aggregate) const;
    void Get_Vertex_Normals(ARRAY<VECTOR<T,2> >& normals) const;
//#####################################################################
};
template<class T> class RAY_HIT;
template<class TV> class RAY;

template<class T> RAY_HIT<T> Closest_Non_Intersecting_Point(
    const RAY<VECTOR<T,2> >& ray,const SEGMENTED_CURVE<VECTOR<T,2> >& curve,
    const T thickness_over_two=0);
}
#endif
