/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     |
    \\  /    A nd           | Copyright (C) 2012 OpenFOAM Foundation
     \\/     M anipulation  |
-------------------------------------------------------------------------------
License
    This file is part of OpenFOAM.

    OpenFOAM 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.

    OpenFOAM 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 OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.

Description

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

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

#ifndef DoIntersect_HPP
#define DoIntersect_HPP

#include "tag.hpp"
#include "Distance.hpp"
#include "IsInside.hpp"

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

namespace geophase { 

    struct no_test 
    {
        template <typename FirstGeometry, typename SecondGeometry>
        static constexpr bool apply(FirstGeometry const&, SecondGeometry const&)
        {
            return true; 
        }
    };

    template <typename Polygon,  typename Halfspace>
    std::enable_if_t
    <
        tag_base_of<Polygon, sequence_polygon_tag>() &&
        type_tagged<Halfspace, halfspace_tag>(),
        bool 
    >
    do_intersect_error(
        Polygon const& polygon, 
        Halfspace const& hspace
    )
    {
        auto firstPointStatus = is_inside_error(polygon.front(), hspace); 

        for (const auto& point : polygon)
        {
            auto pointStatus = is_inside_error(point, hspace); 
            if ((pointStatus != position::COINCIDENT) && 
                (pointStatus != firstPointStatus))
                return true;
        }

        return false; 
    }

    template <typename Polyhedron,  typename Halfspace>
    std::enable_if_t
    <
        tag_base_of<Polyhedron, sequence_polyhedron_tag>() &&
        type_tagged<Halfspace, halfspace_tag>(),
        bool 
    >
    do_intersect_error(
        Polyhedron const& polyhedron, 
        Halfspace const& hspace
    )
    {
        for (const auto& polygon : polyhedron)
            if (do_intersect_error(polygon, hspace))
                return true;

        return false; 
    }

    //template <typename Halfspace,  typename Point>
    //typename std::enable_if
    //<
        //Traits::tag_enabled<Halfspace, halfspace_tag>::value &&
        //Traits::tag_enabled<Point, point_tag>::value,
        //bool 
    //>::type
    //do_intersect(
        //Halfspace const & halfspace, 
        //Point const & p1,
        //Point const & p2,
        //const double tolerance = Traits::tolerance<Halfspace>::value()
    //)
    //{
        //auto p1dist = distance(p1, halfspace);  
        //auto p2dist = distance(p2, halfspace);  

        //// FIXME: Derive the tolerance. TM.
        //return ( 
            //((p1dist * p2dist) <= 2*tolerance) &&
            //(
                //(mag(p1dist) > 2*tolerance) || 
                //(mag(p2dist) > 2*tolerance)
            //)
        //);
    //}

    //template <typename Box>
    //typename std::enable_if
    //<
        //Traits::tag_enabled<Box, aabbox_tag>::value,
        //bool 
    //>::type
    //do_intersect(
        //Box const & b1,
        //Box const & b2,
        //const double tolerance = Traits::tolerance<Box>::value()
    //)
    //{
        //return aabb_do_intersect(b1, b2); 
    //}

    //template <typename Halfspace,  typename Point>
    //typename std::enable_if
    //<
        //Traits::tag_enabled<Halfspace, halfspace_tag>::value &&
        //Traits::tag_enabled<Point, point_tag>::value,
        //bool 
    //>::type
    //do_intersect(
        //Point const & p1,
        //Point const & p2,
        //Halfspace const & halfspace, 
        //const double tolerance = Traits::tolerance<Halfspace>::value()
    //)
    //{
        //return do_intersect(halfspace, p1, p2, tolerance); 
    //}

    //template <typename PointCollection, typename Halfspace>
    //typename std::enable_if
    //<
        //(
            //Traits::tag_enabled<PointCollection, point_collection_tag>::value || 
            //Traits::tag_enabled<PointCollection, triangle_tag>::value 
        //) &&
        //Traits::tag_enabled<Halfspace, halfspace_tag>::value, 
        //bool 
    //>::type 
    //do_intersect(
        //PointCollection const & points, 
        //Halfspace const & halfspace,
        //const double tolerance = Traits::tolerance<Halfspace>::value()
    //)
    //{
        //auto P1 = points.begin();
        //auto P0 = std::next(points.begin());

        //// FIXME: Test this! There might be a bug inside. TM.
        //for (decltype(points.size()) i = 0; i < points.size() - 1; ++i)
        //{
            //if (do_intersect(halfspace, *P0, *P1, tolerance))
                //return true;

            //++P0;
            //++P1; 
        //}

