//#####################################################################
// Copyright 2004, Eran Guendelman.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
#include <Core/Arrays/ARRAYS_ND.h>
#include <Core/Arrays/FACE_ARRAYS.h>
#include <Core/Grids/FACE_RANGE_ITERATOR.h>
#include <Core/Grids/GRID.h>
#include <Core/Read_Write/FILE_UTILITIES.h>
#include <Core/Utilities/VIEWER_DIR.h>
#include "../QT_RENDER_UTILS.h"
#include "../QT_WORLD.h"
#include "QT_COMPONENT_OPTIONS.h"
#include "QT_FACE_FIELD.h"
#include "QT_GRID.h"
#include "QT_GRID_COMPONENT.h"
#include "QT_OBJECT.h"
#include <QAction>
namespace PhysBAM{
static const char* const face_names[3][2]={{"left","right"},{"bottom","top"},{"front","back"}};
#define SET(name,value) (opt.name.Set(value),raw_opt[0].name=opt.name)
#define GET(name) opt.name.Value()
//#####################################################################
// Constructor
//#####################################################################
template<class T,int d> QT_FACE_FIELD<T,d>::
QT_FACE_FIELD(QT_WORLD& world,const std::string& name,const std::string& grid_file,const std::shared_ptr<QT_OBJECT<TYPE>>& obj)
    :QT_GRID_COMPONENT<d>(world,name,grid_file),opt(OPT::def),raw_opt{{},{},{},OPT::def},obj(obj)
{
//    opt.set_range_from_data=[](void* t){((SELF*)t)->Set_Range_From_Data();};
}
//#####################################################################
// Constructor
//#####################################################################
template<class T,int d> QT_FACE_FIELD<T,d>::
QT_FACE_FIELD(QT_WORLD& world,const std::string& name,const std::string& file,const std::string& grid_file)
    :QT_FACE_FIELD(world,name,grid_file,QT_FILE_OBJECT<TYPE>::Make(world.viewer_dir,file))
{
}
//#####################################################################
// Destructor
//#####################################################################
template<class T,int d> QT_FACE_FIELD<T,d>::
~QT_FACE_FIELD()
{
}
//#####################################################################
// Function Get_Color
//#####################################################################
template<class T,int d> auto QT_FACE_FIELD<T,d>::
Get_Color(T value) const -> COLOR
{
    if constexpr(std::is_same<T,bool>::value)
        return GET(color);
    else if constexpr(std::is_same<T,int>::value)
        return (GET(color_map))(value);
    else return {COLOR((GET(color_map))((value-GET(min))/(GET(max)-GET(min))))};
}
//#####################################################################
// Function Display
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Render(QT_RENDER_UTILS& ru)
{
    if(!GET(draw)) return;
    Read();
    if(!obj->valid || ru.select) return;

    ARRAY<SV> X,dX;
    ARRAY<COLOR> colors;
    auto& b=obj->Obj();
    bool use_fixed_color=false;
    COLOR fixed_color;
    if constexpr(is_f)
    {
        use_fixed_color=GET(use_fixed_color);
        fixed_color=GET(color);
    }
    if(GET(draw_points) || GET(draw_diamonds))
    {
        for(FACE_RANGE_ITERATOR<d> it(b.domain);it.Valid();it.Next())
        {
            auto x=b(it.face);
            if constexpr(std::is_same<T,bool>::value)
                if(!x)
                    continue;
            if constexpr(std::is_same<T,int>::value)
                if(x<0 || x>GET(color_map).m)
                    continue;
            if(!use_fixed_color) colors.Append(Get_Color(x));
            X.Append(grid.Face_X(it.face));
        }
        if(GET(draw_diamonds))
        {
            auto old=ru.Push(ru.depth_offset,ru.depth_offset+ru.depth_offset_unit);
            if(use_fixed_color) ru.Render_Diamonds<SV>(X,grid.dX,fixed_color);
            else ru.Render_Diamonds<SV>(X,grid.dX,colors);
        }
        else
        {
            if(use_fixed_color) ru.Render_Points<SV>(X,fixed_color);
            else ru.Render_Points<SV>(X,colors);
        }
    }
    else if constexpr(!is_same<T,bool>::value && !is_same<T,int>::value)
    {
        if(GET(cell_centered))
        {
            for(RANGE_ITERATOR<d> it(b.domain);it.Valid();it.Next())
            {
                SV U;
                for(int a=0;a<d;a++)
                    for(int s=0;s<2;s++)
                        U(a)+=b(FACE_INDEX<d>(a,it.index.Add_Axis(a,s)));
                X.Append(SV(grid.Cell_X(it.index)));
                dX.Append((T).5*U);
                if(!use_fixed_color) colors.Append(Get_Color(U.Magnitude()));
            }
        }
        else
        {
            for(FACE_RANGE_ITERATOR<d> it(b.domain);it.Valid();it.Next())
            {
                SV U;
                auto u=b(it.face);
                U(it.face.axis)=u;
                X.Append(SV(grid.Face_X(it.face)));
                dX.Append(U);
                if(!use_fixed_color) colors.Append(Get_Color(u));
            }
        }
        if(use_fixed_color)
            ru.Render_Arrows<SV>(X,dX,fixed_color,{.use_arrowhead=GET(draw_arrows)});
        else ru.Render_Arrows<SV>(X,dX,colors,{.use_arrowhead=GET(draw_arrows)});
    }

    // if(draw_divergence) opengl_divergence_field->Display();
    // if(draw_vorticity) opengl_vorticity_magnitude->Display();
    // if(draw_streamlines) opengl_streamlines.Render(ru);
}
//#####################################################################
// Function Bounding_Box
//#####################################################################
template<class T,int d> auto QT_FACE_FIELD<T,d>::
Bounding_Box() -> RANGE<FV>
{
    if(!GET(draw)) return RANGE<FV>::Empty_Box();
    Read();
    if(!obj->valid) return RANGE<FV>::Empty_Box();
    return this->Bounding_Box_Helper(obj->Obj().domain);
}
//#####################################################################
// Function Print_Selection_Info
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Print_Selection_Info(std::ostream& o,const IV& index,bool cell) const
{
    if(!cell || !obj->valid) return;
    LOG::fprintf(o,"%P: \n",this->name);
    auto& b=obj->Obj();
    if(!b.domain.Lazy_Inside_Half_Open(index)) return;

    VECTOR<TV,2> U;
    for(int a=0;a<d;a++)
        for(int s=0;s<2;s++)
            U(s)(a)=b(FACE_INDEX<d>(a,index.Add_Axis(a,s)));
    if constexpr(is_same<T,bool>::value || is_same<T,int>::value)
    {
        for(int a=0;a<d;a++)
            LOG::fprintf(o,"    %P = %P, %P = %P\n",
                face_names[a][0],U(0)(a),face_names[a][1],U(1)(a));
    }
    else
    {
        TV center_velocity=U.Average();
        for(int a=0;a<d;a++)
            LOG::fprintf(o,"    %P = %P, %P = %P, avg=%P\n",
                face_names[a][0],U(0)(a),face_names[a][1],U(1)(a),center_velocity(a));
        TV dU=(U(1)-U(0))*TV(grid.one_over_dX);
        LOG::fprintf(o,"    divergence = %P   dU=%P\n",dU.Sum(),dU);
    }
}
//#####################################################################
// Function Update_Divergence
//#####################################################################
// template<class TV> void QT_FACE_VECTOR_FIELD<TV>::
// Update_Divergence()
// {
//     if(!draw_divergence || !valid){
//         divergence.Clean_Memory();
//         return;}

//     const GRID<TV>& grid=opengl_mac_velocity_field->grid;
//     static ARRAY<bool,FACE_INDEX<TV::m> > psi_N;
//     static ARRAY<bool,IV> psi_D;
//     bool got_all_psi=true;
//     std::string psi_N_filename=viewer_dir.Frame_File("psi_N");
//     std::string psi_D_filename=viewer_dir.Frame_File("psi_D");
//     if(File_Exists(psi_N_filename)) Read_From_File(psi_N_filename,psi_N);
//     else got_all_psi=false;
//     if(File_Exists(psi_D_filename)) Read_From_File(psi_D_filename,psi_D);
//     else got_all_psi=false;
//     if(!got_all_psi){
//         psi_N.Clean_Memory();
//         psi_D.Clean_Memory();}
//     divergence.Resize(grid.numbers_of_cells,init_all,0);
//     AVERAGING_UNIFORM<TV> au;
//     VECTOR<FACE_INDEX<TV::m>,4> faces;
//     for(RANGE_ITERATOR<TV::m> it(grid.Cell_Indices());it.Valid();it.Next())
//     {
//         if(got_all_psi)
//         {
//             if(psi_D(it.index)) continue;
//             grid.Neighboring_Faces(faces,it.index);
//             if(psi_N.Subset(faces).Contains(true)) continue;
//         }
//         divergence(it.index)=au.Cell_Centered_Divergence(grid,opengl_mac_velocity_field->data,it.index);
//     }
//     opengl_divergence_field->Update();
// }
//#####################################################################
// Function Update_Streamlines
//#####################################################################
// template<class TV> void QT_FACE_VECTOR_FIELD<TV>::
// Update_Streamlines()
// {
//     streamlines.Clean_Memory();streamlines.particles.Clean_Memory();streamlines.mesh.Clean_Memory();
//     if(!draw_streamlines || !valid) return;
    
//     GRID<TV>& grid=opengl_mac_velocity_field->grid;
//     int number_of_streamlines=100;
//     T step_length=(T).5*grid.dX.Min();

//     RANDOM_NUMBERS<T> random;
//     if(use_seed_for_streamlines) random.Set_Seed(streamline_seed);
//     LINEAR_INTERPOLATION_FACE<TV,T> linear_interpolation;
//     ARRAY<T,FACE_INDEX<TV::m> > mac_velocity_field(opengl_mac_velocity_field->data);
//     ARRAY<typename TV::SCALAR,FACE_INDEX<TV::m> > V_lookup(mac_velocity_field);

//     for(int i=0;i<number_of_streamlines;i++){
//         int p=streamlines.particles.Add_Element();
//         TV X;
//         random.Fill_Uniform(X,grid.domain);
//         streamlines.particles.X(p)=X;
//         for(int step=0;step<number_of_steps;step++){
//             TV velocity=linear_interpolation.Interpolate_Face(grid,mac_velocity_field,X);
//             TV X_new=X+step_length*velocity;
//             velocity=(T).5*(velocity+linear_interpolation.Interpolate_Face(grid,mac_velocity_field,X_new));
//             X_new=grid.domain.Clamp(X+step_length*velocity);
//             int new_particle=streamlines.particles.Add_Element();
//             streamlines.particles.X(new_particle)=X_new;
//             streamlines.mesh.elements.Append(VECTOR<int,2>(p,new_particle));
//             p=new_particle;
//             X=X_new;}}
// }
//#####################################################################
// Function Read
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Read()
{
    this->ptr->Read();
    obj->Read();
}
//#####################################################################
// Function Invalidate
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Invalidate(bool invalidate_common)
{
    obj->Invalidate(invalidate_common);
}
//#####################################################################
// Function Set_Scale_Range
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Set_Range_From_Data()
{
    Read();
    if(!obj->valid) return;
    if constexpr(is_f)
    {
        auto& a=obj->Obj();
        if(a.array.m)
        {
            SET(min,a.array.Min());
            SET(max,a.array.Max());
        }
        else
        {
            SET(min,0);
            SET(max,1);
        }
    }
}
//#####################################################################
// Function Toggle_Velocity_Mode
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Toggle_Velocity_Mode()
{
    SET(cell_centered,!GET(cell_centered));
}
//#####################################################################
// Function Toggle_Velocity_Mode_And_Draw
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Toggle_Velocity_Mode_And_Draw()
{
    if(GET(draw))
    {
        SET(draw,false);
        SET(cell_centered,true);
    }
    else
    {
        SET(draw,true);
        SET(cell_centered,false);
    }
}
//#####################################################################
// Function Toggle_Arrowhead
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Toggle_Arrowhead()
{
    if constexpr(is_f)
        SET(draw_arrows,!GET(draw_arrows));
}
//#####################################################################
// Function Toggle_Draw_Divergence
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Toggle_Draw_Divergence()
{
    if constexpr(is_f) SET(draw_divergence,!GET(draw_divergence));
}
//#####################################################################
// Function Toggle_Draw_Streamlines
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Toggle_Draw_Streamlines()
{
    if constexpr(is_f) SET(draw_streamlines,!GET(draw_streamlines));
}
//#####################################################################
// Function Toggle_Use_Streamline_Seed
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Toggle_Use_Streamline_Seed()
{
    if constexpr(is_f) SET(use_seed_for_streamlines,!GET(use_seed_for_streamlines));
}
//#####################################################################
// Function Set_Streamline_Seed
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Set_Streamline_Seed(const unsigned int seed)
{
    if constexpr(is_f) SET(streamline_seed,seed);
}
//#####################################################################
// Function Lengthen_Streamlines
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Lengthen_Streamlines()
{
    if constexpr(is_f) SET(number_of_steps,GET(number_of_steps)+10);
    Update_Streamlines();
}
//#####################################################################
// Function Shorten_Streamlines
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Shorten_Streamlines()
{
    if constexpr(is_f) SET(number_of_steps,max(GET(number_of_steps)-10,0));
    Update_Streamlines();
}
//#####################################################################
// Function Shorten_Streamlines
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Update_Streamlines()
{
    PHYSBAM_WARNING("TODO: not implemented");
}
//#####################################################################
// Function Toggle_Draw_Vorticity
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Toggle_Draw_Vorticity()
{
    if constexpr(is_f) SET(draw_vorticity,!GET(draw_vorticity));
}
//#####################################################################
// Function Normalize_Vorticity_Color_Map
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Normalize_Vorticity_Color_Map()
{
    if constexpr(is_f) if(!GET(draw_vorticity)) return;
    // opengl_vorticity_magnitude->Set_Scale_Range(min_vorticity,max_vorticity);
    // valid=false;
    // Reinitialize();
}
//#####################################################################
// Function Update_Vorticity
//#####################################################################
// template<class T,int d> void QT_FACE_FIELD<T,d>::
// Update_Vorticity()
// {
//     GRID<TV>& grid=opengl_mac_velocity_field->grid;
//     RANGE<VECTOR<int,2> > domain_indices(grid.Cell_Indices());domain_indices.Change_Size(-VECTOR<int,2>::All_Ones_Vector());
//     opengl_vorticity_magnitude->values.Resize(grid.Cell_Indices());
//     for(CELL_ITERATOR<TV> iterator(grid,domain_indices);iterator.Valid();iterator.Next()){VECTOR<int,2> index=iterator.Cell_Index();
//         T vorticity_magnitude=VORTICITY_UNIFORM<TV>::Vorticity(grid,opengl_mac_velocity_field->data,index).Magnitude();
//         opengl_vorticity_magnitude->values(index)=vorticity_magnitude;
//         min_vorticity=min(min_vorticity,vorticity_magnitude);
//         max_vorticity=max(max_vorticity,vorticity_magnitude);}
// }
//#####################################################################
// Function Update
//#####################################################################
// template<class T,int d> void QT_FACE_FIELD<T,d>::
// Update()
// {
//     vector_field.Remove_All();
//     vector_locations.Remove_All();
//     if(velocity_mode==FACE_CENTERED)
//         for(FACE_RANGE_ITERATOR<TV::m> it(data.domain);it.Valid();it.Next()){
//             vector_locations.Append(grid.Face_X(it.face));
//             vector_field.Append(data(it.face)*TV::Axis_Vector(it.face.axis));}
//     else{
//         AVERAGING_UNIFORM<TV> au;
//         for(RANGE_ITERATOR<TV::m> it(data.domain);it.Valid();it.Next()){
//             vector_locations.Append(grid.Cell_X(it.index));
//             vector_field.Append(au.Face_To_Cell_Vector(grid,it.index,data));}}
// }
//#####################################################################
// Function Make_Dialog
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
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("cell_centered",opt.cell_centered,raw_opt[0].cell_centered);
    dialog->Add("draw_points",opt.draw_points,raw_opt[0].draw_points);
    dialog->Add("draw_diamonds",opt.draw_diamonds,raw_opt[0].draw_diamonds);
    if constexpr(!is_same<T,int>::value)
        dialog->Add("color",opt.color,raw_opt[0].color);
    if constexpr(!is_same<T,bool>::value)
        dialog->Add("color_map",opt.color_map,raw_opt[0].color_map);
    if constexpr(is_f)
    {
        dialog->Add("use color instead of color_map",opt.use_fixed_color,raw_opt[0].use_fixed_color);
        dialog->Add("draw_arrows",opt.draw_arrows,raw_opt[0].draw_arrows);
        dialog->Add("Map this value to 0 before color map",opt.min,raw_opt[0].min);
        dialog->Add("Map this value to 1 before color map",opt.max,raw_opt[0].max);
        dialog->Add_Button("Compute min and max from data",[this](){Set_Range_From_Data();});
        dialog->Add("number_of_steps",opt.number_of_steps,raw_opt[0].number_of_steps);
        dialog->Add("min_vorticity",opt.min_vorticity,raw_opt[0].min_vorticity);
        dialog->Add("max_vorticity",opt.max_vorticity,raw_opt[0].max_vorticity);
        dialog->Add("streamline_seed",opt.streamline_seed,raw_opt[0].streamline_seed);
        dialog->Add("draw_vorticity",opt.draw_vorticity,raw_opt[0].draw_vorticity);
        dialog->Add("draw_divergence",opt.draw_divergence,raw_opt[0].draw_divergence);
        dialog->Add("draw_streamlines",opt.draw_streamlines,raw_opt[0].draw_streamlines);
        dialog->Add("use_seed_for_streamlines",opt.use_seed_for_streamlines,raw_opt[0].use_seed_for_streamlines);
    }
    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,int d> void QT_FACE_FIELD<T,d>::
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,int d> void QT_FACE_FIELD<T,d>::
Set_Draw(bool draw)
{
    SET(draw,draw);
    action->setChecked(Draw());
}
//#####################################################################
// Function Draw
//#####################################################################
template<class T,int d> bool QT_FACE_FIELD<T,d>::
Draw() const
{
    return GET(draw);
}
//#####################################################################
// Function Save_Settings
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
Save_Settings(std::ostream& out)
{
    raw_opt[0].Write(out,name);
}
//#####################################################################
// Function Clear_Settings
//#####################################################################
template<class T,int d> void QT_FACE_FIELD<T,d>::
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_FACE_FIELD<int,2>;
template class QT_FACE_FIELD<bool,2>;
template class QT_FACE_FIELD<float,2>;
template class QT_FACE_FIELD<double,2>;
template class QT_FACE_FIELD<int,3>;
template class QT_FACE_FIELD<bool,3>;
template class QT_FACE_FIELD<float,3>;
template class QT_FACE_FIELD<double,3>;
}
