//#####################################################################
// Copyright 2011.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
#include <Core/Geometry_Particles/DEBUG_PARTICLES.h>
#include <Core/Geometry_Particles/GEOMETRY_PARTICLES.h>
#include <Core/Grids/GRID.h>
#include <Core/Grids/NODE_ITERATOR.h>
#include <Core/Grids_Uniform_Computations/MARCHING_CUBES.h>
#include <Core/Implicit_Objects/IMPLICIT_OBJECT.h>
#include <Core/Matrices/MATRIX.h>
#include <Core/Meshes/POINT_SIMPLICES_1D.h>
#include <Core/Meshes/SEGMENTED_CURVE.h>
#include <Core/Meshes/TRIANGULATED_SURFACE.h>
#include <Core/Read_Write/READ_WRITE.h>
#include <Core/Utilities/VIEWER_DIR.h>
namespace PhysBAM{
//#####################################################################
// Constructor
//#####################################################################
template<class TV> DEBUG_PARTICLES<TV>::
DEBUG_PARTICLES()
{
}
//#####################################################################
// Destructor
//#####################################################################
template<class TV> DEBUG_PARTICLES<TV>::
~DEBUG_PARTICLES()
{
}
//#####################################################################
// Function Clear_Debug_Particles
//#####################################################################
template<class TV> void DEBUG_PARTICLES<TV>::
Clear_Debug_Particles()
{
    points={};
    segments={};
    for(auto&i:tris) i={};
    for(auto&i:regular_polys) i={};
    for(auto&i:balls) for(auto&j:i) j={};
    for(auto&i:arrows) for(auto&j:i) j={};
    text={};
}
//#####################################################################
// Function Write_Debug_Particles
//#####################################################################
template<class TV> void DEBUG_PARTICLES<TV>::
Write_Debug_Particles(STREAM_TYPE stream_type,const VIEWER_DIR& viewer_dir)
{
    FILE_OSTREAM output(stream_type,viewer_dir.Frame_File("debug_particles"));
    Write(output);
    Clear_Debug_Particles();
}
//#####################################################################
// Function Dump_Surface
//#####################################################################
template<class T_SURFACE,class T> void
Dump_Surface(const T_SURFACE& surface,const VECTOR<T,3>& color)
{
    typedef VECTOR<T,T_SURFACE::dimension+1> TV;
    for(int i=0;i<surface.mesh.elements.m;i++)
    {
        VECTOR<TV,TV::m> a(surface.particles.X.Subset(surface.mesh.elements(i)));
        Add_Simplex(a,color);
    }
}
//#####################################################################
// Function Dump_Levelset
//#####################################################################
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)
{
    MESH_OBJECT<TV,TV::m-1> surface;
    MARCHING_CUBES<TV>::Create_Surface(surface,grid,at_nodes,phi,contour_value);
    Dump_Surface(surface,color);
}
//#####################################################################
// Function Dump_Levelset
//#####################################################################
template<class TV,class T> void
Dump_Levelset(const GRID<TV>& grid,GRID_TYPE<0>,const IMPLICIT_OBJECT<TV>& phi,const VECTOR<T,3>& color)
{
    ARRAY<T,VECTOR<int,TV::m> > phi_array(grid.Numbers_Of_Nodes());
    for(NODE_ITERATOR<TV> it(grid);it.Valid();it.Next())
        phi_array(it.index)=phi.Extended_Phi(it.Location());
    Dump_Levelset(grid,at_nodes,phi_array,color);
}
//#####################################################################
// Function Get_Static
//#####################################################################
template<class TV> DEBUG_PARTICLES<TV>&
Get_Static()
{
    static DEBUG_PARTICLES<TV> debug_particles;
    return debug_particles;
}
//#####################################################################
// Function Get
//#####################################################################
template<class TV> DEBUG_PARTICLES<TV>& DEBUG_PARTICLES<TV>::
Get(DEBUG_PARTICLES<TV>* debug_particles)
{
    static DEBUG_PARTICLES<TV>* ptr_debug_particles=nullptr;
    if(!debug_particles)
    {
        if(!ptr_debug_particles)
            ptr_debug_particles=&Get_Static<TV>();
        return *ptr_debug_particles;
    }
    auto prev=ptr_debug_particles;
    ptr_debug_particles=debug_particles;
    return prev?*prev:*debug_particles;
}
//#####################################################################
// Function Plot_Curve
//#####################################################################
// Parametric (2D, 3D).  Evaluates func(t) for ta<=t<=tb.  The output is transformed by X->(M*X+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)
{
    TV last=M*func(ta)+B;
    samples--;
    T dt=(tb-ta)/samples;
    for(int i=0;i<samples;i++)
    {
        TV next=M*func((i+1)*dt+ta)+B;
        Add_Segment(last,next,color);
        last=next;
    }
}
//#####################################################################
// Function Plot_Curve
//#####################################################################
// 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)
{
    samples--;
    T dt=(tb-ta)/samples;
    TV u=(B-A)/(tb-ta);
    MATRIX<T,2> M{u,u.Orthogonal_Vector()};
    TV S=A-M*TV(ta,0);

    TV last=M*TV(ta,func(ta)*scale)+S;
    for(int i=0;i<samples;i++)
    {
        T t=(i+1)*dt+ta;
        TV next=M*TV(t,func(t)*scale)+S;
        Add_Segment(last,next,color);
        last=next;
    }
}
//#####################################################################
// Function Add_Point
//#####################################################################
template<class T,int d> DEBUG_PARTICLES_INFO&
Add_Point(const VECTOR<T,d>& X,const VECTOR<T,3>& color)
{
    auto& particles=DEBUG_PARTICLES<VECTOR<T,d> >::Get();
    auto& a=particles.points;
    a.X(0).Append(X);
    a.color.Append(color);
    a.info.Add_End();
    return a.info.Last();
}
//#####################################################################
// Function Add_Segment
//#####################################################################
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)
{
    auto& particles=DEBUG_PARTICLES<VECTOR<T,d> >::Get();
    auto& a=particles.segments;
    a.X(0).Append(X);
    a.X(1).Append(Y);
    a.color.Append(color);
    a.info.Add_End();
    return a.info.Last();
}
//#####################################################################
// Function Add_Triangle
//#####################################################################
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)
{
    auto& particles=DEBUG_PARTICLES<VECTOR<T,d> >::Get();
    auto& a=particles.tris(filled);
    a.X(0).Append(X);
    a.X(1).Append(Y);
    a.X(2).Append(Z);
    a.color.Append(color);
    a.info.Add_End();
    return a.info.Last();
}
//#####################################################################
// Function Add_Ball
//#####################################################################
template<class T,int d> DEBUG_PARTICLES_INFO&
Add_Ball(const VECTOR<T,d>& X,T radius,const VECTOR<T,3>& color,
    bool filled,bool scaled)
{
    auto& particles=DEBUG_PARTICLES<VECTOR<T,d> >::Get();
    auto& a=particles.balls(filled)(scaled);
    a.X(0).Append(X);
    a.t(0).Append(radius);
    a.color.Append(color);
    a.info.Add_End();
    return a.info.Last();
}
//#####################################################################
// Function Add_Arrow
//#####################################################################
template<class T,int d> DEBUG_PARTICLES_INFO&
Add_Arrow(const VECTOR<T,d>& X,const VECTOR<T,d>& Y,const VECTOR<T,3>& color,
    bool scaled,bool arrowhead)
{
    auto& particles=DEBUG_PARTICLES<VECTOR<T,d> >::Get();
    auto& a=particles.arrows(arrowhead)(scaled);
    a.X(0).Append(X);
    a.X(1).Append(Y);
    a.color.Append(color);
    a.info.Add_End();
    return a.info.Last();
}
//#####################################################################
// Function Add_Regular_Polygon
//#####################################################################
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,bool scaled)
{
    if constexpr(d==3) return Add_Ball(X,radius,color,filled);
    else
    {
        auto& particles=DEBUG_PARTICLES<VECTOR<T,d> >::Get();
        auto& a=particles.regular_polys(filled)(scaled);
        a.X(0).Append(X);
        a.t(0).Append(radius);
        a.i(0).Append(sides);
        a.i(1).Append(angle);
        a.color.Append(color);
        a.info.Add_End();
        return a.info.Last();
    }
}
//#####################################################################
// Function Add_Text
//#####################################################################
template<class T,int d> DEBUG_PARTICLES_INFO&
Add_Text(const VECTOR<T,d>& X,const std::string& text,const VECTOR<T,3>& color)
{
    auto& particles=DEBUG_PARTICLES<VECTOR<T,d> >::Get();
    auto& a=particles.text;
    a.Append({X,color,text});
    return a.Last().info;
}
//#####################################################################
// Function Bounding_Box
//#####################################################################
template<class TV> RANGE<TV> DEBUG_PARTICLES<TV>::
Bounding_Box() const
{
    RANGE<TV> box=RANGE<TV>::Empty_Box();

    for(int f=0;f<2;f++)
    {
        for(const auto&i:tris(f).X)
            for(const auto&j:i)
                box.Enlarge_To_Include_Point(j);
        for(int s=0;s<2;s++)
        {
            const auto&b=balls(f)(s);
            for(int i=0;i<b.X(0).m;i++)
            {
                box.Enlarge_To_Include_Point(b.X(0)(i)-b.t(0)(i)*!s);
                box.Enlarge_To_Include_Point(b.X(0)(i)+b.t(0)(i)*!s);
            }
            const auto& p=regular_polys(f)(s);
            for(int i=0;i<p.X(0).m;i++)
            {
                box.Enlarge_To_Include_Point(p.X(0)(i)-p.t(0)(i)*!s);
                box.Enlarge_To_Include_Point(p.X(0)(i)+p.t(0)(i)*!s);
            }
        }
    }
    for(const auto&i:segments.X)
        for(const auto&j:i)
            box.Enlarge_To_Include_Point(j);
    for(int a=0;a<2;a++)
        for(int s=0;s<2;s++)
            box.Enlarge_To_Include_Points(arrows(a)(s).X(0));
    box.Enlarge_To_Include_Points(points.X(0));
    return box;
}
//#####################################################################
// Function Read
//#####################################################################
template<class TV> template<int n,int m,int p> void DEBUG_PARTICLES<TV>::DATA<n,m,p>::
Read(TYPED_ISTREAM& in)
{
    Read_Binary_Group(in,X,color,t,i,info);
}
//#####################################################################
// Function Write
//#####################################################################
template<class TV> template<int n,int m,int p> void DEBUG_PARTICLES<TV>::DATA<n,m,p>::
Write(TYPED_OSTREAM& out) const
{
    Write_Binary_Group(out,X,color,t,i,info);
}
//#####################################################################
// Function Read
//#####################################################################
void DEBUG_PARTICLES_INFO::
Read(TYPED_ISTREAM& in)
{
    Read_Binary_Group(in,name,info);
}
//#####################################################################
// Function Write
//#####################################################################
void DEBUG_PARTICLES_INFO::
Write(TYPED_OSTREAM& out) const
{
    Write_Binary_Group(out,name,info);
}
//#####################################################################
// Function Read
//#####################################################################
template<class TV> void DEBUG_PARTICLES<TV>::TEXT_DATA::
Read(TYPED_ISTREAM& in)
{
    Read_Binary_Group(in,X,color,str,info);
}
//#####################################################################
// Function Write
//#####################################################################
template<class TV> void DEBUG_PARTICLES<TV>::TEXT_DATA::
Write(TYPED_OSTREAM& out) const
{
    Write_Binary_Group(out,X,color,str,info);
}
//#####################################################################
// Function Read
//#####################################################################
template<class TV> void DEBUG_PARTICLES<TV>::
Read(TYPED_ISTREAM& in)
{
    Read_Binary_Group(in,points,segments,tris,balls,arrows,regular_polys,text);
}
//#####################################################################
// Function Write
//#####################################################################
template<class TV> void DEBUG_PARTICLES<TV>::
Write(TYPED_OSTREAM& out) const
{
    Write_Binary_Group(out,points,segments,tris,balls,arrows,regular_polys,text);
}
template class DEBUG_PARTICLES<VECTOR<float,1> >;
template class DEBUG_PARTICLES<VECTOR<float,2> >;
template class DEBUG_PARTICLES<VECTOR<float,3> >;
template class DEBUG_PARTICLES<VECTOR<double,1> >;
template class DEBUG_PARTICLES<VECTOR<double,2> >;
template class DEBUG_PARTICLES<VECTOR<double,3> >;

