//#####################################################################
// Copyright 2005-2009, Zhaosheng Bao, Eran Guendelman, Geoffrey Irving, Nipun Kwatra, Sergey Koltakov, Joseph Teran.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
#include <Core/Arrays/INDIRECT_ARRAY.h>
#include <Core/Basic_Geometry/PLANE.h>
#include <Core/Basic_Geometry/TETRAHEDRON.h>
#include <Core/Geometry_Particles/GEOMETRY_PARTICLES.h>
#include <Core/Math_Tools/RANGE.h>
#include <Core/Meshes/SEGMENTED_CURVE.h>
#include <Core/Meshes/TETRAHEDRALIZED_VOLUME.h>
#include <Core/Meshes/TRIANGLE_MESH.h>
#include <Core/Meshes/TRIANGULATED_SURFACE.h>
#include "../QT_RENDER_UTILS.h"
#include "../QT_WORLD.h"
#include "QT_COMPONENT_OPTIONS.h"
#include "QT_OBJECT.h"
#include "QT_TETRAHEDRALIZED_VOLUME.h"
#include <QAction>
namespace PhysBAM{
#define SET(name,value) (opt.name.Set(value),raw_opt[0].name=opt.name)
#define GET(name) opt.name.Value()
//#####################################################################
// Constructor
//#####################################################################
template<class T> QT_TETRAHEDRALIZED_VOLUME<T>::
QT_TETRAHEDRALIZED_VOLUME(QT_WORLD& world,const std::string& name,const std::string& file)
    :BASE(world,name,file),opt(OPT::def),raw_opt{{},{},{},OPT::def}
{
}
//#####################################################################
// Constructor
//#####################################################################
template<class T> QT_TETRAHEDRALIZED_VOLUME<T>::
QT_TETRAHEDRALIZED_VOLUME(QT_WORLD& world,const std::string& name,const std::string& mesh_file,const std::string& part_file)
    :BASE(world,name,mesh_file,part_file),opt(OPT::def),raw_opt{{},{},{},OPT::def}
{
}
//#####################################################################
// Destructor
//#####################################################################
template<class T> QT_TETRAHEDRALIZED_VOLUME<T>::
~QT_TETRAHEDRALIZED_VOLUME()
{
}
//#####################################################################
// Function Read
//#####################################################################
template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
Read()
{
    BASE::Read();
    if(valid)
    {
        auto& tv=Obj();
        ts=&tv.Get_Boundary_Object();
        delete tv.mesh.segment_mesh;
        tv.mesh.segment_mesh=nullptr;
    }
}
//#####################################################################
// Function Render
//#####################################################################
template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
Render(QT_RENDER_UTILS& ru)
{
    if(!GET(draw)) return;
    Read();
    if(!valid) return;
    auto& tv=Obj();

    ru.pick.Push(0);
    if(GET(draw_vertices))
    {
        auto old=ru.Push(ru.depth_offset,ru.depth_offset-ru.depth_offset_unit*2);
        ru.Render_Points<TV>(tv.particles.X,GET(vertex_color));
    }

    ru.pick.Set(1);
    if(GET(draw_edges))
    {
        auto old=ru.Push(ru.depth_offset,ru.depth_offset-ru.depth_offset_unit);
        if(!tv.mesh.segment_mesh) tv.mesh.Initialize_Segment_Mesh();
        ru.Render_Mesh(SEGMENTED_CURVE<TV>(*tv.mesh.segment_mesh,tv.particles),
            GET(edge_color));
    }

    ru.pick.Set(2);
    ru.Render_Mesh(*ts,GET(color),{.bgcolor=GET(bgcolor)});
    ru.pick.Pop();

    if(ru.select) return;

    if(selected_vertex>=0)
    {
        auto old=ru.Push(ru.depth_offset,ru.depth_offset-ru.depth_offset_unit*2.5f);
        ru.Render_Point(tv.particles.X(selected_vertex),ru.highlight_color);
        LOG::fprintf(ru.info_text,"vertex %P\n",selected_vertex);
        tv.particles.Print(ru.info_text,selected_vertex);
    }

    if(selected_segment>=0)
    {
        auto old=ru.Push(ru.depth_offset,ru.depth_offset-ru.depth_offset_unit*1.5f);
        auto e=tv.mesh.segment_mesh->elements(selected_segment);
        ru.Render_Line(tv.particles.X(e.x),tv.particles.X(e.y),
            ru.highlight_color);
        LOG::fprintf(ru.info_text,"segment %P %P\n",selected_segment,e);
        for(auto p:e)
        {
            LOG::fprintf(ru.info_text,"\nparticle %P\n",p);
            tv.particles.Print(ru.info_text,p);
        }
    }

    if(selected_tetrahedron>=0)
    {
        auto old=ru.Push(ru.depth_offset,ru.depth_offset-ru.depth_offset_unit);
        auto ole=ru.Push(ru.wireframe,true);
        auto e=tv.mesh.elements(selected_tetrahedron);
        for(int i=0;i<4;i++)
        {
            auto f=e.Remove_Index(i);
            ru.Render_Triangle(ts->particles.X(f.x),ts->particles.X(f.y),
                ts->particles.X(f.z),ru.highlight_color);
        }
        LOG::fprintf(ru.info_text,"tetrahedron %P %P\n",selected_tetrahedron,e);
        for(auto p:e)
        {
            LOG::fprintf(ru.info_text,"\nparticle %P\n",p);
            ts->particles.Print(ru.info_text,p);
        }
    }

    if(GET(draw_velocities))
        if(ARRAY_VIEW<TV>* V=tv.particles.template Get_Array<TV>("V"))
            ru.Render_Arrows<TV>(tv.particles.X,*V,GET(velocity_color));
}
//#####################################################################
// Bounding_Box
//#####################################################################
template<class T> auto QT_TETRAHEDRALIZED_VOLUME<T>::
Bounding_Box() -> RANGE<FV>
{
    if(!GET(draw)) return RANGE<FV>::Empty_Box();
    Read();
    return BASE::Bounding_Box();
}
//#####################################################################
// Function Draw_Wireframe_Mesh
//#####################################################################
// template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
// Draw_Wireframe_Mesh(const TETRAHEDRON_MESH& tetrahedron_mesh) const
// {
//     glDisable(GL_LIGHTING);
//     material.diffuse.Send_To_GL_Pipeline();
//     ru.glBegin(GL_LINES);
//     for(int t=0;t<tetrahedron_mesh.elements.m;t++){
//         int i,j,k,l;tetrahedron_mesh.elements(t).Get(i,j,k,l);
//         OpenGL_Line(particles->X(i),particles->X(j));
//         OpenGL_Line(particles->X(j),particles->X(k));
//         OpenGL_Line(particles->X(k),particles->X(l));
//         OpenGL_Line(particles->X(l),particles->X(i));
//         OpenGL_Line(particles->X(i),particles->X(k));
//         OpenGL_Line(particles->X(j),particles->X(l));}
//     ru.glEnd();
//     glEnable(GL_LIGHTING);
// }
//#####################################################################
// Function Draw_Selected_Tetrahedron
//#####################################################################
// template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
// Draw_Selected_Tetrahedron() const
// {
//     int t=selected_tetrahedron,i,j,k,l;mesh->elements(t).Get(i,j,k,l);
//     VECTOR<T,3> xi=particles->X(i),xj=particles->X(j),xk=particles->X(k),xl=particles->X(l);
//     TETRAHEDRON<T> tet(xi,xj,xk,xl);
//     ru.glBegin(GL_TRIANGLES);
//     for(int i=0;i<4;i++){
//         for(int plane_vertices=0;plane_vertices<3;plane_vertices++)
//             OpenGL_Normal(tet.triangle(i).Normal());
//         OpenGL_Triangle(tet.triangle(i).X);}
//     ru.glEnd();
// }
//#####################################################################
// Function Update_Cutaway_Plane
//#####################################################################
// template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
// Update_Cutaway_Plane()
// {
//     TETRAHEDRALIZED_VOLUME<T> tetrahedralized_volume(*mesh,*particles);
//     tetrahedralized_volume.Update_Bounding_Box();
//     RANGE<VECTOR<T,3> > box=*tetrahedralized_volume.bounding_box;
//     ARRAY<bool> inside(particles->Size());T threshold;
//     switch(cutaway_mode){
//         case 1:threshold=box.min_corner.x+cutaway_fraction*(box.max_corner.x-box.min_corner.x);for(int p=0;p<particles->Size();p++)inside(p)=particles->X(p).x<threshold;break;
//         case 2:threshold=box.max_corner.x+cutaway_fraction*(box.min_corner.x-box.max_corner.x);for(int p=0;p<particles->Size();p++)inside(p)=particles->X(p).x>threshold;break;
//         case 3:threshold=box.min_corner.y+cutaway_fraction*(box.max_corner.y-box.min_corner.y);for(int p=0;p<particles->Size();p++)inside(p)=particles->X(p).y<threshold;break;
//         case 4:threshold=box.max_corner.y+cutaway_fraction*(box.min_corner.y-box.max_corner.y);for(int p=0;p<particles->Size();p++)inside(p)=particles->X(p).y>threshold;break;
//         case 5:threshold=box.min_corner.z+cutaway_fraction*(box.max_corner.z-box.min_corner.z);for(int p=0;p<particles->Size();p++)inside(p)=particles->X(p).z<threshold;break;
//         case 6:threshold=box.max_corner.z+cutaway_fraction*(box.min_corner.z-box.max_corner.z);for(int p=0;p<particles->Size();p++)inside(p)=particles->X(p).z>threshold;break;}
//     cutaway_mesh.number_nodes=mesh->number_nodes;
//     cutaway_mesh.elements.Remove_All();
//     for(int t=0;t<mesh->elements.m;t++){
//         int i,j,k,l;mesh->elements(t).Get(i,j,k,l);
//         if(inside(i) || inside(j) || inside(k) || inside(l)) cutaway_mesh.elements.Append(VECTOR<int,4>(i,j,k,l));}
//     cutaway_mesh.Initialize_Boundary_Mesh();
// }
//#####################################################################
// Function Get_Selection_Priority
//#####################################################################
// template<class T> int QT_TETRAHEDRALIZED_VOLUME<T>::
// Get_Selection_Priority(ARRAY_VIEW<GLuint> indices)
// {
//     if(!indices.m) return -1;
//     PHYSBAM_ASSERT(indices.m==2);
//     const static int priority[]={85,84};
//     return priority[indices(0)];
// }
//#####################################################################
// Function Clear_Selection
//#####################################################################
template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
Clear_Selection()
{
    selected_vertex=-1;
    selected_segment=-1;
    selected_triangle=-1;
    selected_tetrahedron=-1;
}
//#####################################################################
// Function Set_Selection
//#####################################################################
template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
Set_Selection(ARRAY_VIEW<int> selection)
{
    PHYSBAM_ASSERT(selection.m>=2);
    if(selection(0)==0) selected_vertex=selection(1);
    else if(selection(0)==1) selected_segment=selection(1);
    else if(selection(0)==2)
    {
        selected_triangle=selection(1);
        Select_Tetrahedron_From_Triangle();
    }
    else PHYSBAM_FATAL_ERROR();
}
//#####################################################################
// Function Select_Tetrahedron_From_Triangle
//#####################################################################
template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
Select_Tetrahedron_From_Triangle()
{
    auto t=ts->mesh.elements(selected_triangle);
    auto& tv=Obj();
    for(int e=0;e<tv.mesh.elements.m;e++)
    {
        auto f=tv.mesh.elements(e);
        bool ok=true;
        for(auto a:t)
            if(f.Find(a)<0)
            {
                ok=false;
                break;
            }
        if(ok)
        {
            selected_tetrahedron=e;
            break;
        }
    }
}
//#####################################################################
// Function Selection_Point
//#####################################################################
template<class TV> auto QT_TETRAHEDRALIZED_VOLUME<TV>::
Selection_Point() -> FV
{
    PHYSBAM_ASSERT(valid);
    auto& tv=Obj();
    if(selected_vertex>=0) return FV(tv.particles.X(selected_vertex));
    if(selected_segment>=0)
    {
        auto e=tv.mesh.segment_mesh->elements(selected_segment);
        return FV(tv.particles.X.Subset(e).Average());
    }
    if(selected_triangle>=0)
    {
        auto e=ts->mesh.elements(selected_triangle);
        return FV(tv.particles.X.Subset(e).Average());
    }
    PHYSBAM_FATAL_ERROR();
}
//#####################################################################
// Function Make_Dialog
//#####################################################################
template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
Make_Dialog(QT_WINDOW& window)
{
    QT_COMPONENT_OPTIONS* dialog=new QT_COMPONENT_OPTIONS(window,*this);
    dialog->setWindowTitle(QString::fromStdString(name));
    OPT opt2,raw_opt2;
    opt2=opt;
    raw_opt2=raw_opt[0];
    dialog->Add("Show object in viewer",opt.draw,raw_opt[0].draw);
    dialog->Add("Name to display in viewer",opt.name,raw_opt[0].name);
    dialog->Add("vertex color",opt.vertex_color,raw_opt[0].vertex_color);
    dialog->Add("display vertices",opt.draw_vertices,raw_opt[0].draw_vertices);
    dialog->Add("edge color",opt.edge_color,raw_opt[0].edge_color);
    dialog->Add("display edges",opt.draw_edges,raw_opt[0].draw_edges);
    dialog->Add("front color of triangles",opt.color,raw_opt[0].color);
    dialog->Add("back color of triangles",opt.bgcolor,raw_opt[0].bgcolor);
    dialog->Add("render triangles as two-sided",opt.two_sided,raw_opt[0].two_sided);
    dialog->Add("velocity color",opt.velocity_color,raw_opt[0].velocity_color);
    dialog->Add("display vertex velocites",opt.draw_velocities,raw_opt[0].draw_velocities);
    dialog->Add_End_Buttons();
    int result=dialog->exec();
    if(result!=QDialog::Accepted)
    {
        opt=opt2;
        raw_opt[0]=raw_opt2;
    }
    window.update();
}
//#####################################################################
// Function Read_Option
//#####################################################################
template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
Read_Option(std::istream& in,int level)
{
    auto& o=raw_opt[level];
    o.Read_Entry(in);
    opt=raw_opt[3];
    opt.Merge(raw_opt[2]);
    opt.Merge(raw_opt[1]);
    opt.Merge(raw_opt[0]);
}
//#####################################################################
// Function Set_Draw
//#####################################################################
template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
Set_Draw(bool draw)
{
    SET(draw,draw);
    action->setChecked(Draw());
}
//#####################################################################
// Function Draw
//#####################################################################
template<class T> bool QT_TETRAHEDRALIZED_VOLUME<T>::
Draw() const
{
    return GET(draw);
}
//#####################################################################
// Function Save_Settings
//#####################################################################
template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
Save_Settings(std::ostream& out)
{
    raw_opt[0].Write(out,name);
}
//#####################################################################
// Function Clear_Settings
//#####################################################################
template<class T> void QT_TETRAHEDRALIZED_VOLUME<T>::
Clear_Settings()
{
    raw_opt[0]={};
    opt=raw_opt[3];
    opt.Merge(raw_opt[2]);
    opt.Merge(raw_opt[1]);
    opt.Merge(raw_opt[0]);
}
template class QT_TETRAHEDRALIZED_VOLUME<float>;
template class QT_TETRAHEDRALIZED_VOLUME<double>;
}
