/*---------------------------------------------------------------------------*\

    geophase: A C++ template library for geometrical operations on non-convex
    polyhedra with non-planar faces
     
    Copyright (C) 2019 Tomislav Maric, TU Darmstadt 

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.


Description
    A sequence of Polygons complian to an STL sequence container. 

Author
    Tomislav Maric maric@mma.tu-darmstadt.de

\*---------------------------------------------------------------------------*/

#ifndef Polyhedron_H
#define Polyhedron_H

#include "tag.hpp"
#include "Polygon.hpp"

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

namespace geophase 
{

/*---------------------------------------------------------------------------*\
                         Class ContainerPolyhedron.H Declaration
\*---------------------------------------------------------------------------*/

template
<
    typename Polygon,    
    template<typename, typename> class Container,
    template <typename T> class Allocator = std::allocator, 
    typename Enable = void
>
class ContainerPolyhedron;

template
<
    typename Polygon,    
    template<typename, typename> class Container,
    template <typename T> class Allocator 
>
class ContainerPolyhedron
<
    Polygon, 
    Container, 
    Allocator,  
    std::enable_if_t<tag_base_of<Polygon,sequence_polygon_tag>()>
> final
:
    public Container<Polygon, Allocator<Polygon>> 
{
    public:

        using container_type = Container<Polygon, Allocator<Polygon>>; 
        using container_type::container_type; 

        template<typename ...Args>
        void* operator new (size_t, Args...) = delete;
};


template<typename Polygon, template<typename> typename Allocator = std::allocator> 
using VectorPolyhedron = ContainerPolyhedron<Polygon, std::vector, Allocator>; 

template<typename Polygon, template <typename> typename Allocator> 
struct tag<VectorPolyhedron<Polygon, Allocator>>
{
    using type = vector_polyhedron_tag; 
};


template<typename Polygon, template<typename> typename Allocator = std::allocator> 
using ListPolyhedron = ContainerPolyhedron<Polygon, std::list, Allocator>; 

template<typename Polygon, template <typename> typename Allocator> 
struct tag<ListPolyhedron<Polygon, Allocator>>
{
    using type = list_polyhedron_tag; 
};

template<typename Polygon, template<typename> typename Allocator = std::allocator> 
using DequePolyhedron = ContainerPolyhedron<Polygon, std::deque, Allocator>; 

template<typename Polygon, template <typename> typename Allocator> 
struct tag<DequePolyhedron<Polygon, Allocator>>
{
    using type = deque_polyhedron_tag; 
};

// WKT format output 
template<typename Polyhedron> 
std::ostream& 
write_wkt_polyhedron(std::ostream& os, Polyhedron const& polyhedron)
{
    // https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry
    // MULTIPOLYGON
    os << "MULTIPOLYGON("; 

    for (const auto& polygon : polyhedron)
    {
        os << '(' << '(';
        for (const auto& point : polygon)
        {
            for (const auto cmpt : point)
                os << cmpt << ' '; 
            os << '\b' << ',' << ' '; 
        }
        for (const auto cmpt: polygon.front())
            os << cmpt << ' '; 
        os << '\b' << ')' << ')' << ',' << ' ';
    }
    os << '\b' << '\b' << ")";

    return os;
}

template
<
    typename Polygon,    
    template<typename, typename> class Container,
    template <typename T> class Allocator = std::allocator, 
    typename Enable = void
>
std::ostream& 
operator<<(
    std::ostream& os, 
    ContainerPolyhedron<Polygon, Container, Allocator, Enable> const& polyhedron 
)
{
    // https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry
    return write_wkt_polyhedron(os, polyhedron); 
}

template
<
    typename Polygon,    
    std::size_t N,  
    typename Enable = void
>
class ArrayPolyhedron;

template
<
    typename Polygon,    
    std::size_t N 
>
class ArrayPolyhedron
<
    Polygon, 
    N,
    std::enable_if_t<tag_base_of<Polygon,sequence_polygon_tag>()>
> final 
: 
    public std::array<Polygon, N> 
{
    public:

        using container_type = std::array<Polygon, N>; 
        using container_type::container_type;

        template<typename ... Args>
        constexpr ArrayPolyhedron(Args&& ... args)
            : 
                container_type{std::forward<Args>(args)...}
        {}

        // Delete new operator to prevent undefined behavior for
        // std::array*= new Vector; delete array; std::array has 
        // no virtual destructors.
        template<typename ...Args>
        void* operator new (size_t, Args...) = delete;
};

template<typename Polygon, std::size_t N>  
struct tag<ArrayPolyhedron<Polygon, N>>
{
    using type = array_polyhedron_tag; 
};

// Common polyhedra 
using vectorPolyhedron = VectorPolyhedron<vectorPolygon>; 
using listPolyhedron   = ListPolyhedron<listPolygon>; 
using dequePolyhedron  = DequePolyhedron<dequePolygon>; 
using tetrahedron      = ArrayPolyhedron<triangle, 4>; 
using hexahedron       = ArrayPolyhedron<rectangle, 6>; 

enum class polytope {TETRAHEDRON, CUBE, DODECAHEDRON}; 

template <typename Polygon, std::size_t N >
std::ostream& 
operator<<(std::ostream& os, ArrayPolyhedron<Polygon, N> const& polyhedron)
{
    return write_wkt_polyhedron(os, polyhedron); 
}

}  // End namespace geophase 

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

#endif

// ************************************************************************* //
