//#####################################################################
// Copyright 2020, Craig Schroeder.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class TRANSFORMED_OBJECT
//##################################################################### 
#ifndef __TRANSFORMED_OBJECT__
#define __TRANSFORMED_OBJECT__
#include <Core/Math_Tools/RANGE.h>
#include <Core/Matrices/FRAME.h>
#include <Core/Matrices/SYMMETRIC_MATRIX.h>
#include <Core/Meshes/MESH_OBJECT.h>
#include <Core/Meshes/MESHES_FORWARD.h>
#include <iosfwd>
#include <string>
namespace PhysBAM{

template<class TV,class OBJECT>
class TRANSFORMED_OBJECT
{
    typedef typename TV::SCALAR T;
public:
    OBJECT object;
    FRAME<TV> frame;

    TRANSFORMED_OBJECT()=default;
    TRANSFORMED_OBJECT(const OBJECT& object,const FRAME<TV>& frame)
        :object(object),frame(frame)
    {}

//#####################################################################
    RANGE<TV> Bounding_Box() const;
    T Signed_Distance(const TV& X) const;
    TV Surface(const TV& X) const;
    TV Normal(const TV& X) const;
    TV Normal(const TV& X,const int aggregate) const;
    SYMMETRIC_MATRIX<T,TV::m> Hessian(const TV& X) const;
    bool Lazy_Inside(const TV& X) const;
    bool Lazy_Outside(const TV& X) const;
    bool Inside(const TV& X,const T thickness_over_two=0) const;
    bool Outside(const TV& X,const T thickness_over_two=0) const;
    bool Boundary(const TV& X,const T thickness_over_two) const;
    static std::string Name();
    template<class RW> void Read(UNTYPED_ISTREAM<RW>& input);
    template<class RW> void Write(UNTYPED_OSTREAM<RW>& output) const;
//#####################################################################
};

template<class TV,class OBJECT>
auto Scaled(const TRANSFORMED_OBJECT<TV,OBJECT>& o,
    const TV& center,typename TV::SCALAR scale)
{
    FRAME<TV> f((o.frame.t-center)*scale+center,o.frame.r);
    return Transformed(Scaled(o.object,TV(),scale),f);
}
template<class TV,class OBJECT>
TRANSFORMED_OBJECT<TV,OBJECT> Transformed(const TRANSFORMED_OBJECT<TV,OBJECT>& o,
    const FRAME<TV>& f)
{
    return {o.object,f*o.frame};
}
template<class TV,class OBJECT,class... Args> auto
Generate_Triangles(const TRANSFORMED_OBJECT<TV,OBJECT>& o,Args&&... args)
    -> std::enable_if_t<sizeof(Generate_Triangles(o.object,args...))!=0,MESH_OBJECT<TV,2>*>
{
    auto mo=Generate_Triangles(o.object,args...);
    for(auto&x:mo->particles.X) x=o.frame*x;
    return mo;
}
template<class TV,class OBJECT,class... Args> auto
Tessellate_Boundary(const TRANSFORMED_OBJECT<TV,OBJECT>& o,Args&&... args)
    -> std::enable_if_t<sizeof(Tessellate_Boundary(o.object,args...))!=0,MESH_OBJECT<TV,TV::m-1>*>
{
    auto mo=Tessellate_Boundary(o.object,args...);
    for(auto&x:mo->particles.X) x=o.frame*x;
    return mo;
}
}
#endif
