//#####################################################################
// Copyright 2003-2009, Eran Guendelman, Geoffrey Irving, Sndrew Selle.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
#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_GRID.h"
#include "QT_GRID_COMPONENT.h"
#include "QT_OBJECT.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<int d> QT_GRID<d>::
QT_GRID(QT_WORLD& world,const std::string& name,const std::shared_ptr<QT_OBJECT<TYPE>>& obj)
    :QT_COMPONENT(world,name),opt(OPT::def),raw_opt{{},{},{},OPT::def},obj(obj)
{
}
//#####################################################################
// Constructor
//#####################################################################
template<int d> QT_GRID<d>::
QT_GRID(QT_WORLD& world,const std::string& name,const std::string& file)
    :QT_GRID(world,name,QT_FILE_OBJECT<TYPE>::Make(world.viewer_dir,file))
{
}
//#####################################################################
// Destructor
//#####################################################################
template<int d> QT_GRID<d>::
~QT_GRID()
{
}
//#####################################################################
// Function Display
//#####################################################################
template<int d> void QT_GRID<d>::
Render(QT_RENDER_UTILS& ru)
{
    if(!GET(draw)) return;
    Read();
    if(!obj->valid) return;
    for(auto o:grid_objects)
        if(o->Draw())
            o->Read();

    auto& grid=obj->Obj();
    RANGE<TV> domain=grid.Ghost_Domain(GET(ghost));
    TV dX=grid.dX;
    if(ru.select)
    {
        ru.pick.Push(0);
        ARRAY<TV> pts;
        for(RANGE_ITERATOR<TV::m> it(grid.Node_Indices(GET(ghost)));it.Valid();it.Next())
            pts.Append(grid.Node_X(it.index));
        {
            auto old=ru.Push(ru.depth_offset,ru.depth_offset-ru.depth_offset_unit);
            ru.Render_Points<TV>(pts,{});
        }
        ru.pick.Set(1);
        pts.Remove_All();
        for(RANGE_ITERATOR<TV::m> it(grid.Cell_Indices(GET(ghost)));it.Valid();it.Next())
            pts.Append(grid.Cell_X(it.index));
        ru.Render_Boxes<TV>(pts,grid.dX,{},{});
        ru.pick.Pop();
        return;
    }

    ARRAY<TV> pts;
    TV mx=grid.Node_X(grid.numbers_of_cells+GET(ghost));
    for(int a=0;a<TV::m;a++)
    {
        RANGE<IV> r=grid.Node_Indices(GET(ghost));
        r.max_corner(a)=r.min_corner(a)+1;
        for(RANGE_ITERATOR<TV::m> it(r);it.Valid();it.Next())
        {
            TV X=grid.Node_X(it.index);
            pts.Append(X);
            X(a)=mx(a);
            pts.Append(X);
        }
    }
    ru.Render_Lines<TV>(pts,GET(color));
    ru.Render_Box(grid.domain,GET(color),{.line_width=2*ru.line_width,.fill=false});

    auto old=ru.Push(ru.depth_offset,ru.depth_offset-ru.depth_offset_unit*2);
    if(has_selected_cell)
    {
        LOG::fprintf(ru.info_text,"cell %P\n",selected_cell);
        ru.Render_Box(grid.Cell_Domain(selected_cell),
            ru.highlight_color,{.line_width=ru.highlighted_line_width,.fill=false});
        for(auto o:grid_objects)
            if(o->Draw())
                o->Print_Selection_Info(ru.info_text,selected_cell,true);
    }
    if(has_selected_node)
    {
        LOG::fprintf(ru.info_text,"node %P\n",selected_node);
        ru.Render_Point(grid.Node_X(selected_node),
            ru.highlight_color,{.point_size=ru.highlighted_point_size});
        for(auto o:grid_objects)
            if(o->Draw())
                o->Print_Selection_Info(ru.info_text,selected_node,false);
    }
}
//#####################################################################
// Function Bounding_Box
//#####################################################################
template<int d> auto QT_GRID<d>::
Bounding_Box() -> RANGE<FV>
{
    if(!GET(draw)) return RANGE<FV>::Empty_Box();
    Read();
    if(!obj->valid) return RANGE<FV>::Empty_Box();
    return RANGE<FV>(obj->Obj().Ghost_Domain(GET(ghost)));
}
//#####################################################################
// Function Get_Selection
//#####################################################################
template<int d> void QT_GRID<d>::
Set_Selection(ARRAY_VIEW<int> selection)
{
    Read();
    if(!obj->valid) return;
    auto& grid=obj->Obj();

    has_selected_node=false;
    has_selected_cell=false;
    if(selection(0)==0)
    {
        int i=0;
        for(RANGE_ITERATOR<TV::m> it(grid.Node_Indices(GET(ghost)));it.Valid();it.Next(),i++)
            if(i==selection(1))
            {
                selected_node=it.index;
                has_selected_node=true;
                break;
            }
    }
    else
    {
        int i=0;
        for(RANGE_ITERATOR<TV::m> it(grid.Cell_Indices(GET(ghost)));it.Valid();it.Next(),i++)
            if(i==selection(1))
            {
                selected_cell=it.index;
                has_selected_cell=true;
                break;
            }
    }
}
//#####################################################################
// Function Clear_Selection
//#####################################################################
template<int d> void QT_GRID<d>::
Clear_Selection()
{
    has_selected_node=false;
    has_selected_cell=false;
}
//#####################################################################
// Function Reinitialize
//#####################################################################
template<int d> void QT_GRID<d>::
Read()
{
    obj->Read();
}
//#####################################################################
// Function Invalidate
//#####################################################################
template<int d> void QT_GRID<d>::
Invalidate(bool invalidate_common)
{
    obj->Invalidate(invalidate_common);
}
//#####################################################################
// Function Add_Object
//#####################################################################
template<int d> void QT_GRID<d>::
Add_Object(QT_GRID_COMPONENT<TV::m>& obj)
{
    obj.iter=grid_objects.insert(grid_objects.end(),&obj);
}
//#####################################################################
// Function Remove_Object
//#####################################################################
template<int d> void QT_GRID<d>::
Remove_Object(QT_GRID_COMPONENT<TV::m>& obj)
{
    grid_objects.erase(obj.iter);
    obj.iter=grid_objects.end();
}
//#####################################################################
// Function Selection_Point
//#####################################################################
template<int d> auto QT_GRID<d>::
Selection_Point() -> FV
{
    Read();
    PHYSBAM_ASSERT(obj->valid);
    auto& grid=obj->Obj();
    if(has_selected_cell)
        return FV(grid.Cell_X(selected_cell));
    if(has_selected_node)
        return FV(grid.Node_X(selected_cell));
    PHYSBAM_FATAL_ERROR();
}
//#####################################################################
// Function Make_Dialog
//#####################################################################
template<int d> void QT_GRID<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("color",opt.color,raw_opt[0].color);
    dialog->Add("ghost",opt.ghost,raw_opt[0].ghost);
    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<int d> void QT_GRID<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<int d> void QT_GRID<d>::
Set_Draw(bool draw)
{
    SET(draw,draw);
    action->setChecked(Draw());
}
//#####################################################################
// Function Draw
//#####################################################################
template<int d> bool QT_GRID<d>::
Draw() const
{
    return GET(draw);
}
//#####################################################################
// Function Save_Settings
//#####################################################################
template<int d> void QT_GRID<d>::
Save_Settings(std::ostream& out)
{
    raw_opt[0].Write(out,name);
}
//#####################################################################
// Function Clear_Settings
//#####################################################################
template<int d> void QT_GRID<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_GRID<2>;
template class QT_GRID<3>;
}