        //return false;  
    //}

    //template <typename Halfspace, typename PointCollection>
    //typename std::enable_if
    //<
        //Traits::tag_enabled<PointCollection, point_collection_tag>::value && 
        //Traits::tag_enabled<Halfspace, halfspace_tag>::value, 
        //bool 
    //>::type 
    //do_intersect(
        //Halfspace const & halfspace, 
        //PointCollection const& points,
        //const double tolerance = Traits::tolerance<Halfspace>::value()
    //)
    //{
        //return  do_intersect(points, halfspace, tolerance);  
    //}
    
    //template
    //<
        //typename Halfspace,  
        //typename Tetrahedron
    //>
    //typename std::enable_if
    //<
        //Traits::tag_enabled<Halfspace, halfspace_tag>::value &&
        //Traits::tag_enabled<Tetrahedron, tetrahedron_tag>::value,
        //bool 
    //>::type
    //do_intersect(
        //Halfspace const & halfspace, 
        //Tetrahedron const & tetrahedron,
        //const double tolerance = Traits::tolerance<Halfspace>::value()
    //)
    //{
        //const auto& P0 = tetrahedron[0].front(); 

        //for (const auto& triangle : tetrahedron)
            //if (do_intersect(triangle, halfspace, tolerance))
                //return true; 

        //return false; 
    //}

    //template
    //<
        //typename Tetrahedron,  
        //typename Halfspace 
    //>
    //typename std::enable_if
    //<
        //Traits::tag_enabled<Tetrahedron, tetrahedron_tag>::value &&
        //Traits::tag_enabled<Halfspace, halfspace_tag>::value, 
        //bool 
    //>::type
    //do_intersect(
        //Tetrahedron const & tetrahedron, 
        //Halfspace const & halfspace,
        //const double tolerance = Traits::tolerance<Halfspace>::value()
    //)
    //{
        //return do_intersect(halfspace, tetrahedron, tolerance); 
    //}

    //template <typename Triangulation, typename Halfspace>
    //typename std::enable_if
    //<
        //Traits::tag_enabled<Triangulation, tetrahedron_collection_tag>::value && 
        //Traits::tag_enabled<Halfspace, halfspace_tag>::value, 
        //bool 
    //>::type 
    //do_intersect(
        //Triangulation const & triangulation, 
        //Halfspace const & halfspace, 
        //const double tolerance = Traits::tolerance<Halfspace>::value()
    //)
    //{
        //for (const auto& tetrahedron : triangulation)
        //{
            //if (do_intersect(tetrahedron, halfspace, tolerance))
            //{
                //return true;
            //}
        //}

        //return false;  
    //}


    //template <typename PolygonCollection, typename Halfspace>
    //typename std::enable_if
    //<
        //Traits::tag_enabled<PolygonCollection, polygon_collection_tag>::value && 
        //Traits::tag_enabled<Halfspace, halfspace_tag>::value, 
        //bool 
    //>::type 
    //do_intersect(
        //PolygonCollection const & polygons, 
        //Halfspace const & halfspace,
        //const double tolerance = Traits::tolerance<Halfspace>::value()
    //)
    //{
        //for (const auto& polygon : polygons)
        //{
            //if (do_intersect(polygon, halfspace, tolerance))
            //{
                //return true; 
            //}
        //}

        //return false;  
    //}

    //template <typename Halfspace, typename PolygonCollection>
    //typename std::enable_if
    //<
        //Traits::tag_enabled<Halfspace, halfspace_tag>::value && 
        //Traits::tag_enabled<PolygonCollection, polygon_collection_tag>::value, 
        //bool 
    //>::type 
    //do_intersect(
        //Halfspace const & halfspace, 
        //PolygonCollection const & polygons,
        //const double tolerance = Traits::tolerance<Halfspace>::value()
    //)
    //{
        //return do_intersect(polygons, halfspace, tolerance);  
    //}

    //// Struct test wrapper is needed to pass the test as a template
    //// parameter to algorithms that want to use it. E.g. intersection
    //// algorithm that performs aabb_test and calculates intersection 
    //// only when do_intersect test returns true. TM.
    //struct do_intersect_test 
    //{
        //template
        //<
            //typename FirstGeometry, 
            //typename SecondGeometry
        //>
        //static bool apply(
            //FirstGeometry const & f, 
            //SecondGeometry const & s,
            //const double tolerance = Traits::tolerance<FirstGeometry>::value()
        //)
        //{
            //return GeometricalTransport::do_intersect(f, s, tolerance);
        //}
    //};

} // End namespace geophase 

#endif

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