//#####################################################################
// Copyright 2004-2008, Ron Fedkiw, Eran Guendelman, Geoffrey Irving, Frank Losasso, Andrew Selle.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class LEVELSET_MAKER
//##################################################################### 
#ifndef __LEVELSET_MAKER__
#define __LEVELSET_MAKER__

#include <Core/Arrays/ARRAY.h>
#include <Core/Arrays/ARRAYS_FORWARD.h>
#include <Core/Grids/GRID.h>
#include <Core/Meshes/TRIANGULATED_SURFACE.h>
#include <Core/Vectors/VECTOR.h>
namespace PhysBAM{

template<class T>
class LEVELSET_MAKER
{
protected:
    typedef VECTOR<int,3> IV;typedef VECTOR<T,3> TV;
public:
    bool verbose;
    bool compute_signed_distance_function,compute_unsigned_distance_function,compute_heaviside_function;
    bool use_fmm;
    T fmm_one_sided_band_width; // in number of cells (if zero then compute phi everywhere)
    bool extrapolate_velocity;
    T velocity_extrapolation_one_sided_band_width; // in number of cells
    bool only_boundary_region_is_outside;
    bool keep_only_largest_inside_region;
    bool flip_sign_if_corners_are_inside;
    T surface_thickness_or_zero; // if zero, use grid.dx/100
    T surface_padding_for_flood_fill_or_negative; // if negative, use grid.dx/100 (0 means no padding)
    ARRAY<VECTOR<int,3> > initialized_indices;
    bool write_debug_data;
    bool write_debug_path;
    VECTOR<int,3> path_start_node,path_end_node;
    bool use_orthogonal_vote;
    int positive_boundary_band;
    T remove_degenerate_triangles_area_threshold;
    T phi_offset;
   
    LEVELSET_MAKER()
    {
        Verbose_Mode(false);
        Write_Debug_Data(false);
        Write_Debug_Path(false);
        Compute_Signed_Distance_Function();
        Use_Fast_Marching_Method();
        Extrapolate_Velocity(false);
        Only_Boundary_Region_Is_Outside(false);
        Keep_Only_Largest_Inside_Region(false);
        Flip_Sign_If_Corners_Are_Inside(false);
        Set_Surface_Thickness();
        Set_Surface_Padding_For_Flood_Fill();
        Use_Orthogonal_Vote(false);
        Set_Positive_Boundary_Band(0);
        Use_Remove_Degenerate_Triangles();
        Set_Phi_Offset();
    }
    LEVELSET_MAKER(const LEVELSET_MAKER&) = delete;
    void operator=(const LEVELSET_MAKER&) = delete;

    void Verbose_Mode(const bool verbose_input=true)
    {verbose=verbose_input;}

    void Write_Debug_Data(const bool write_debug_data_input=true)
    {write_debug_data=write_debug_data_input;}

    void Write_Debug_Path(const bool write_debug_path_input=true,const VECTOR<int,3>& path_start_node_input=IV(),const VECTOR<int,3>& path_end_node_input=IV())
    {write_debug_path=write_debug_path_input;if(write_debug_path){path_start_node=path_start_node_input;path_end_node=path_end_node_input;}}

    void Compute_Signed_Distance_Function()
    {compute_signed_distance_function=true;compute_unsigned_distance_function=false;compute_heaviside_function=false;}

    void Compute_Unsigned_Distance_Function()
    {compute_signed_distance_function=false;compute_unsigned_distance_function=true;compute_heaviside_function=false;}

    void Compute_Heaviside_Function()
    {compute_signed_distance_function=false;compute_unsigned_distance_function=false;compute_heaviside_function=true;}

    // fmm_one_sided_band_width_input==0 indicates an infinite band width (run fmm everywhere)
    void Use_Fast_Marching_Method(const bool use_fmm_input=true,const T fmm_one_sided_band_width_input=0)
    {use_fmm=use_fmm_input;if(use_fmm)fmm_one_sided_band_width=fmm_one_sided_band_width_input;}

    void Extrapolate_Velocity(const bool extrapolate_velocity_input=true,const T velocity_extrapolation_one_sided_band_width_input=3)
    {extrapolate_velocity=extrapolate_velocity_input;if(extrapolate_velocity)velocity_extrapolation_one_sided_band_width=velocity_extrapolation_one_sided_band_width_input;}

    void Only_Boundary_Region_Is_Outside(const bool only_boundary_region_is_outside_input=true)
    {only_boundary_region_is_outside=only_boundary_region_is_outside_input;}

    void Keep_Only_Largest_Inside_Region(const bool keep=true)
    {keep_only_largest_inside_region=keep;}

    void Flip_Sign_If_Corners_Are_Inside(const bool flip=true)
    {flip_sign_if_corners_are_inside=flip;}

    void Set_Surface_Thickness(const T surface_thickness_input=0) // zero means grid.dx/100
    {surface_thickness_or_zero=surface_thickness_input;}

    void Set_Surface_Padding_For_Flood_Fill(const T surface_padding_for_flood_fill_input=-1) // negative means grid.dx/100
    {surface_padding_for_flood_fill_or_negative=surface_padding_for_flood_fill_input;}

    void Use_Orthogonal_Vote(const bool use_orthogonal_vote_input=true)
    {use_orthogonal_vote=use_orthogonal_vote_input;}

    void Set_Positive_Boundary_Band(const int positive_boundary_band_input=0)
    {positive_boundary_band=positive_boundary_band_input;}

    void Use_Remove_Degenerate_Triangles(const bool remove_degenerate_triangles_input=true,const T area_threshold_input=1e-15)
    {if(!remove_degenerate_triangles_input) remove_degenerate_triangles_area_threshold=0;
    else remove_degenerate_triangles_area_threshold=area_threshold_input;}

    void Set_Phi_Offset(const T phi_offset_input=0)
    {phi_offset=phi_offset_input;}

    bool Compute_Level_Set(TRIANGULATED_SURFACE<T>& trianglated_surface,GRID<TV>& grid,AT_NODES,ARRAY<T,IV>& phi,ARRAY<TV,IV>* velocity=0);
    bool Compute_Level_Set(TRIANGULATED_SURFACE<T>& trianglated_surface,GRID<TV>& grid,AT_CELLS,ARRAY<T,IV>& phi,ARRAY<TV,IV>* velocity=0)
    {GRID<TV> reg_grid=grid.Get_Regular_Grid_At_MAC_Positions();return Compute_Level_Set(trianglated_surface,reg_grid,at_nodes,phi,velocity);}
protected:
    template<class TV> T Surface_Thickness_Over_Two(const GRID<TV>& grid) const
    {return (T).5*(surface_thickness_or_zero?surface_thickness_or_zero:grid.dX.Min()/100);}

    template<class TV> T Surface_Padding_For_Flood_Fill(const GRID<TV>& grid) const
    {return surface_padding_for_flood_fill_or_negative>=0?surface_padding_for_flood_fill_or_negative:grid.dX.Min()/100;}
};
}
#endif
