//#####################################################################
// Copyright 2007-2009, Jon Gretarsson, Geoffrey Irving, Nipun Kwatra, Andrew Selle, Jonathan Su.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class POINT_SIMPLEX_1D  
//##################################################################### 
#ifndef __POINT_SIMPLEX_1D__
#define __POINT_SIMPLEX_1D__

#include <Core/Basic_Geometry/BASIC_GEOMETRY_FORWARD.h>
#include <Core/Math_Tools/FIXED_NUMBER.h>
#include <Core/Matrices/SYMMETRIC_MATRIX.h>
#include <Core/Vectors/VECTOR.h>
namespace PhysBAM{

template<class TV> class RANGE;

template<class T>
class POINT_SIMPLEX_1D
{
    typedef VECTOR<T,1> TV;
public:
    typedef TV VECTOR_T;

    VECTOR<TV,1> X;
    bool direction; // false for -1 and true for 1 direction
 
    POINT_SIMPLEX_1D()
        :direction(true)
    {}

    POINT_SIMPLEX_1D(const TV& x0,const bool direction)
        :X(x0),direction(direction)
    {}

    template<class T_ARRAY>
    explicit POINT_SIMPLEX_1D(const T_ARRAY& X_input)
        :X(X_input(0)),direction(true)
    {
        static_assert(T_ARRAY::fixed_m<0 || T_ARRAY::fixed_m==1);
        if constexpr(T_ARRAY::fixed_m<0){assert(X_input.Size()==1);}
        // TODO: initialize directions properly
    }

    const TV& Center() const
    {return X.x;}

    T Size() const
    {return (T)1;}

    static TV Normal(const TV& x0,const int direction)
    {return TV((T)(direction?1:-1));}

    TV Normal() const
    {return POINT_SIMPLEX_1D<T>::Normal(X.x,direction);}

    TV Normal(const TV& location) const
    {return Normal();}

    SYMMETRIC_MATRIX<T,1> Hessian(const TV& X) const
    {return SYMMETRIC_MATRIX<T,1>();}

    static FIXED_NUMBER<T,1> Clamped_Barycentric_Coordinates(const TV& location,const TV& x0)
    {return FIXED_NUMBER<T,1>();}

    template<class T_ARRAY>
    static enable_if_t<is_same<typename T_ARRAY::ELEMENT,TV>::value,FIXED_NUMBER<T,1>>
    Clamped_Barycentric_Coordinates(const TV& location,const T_ARRAY& X)
    {
        static_assert(T_ARRAY::fixed_m<0 || T_ARRAY::fixed_m==1);
        if constexpr(T_ARRAY::fixed_m<0){assert(X.Size()==1);}
        return FIXED_NUMBER<T,1>();
    }

    TV Sum_Barycentric_Coordinates(const POINT_SIMPLEX_1D<T>& embedded_point_simplex) const
    {return TV::All_Ones_Vector();} // TODO

    FIXED_NUMBER<T,1> Barycentric_Coordinates(const TV& location) const
    {return FIXED_NUMBER<T,1>();}

    static TV Point_From_Barycentric_Coordinates(const FIXED_NUMBER<T,1>,const TV& x0)
    {return x0;}

    TV Point_From_Barycentric_Coordinates(const TV& weights)
    {return X.x;}

    template<class T_ARRAY>
    static enable_if_t<is_same<typename T_ARRAY::ELEMENT,TV>::value,TV>
    Point_From_Barycentric_Coordinates(const FIXED_NUMBER<T,1>,const T_ARRAY& X)
    {
        static_assert(T_ARRAY::fixed_m<0 || T_ARRAY::fixed_m==1);
        if constexpr(T_ARRAY::fixed_m<0){assert(X.Size()==1);}
        return X(0);
    }

    TV Point_From_Barycentric_Coordinates(const FIXED_NUMBER<T,1>) const
    {return X.x;}

    RANGE<TV> Bounding_Box() const
    {return RANGE<TV>(X.x);}

    T Signed_Distance(const TV& location) const
    {return (direction?(T)1:(T)-1)*(location.x-X.x.x);}

    void Clip_To_Box(const RANGE<TV>& box,ARRAY<POINT_SIMPLEX_1D<T> >& clipped_simplices) const
    {clipped_simplices.Remove_All();
    if(box.Lazy_Inside(X.x)) clipped_simplices.Append(*this);}

    bool Inside(const TV& point,const T thickness_over_two=0) const
    {if(X.x.x-thickness_over_two <= point.x && X.x.x+thickness_over_two >= point.x) return true;
    else return false;}

    static std::string Name()
    {return "POINT_SIMPLEX_1D<T>";}

    static bool Point_Face_Collision(const POINT_SIMPLEX_1D<T>& initial_simplex,const VECTOR<T,1>& x,const VECTOR<T,1>& v,const VECTOR<T,1>& v1,const T dt,const T collision_thickness,
        T& collision_time,VECTOR<T,1>& normal,VECTOR<T,2>& weights,const bool exit_early);
    static POINT_SIMPLEX_COLLISION_TYPE Robust_Point_Face_Collision(const POINT_SIMPLEX_1D<T>& initial_simplex,const POINT_SIMPLEX_1D<T>& final_simplex,
        const VECTOR<T,1>& x,const VECTOR<T,1>& final_x,const T dt,const T collision_thickness,T& collision_time,VECTOR<T,1>& normal,VECTOR<T,2>& weights);
    template<class RW> void Read(UNTYPED_ISTREAM<RW>& input);
    template<class RW> void Write(UNTYPED_OSTREAM<RW>& output) const;
};
template<class T> class RAY_HIT;
template<class TV> class RAY;

template<class T,class TV> POINT_SIMPLEX_1D<T>
Scaled(const POINT_SIMPLEX_1D<T>& b,const TV& center,T scale)
{
    return POINT_SIMPLEX_1D<T>((b.X-center)*scale+center,b.direction);
}
template<class T,class TV> POINT_SIMPLEX_1D<T>
Transformed(const POINT_SIMPLEX_1D<T>& b,const FRAME<TV>& f)
{
    return POINT_SIMPLEX_1D<T>(f*b.X,b.direction);
}
template<class T> RAY_HIT<T> Intersects(const RAY<VECTOR<T,1> >& ray,
    const POINT_SIMPLEX_1D<T>& point,const T thickness_over_two=0);
template<class T> RAY_HIT<T> Closest_Non_Intersecting_Point(
    const RAY<VECTOR<T,1> >& ray,const POINT_SIMPLEX_1D<T>& point,
    const T thickness_over_two=0);
template<class T> VECTOR<RAY_HIT<T>,2>
Intersection_Interval(const RAY<VECTOR<T,3> >& ray,
    const POINT_SIMPLEX_1D<T>& point,T isocontour=0);
template<class TV,class T> void
All_Intersections(ARRAY<RAY_HIT<T> >& hits,const RAY<TV>& ray,
    const POINT_SIMPLEX_1D<T>& point,const T isocontour=0);
}
#endif
