//#####################################################################
// Copyright 2011.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class DEBUG_PARTICLES
//#####################################################################
#ifndef __DEBUG_PARTICLES__
#define __DEBUG_PARTICLES__

#include <Core/Arrays/ATTRIBUTE_ID.h>
#include <Core/Grids/GRID.h>
#include <Core/Read_Write/READ_WRITE_FORWARD.h>
#include <Core/Read_Write/STREAM_TYPE.h>
#include <Core/Vectors/VECTOR.h>
#include <functional>
namespace PhysBAM{
template<class TV> class GEOMETRY_PARTICLES;
template<class TV> class IMPLICIT_OBJECT;
template<class T,int m,int n> class MATRIX;
struct VIEWER_DIR;
template<class TV> class GRID;

struct DEBUG_PARTICLES_INFO
{
    std::string name,info;
    void Read(TYPED_ISTREAM& in);
    void Write(TYPED_OSTREAM& out) const;
};

template<class TV>
class DEBUG_PARTICLES
{
public:
    typedef typename TV::SCALAR T;
    typedef VECTOR<T,3> C;
    template<int m,int n,int p>
    struct DATA
    {
        VECTOR<ARRAY<TV>,m> X;
        ARRAY<C> color;
        VECTOR<ARRAY<T>,n> t;
        VECTOR<ARRAY<int>,p> i;
        ARRAY<DEBUG_PARTICLES_INFO> info;
        void Read(TYPED_ISTREAM& in);
        void Write(TYPED_OSTREAM& out) const;
    };
    DATA<1,0,0> points;
    DATA<2,0,0> segments;
    VECTOR<DATA<3,0,0>,2> tris; // [filled]
    VECTOR<VECTOR<DATA<1,1,0>,2>,2> balls; // [filled][scaled]
    VECTOR<VECTOR<DATA<2,0,0>,2>,2> arrows; // [arrowhead][scaled]
    VECTOR<VECTOR<DATA<1,1,2>,2>,2> regular_polys; // [filled][scaled]

    struct TEXT_DATA
    {
        TV X;
        C color;
        std::string str;
        DEBUG_PARTICLES_INFO info;
        void Read(TYPED_ISTREAM& in);
        void Write(TYPED_OSTREAM& out) const;
    };
    ARRAY<TEXT_DATA> text;

    DEBUG_PARTICLES();
    ~DEBUG_PARTICLES();

    static DEBUG_PARTICLES<TV>& Get(DEBUG_PARTICLES<TV>* debug_particles=nullptr);
    void Write_Debug_Particles(STREAM_TYPE stream_type,const VIEWER_DIR& viewer_dir);
    void Clear_Debug_Particles();
    RANGE<TV> Bounding_Box() const;

    void Read(TYPED_ISTREAM& in);
    void Write(TYPED_OSTREAM& out) const;
};

template<class T,int d> DEBUG_PARTICLES_INFO&
Add_Point(const VECTOR<T,d>& X,const VECTOR<T,3>& color);

template<class T,int d> DEBUG_PARTICLES_INFO&
Add_Segment(const VECTOR<T,d>& X,const VECTOR<T,d>& Y,const VECTOR<T,3>& color);

template<class T,int d> DEBUG_PARTICLES_INFO&
Add_Triangle(const VECTOR<T,d>& X,const VECTOR<T,d>& Y,const VECTOR<T,d>& Z,
    const VECTOR<T,3>& color,bool filled=true);

template<class T,int d,int n> DEBUG_PARTICLES_INFO&
Add_Simplex(const VECTOR<VECTOR<T,d>,n>& X,const VECTOR<T,3>& color)
{
    if constexpr(n==1) return Add_Point(X.x,color);
    else if constexpr(n==2) return Add_Segment(X.x,X.y,color);
    else return Add_Triangle(X.x,X.y,X.z,color);
}

template<class T,int d> DEBUG_PARTICLES_INFO&
Add_Ball(const VECTOR<T,d>& X,T radius,const VECTOR<T,3>& color,
    bool filled=false,bool scaled=true);

template<class T,int d> DEBUG_PARTICLES_INFO&
Add_Arrow(const VECTOR<T,d>& X,const VECTOR<T,d>& dX,const VECTOR<T,3>& color,
    bool scaled=true,bool arrowhead=true);

// angle = 0..7, sides = 3..64
template<class T,int d> DEBUG_PARTICLES_INFO&
Add_Regular_Polygon(const VECTOR<T,d>& X,T radius,int sides,int angle,
    const VECTOR<T,3>& color,bool filled=false,bool scaled=true);

template<class T,int d> DEBUG_PARTICLES_INFO&
Add_Text(const VECTOR<T,d>& X,const std::string& text,const VECTOR<T,3>& color);

template<class TV,class IV,class T,bool is_mac> void
Dump_Levelset(const GRID<TV>& grid,GRID_TYPE<is_mac> t,const ARRAY<T,IV>& phi,const VECTOR<T,3>& color,T contour_value=0)
{Dump_Levelset(grid,t,phi,color,contour_value);}
template<class T_SURFACE,class T> void
Dump_Surface(const T_SURFACE& surface,const VECTOR<T,3>& color);
template<class TV,class IV,class T> void
Dump_Levelset(const GRID<TV>& grid,AT_NODES,const ARRAY<T,IV>& phi,const VECTOR<T,3>& color,T contour_value=0);
template<class TV,class IV,class T> void
Dump_Levelset(const GRID<TV>& grid,AT_CELLS,const ARRAY<T,IV>& phi,const VECTOR<T,3>& color,T contour_value=0)
{Dump_Levelset(grid.Get_Regular_Grid_At_MAC_Positions(),at_nodes,phi,color,contour_value);}
template<class TV,class T,bool is_mac> void
Dump_Levelset(const GRID<TV>& grid,GRID_TYPE<is_mac> t,const IMPLICIT_OBJECT<TV>& phi,const VECTOR<T,3>& color)
{Dump_Levelset(grid,t,phi,color,color);}
template<class TV,class T> void
Dump_Levelset(const GRID<TV>& grid,AT_NODES,const IMPLICIT_OBJECT<TV>& phi,const VECTOR<T,3>& color);
template<class TV,class T> void
Dump_Levelset(const GRID<TV>& grid,AT_CELLS,const IMPLICIT_OBJECT<TV>& phi,const VECTOR<T,3>& color)
{Dump_Levelset(grid.Get_Regular_Grid_At_MAC_Positions(),at_nodes,phi,color);}

// Parametric (2D, 3D).  Evaluates func(t) for ta<=t<=tb.  The output is rotated and uniformly scaled so that func(t)=FA is transformed to A, and FB is transformed to B.
template<class TV,class T> void Plot_Curve(std::function<TV(T)> func,T ta,T tb,int samples,const MATRIX<T,TV::m,TV::m>& M,TV B,VECTOR<T,3> color);

// plot function (2D).  Plots (t,func(t)*scale).  Points (ta,0) and (tb,0) map to locations A and B.
template<class TV,class T> void Plot_Curve(std::function<T(T)> func,T ta,T tb,int samples,TV A,TV B,T scale,VECTOR<T,3> color);
}
#endif
