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

    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
    Axis-Aligned Bounding Box. 

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

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

#ifndef AxisAlignedBoundingBox_HPP
#define AxisAlignedBoundingBox_HPP

#include "tag.hpp"
#include "constants.hpp"
#include "Vector.hpp"
#include "ExtremePoints.hpp"

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

namespace geophase {  

/*---------------------------------------------------------------------------*\
                         Class AxisAlignedBoundingBox Declaration
\*---------------------------------------------------------------------------*/

template<typename Vector, typename Enabled=void> 
class AxisAlignedBoundingBox; 

template<typename Vector> 
class AxisAlignedBoundingBox
<
    Vector, 
    enable_if_type_tagged_t<Vector, vector_tag>
>
{
    Vector min_; 
    Vector max_; 

public:

    typedef Vector point_type; 

    AxisAlignedBoundingBox()
    : 
        min_(MAX,MAX,MAX),
        max_(LOWEST,LOWEST,LOWEST)
    {}

    AxisAlignedBoundingBox(
        Vector const & min,
        Vector const & max
    )
    :
        min_(min), 
        max_(max)
    {}

    template<typename Geometry>
    AxisAlignedBoundingBox(Geometry const& geo)
    :
       AxisAlignedBoundingBox()
    {
        auto extremePointPair = extreme_points(geo); 
        min_ = extremePointPair.first; 
        max_ = extremePointPair.second;
    }

    template<typename VectorIterator>
    AxisAlignedBoundingBox(
        VectorIterator begin,  
        VectorIterator end
    )
    :
       AxisAlignedBoundingBox()
    {
       auto extremePointPair = extreme_points(begin, end);  
       min_ = extremePointPair.first; 
       max_ = extremePointPair.second;
    }

    template<typename VectorIterator>
    void expand(VectorIterator begin, VectorIterator end)
    {
        auto extremePointPair = extreme_points(begin, end);  
        assign_less(extremePointPair.first, min_);  
        assign_greater(extremePointPair.second, max_);
    }

    template<typename Geometry>
    void expand(Geometry const& geo)
    {
       auto extremePointPair = extreme_points(geo);  
       assign_less(extremePointPair.first, min_);  
       assign_greater(extremePointPair.second, max_);
    }

    void pointExpand(Vector const& p)
    {
        assign_less(min_, p);  
        assign_greater(max_, p);
    }

    constexpr Vector const& maxPoint() const
    {
        return max_; 
    }

    Vector& maxPoint() 
    {
        return max_; 
    }

    constexpr Vector const& minPoint() const
    {
        return min_;
    }

    Vector& minPoint() 
    {
        return min_;
    }

    bool operator==(AxisAlignedBoundingBox const& other) const
    {
        return (
            (this->max_ == other.max_) &&
            (this->min_ == other.min_) 
       );
    }

    bool operator!=(AxisAlignedBoundingBox const& other) const
    {
        return !this->operator==(other);
    }
};

template<typename Vector> 
struct tag<AxisAlignedBoundingBox<Vector>>
{
    using type = axis_box_tag;
};

// Commonly used box types.
using aabbox = AxisAlignedBoundingBox<vector>;

} // End namespace geophase 

#endif

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