template void Plot_Curve<VECTOR<double,2>,double>(std::function<VECTOR<double,2> (double)>,double,double,int,MATRIX<double,VECTOR<double,2>::m,VECTOR<double,2>::m> const&,VECTOR<double,2>,VECTOR<double,3>);
template void Plot_Curve<VECTOR<double,2>,double>(std::function<double (double)>,double,double,int,VECTOR<double,2>,VECTOR<double,2>,double,VECTOR<double,3>);
template void Plot_Curve<VECTOR<float,2>,float>(std::function<VECTOR<float,2> (float)>,float,float,int,MATRIX<float,VECTOR<float,2>::m,VECTOR<float,2>::m> const&,VECTOR<float,2>,VECTOR<float,3>);
template void Plot_Curve<VECTOR<float,2>,float>(std::function<float (float)>,float,float,int,VECTOR<float,2>,VECTOR<float,2>,float,VECTOR<float,3>);
template DEBUG_PARTICLES_INFO& Add_Ball<double,2>(VECTOR<double,2> const&,double,VECTOR<double,3> const&,bool,bool);
template DEBUG_PARTICLES_INFO& Add_Ball<double,3>(VECTOR<double,3> const&,double,VECTOR<double,3> const&,bool,bool);
template DEBUG_PARTICLES_INFO& Add_Ball<float,2>(VECTOR<float,2> const&,float,VECTOR<float,3> const&,bool,bool);
template DEBUG_PARTICLES_INFO& Add_Ball<float,3>(VECTOR<float,3> const&,float,VECTOR<float,3> const&,bool,bool);
template DEBUG_PARTICLES_INFO& Add_Point<double,1>(VECTOR<double,1> const&,VECTOR<double,3> const&);
template DEBUG_PARTICLES_INFO& Add_Point<double,2>(VECTOR<double,2> const&,VECTOR<double,3> const&);
template DEBUG_PARTICLES_INFO& Add_Point<double,3>(VECTOR<double,3> const&,VECTOR<double,3> const&);
template DEBUG_PARTICLES_INFO& Add_Point<float,1>(VECTOR<float,1> const&,VECTOR<float,3> const&);
template DEBUG_PARTICLES_INFO& Add_Point<float,2>(VECTOR<float,2> const&,VECTOR<float,3> const&);
template DEBUG_PARTICLES_INFO& Add_Point<float,3>(VECTOR<float,3> const&,VECTOR<float,3> const&);
template DEBUG_PARTICLES_INFO& Add_Segment<double,3>(VECTOR<double,3> const&,VECTOR<double,3> const&,VECTOR<double,3> const&);
template DEBUG_PARTICLES_INFO& Add_Segment<float,3>(VECTOR<float,3> const&,VECTOR<float,3> const&,VECTOR<float,3> const&);
template DEBUG_PARTICLES_INFO& Add_Arrow<double,2>(VECTOR<double,2> const&,VECTOR<double,2> const&,VECTOR<double,3> const&,bool,bool);
template DEBUG_PARTICLES_INFO& Add_Arrow<double,3>(VECTOR<double,3> const&,VECTOR<double,3> const&,VECTOR<double,3> const&,bool,bool);
template DEBUG_PARTICLES_INFO& Add_Arrow<float,2>(VECTOR<float,2> const&,VECTOR<float,2> const&,VECTOR<float,3> const&,bool,bool);
template DEBUG_PARTICLES_INFO& Add_Arrow<float,3>(VECTOR<float,3> const&,VECTOR<float,3> const&,VECTOR<float,3> const&,bool,bool);
template void Dump_Levelset<VECTOR<double,2>,double>(GRID<VECTOR<double,2> > const&,GRID_TYPE<false>,IMPLICIT_OBJECT<VECTOR<double,2> > const&,VECTOR<double,3> const&);
template void Dump_Levelset<VECTOR<double,3>,double>(GRID<VECTOR<double,3> > const&,GRID_TYPE<false>,IMPLICIT_OBJECT<VECTOR<double,3> > const&,VECTOR<double,3> const&);
template void Dump_Levelset<VECTOR<float,2>,float>(GRID<VECTOR<float,2> > const&,GRID_TYPE<false>,IMPLICIT_OBJECT<VECTOR<float,2> > const&,VECTOR<float,3> const&);
template void Dump_Levelset<VECTOR<float,3>,float>(GRID<VECTOR<float,3> > const&,GRID_TYPE<false>,IMPLICIT_OBJECT<VECTOR<float,3> > const&,VECTOR<float,3> const&);
template void Dump_Surface<MESH_OBJECT<VECTOR<float,2>,1>,float>(MESH_OBJECT<VECTOR<float,2>,1> const&,VECTOR<float,3> const&);
template DEBUG_PARTICLES_INFO& Add_Regular_Polygon<double,2>(VECTOR<double,2> const&,double,int,int,VECTOR<double,3> const&,bool,bool);
template DEBUG_PARTICLES_INFO& Add_Regular_Polygon<float,2>(VECTOR<float,2> const&,float,int,int,VECTOR<float,3> const&,bool,bool);
template DEBUG_PARTICLES_INFO& Add_Regular_Polygon<double,3>(VECTOR<double,3> const&,double,int,int,VECTOR<double,3> const&,bool,bool);
template DEBUG_PARTICLES_INFO& Add_Regular_Polygon<float,3>(VECTOR<float,3> const&,float,int,int,VECTOR<float,3> const&,bool,bool);
template DEBUG_PARTICLES_INFO& Add_Text<float,2>(VECTOR<float,2> const&,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char> > const&,VECTOR<float,3> const&);
template DEBUG_PARTICLES_INFO& Add_Text<float,3>(VECTOR<float,3> const&,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char> > const&,VECTOR<float,3> const&);
template DEBUG_PARTICLES_INFO& Add_Text<double,2>(VECTOR<double,2> const&,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char> > const&,VECTOR<double,3> const&);
template DEBUG_PARTICLES_INFO& Add_Text<double,3>(VECTOR<double,3> const&,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char> > const&,VECTOR<double,3> const&);
template DEBUG_PARTICLES_INFO& Add_Triangle<double,2>(VECTOR<double,2> const&,VECTOR<double,2> const&,VECTOR<double,2> const&,VECTOR<double,3> const&,bool);
template DEBUG_PARTICLES_INFO& Add_Triangle<double,3>(VECTOR<double,3> const&,VECTOR<double,3> const&,VECTOR<double,3> const&,VECTOR<double,3> const&,bool);
template DEBUG_PARTICLES_INFO& Add_Segment<double,2>(VECTOR<double,2> const&,VECTOR<double,2> const&,VECTOR<double,3> const&);
template DEBUG_PARTICLES_INFO& Add_Segment<float,2>(VECTOR<float,2> const&,VECTOR<float,2> const&,VECTOR<float,3> const&);
template DEBUG_PARTICLES_INFO& Add_Triangle<float,3>(VECTOR<float,3> const&,VECTOR<float,3> const&,VECTOR<float,3> const&,VECTOR<float,3> const&,bool);
template void Dump_Surface<MESH_OBJECT<VECTOR<double,3>,2>,double>(MESH_OBJECT<VECTOR<double,3>,2> const&,VECTOR<double,3> const&);
template void Dump_Levelset<VECTOR<float,2>,VECTOR<int,2>,float>(GRID<VECTOR<float,2> > const&,GRID_TYPE<false>,ARRAY<float,VECTOR<int,2> > const&,VECTOR<float,3> const&,float);
template void Dump_Levelset<VECTOR<double,2>,VECTOR<int,2>,double>(GRID<VECTOR<double,2> > const&,GRID_TYPE<false>,ARRAY<double,VECTOR<int,2> > const&,VECTOR<double,3> const&,double);
template void Dump_Levelset<VECTOR<float,3>,VECTOR<int,3>,float>(GRID<VECTOR<float,3> > const&,GRID_TYPE<false>,ARRAY<float,VECTOR<int,3> > const&,VECTOR<float,3> const&,float);
template void Dump_Levelset<VECTOR<double, 3>, VECTOR<int, 3>, double>(GRID<VECTOR<double, 3> > const&, GRID_TYPE<false>, ARRAY<double, VECTOR<int, 3> > const&, VECTOR<double, 3> const&, double);
template DEBUG_PARTICLES_INFO& Add_Segment<double,1>(VECTOR<double,1> const&,VECTOR<double,1> const&,VECTOR<double,3> const&);
template DEBUG_PARTICLES_INFO& Add_Segment<float,1>(VECTOR<float,1> const&,VECTOR<float,1> const&,VECTOR<float,3> const&);
}
