/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 2015-2017 Danny Robson <danny@nerdcruft.net>
 */


#include "aabb.hpp"

#include "ops.hpp"
#include "iostream.hpp"
#include "../coord/iostream.hpp"

using cruft::geom::aabb;


///////////////////////////////////////////////////////////////////////////////
template <>
std::array<
    cruft::point3f,8
>
aabb<3,float>::vertices (void) const noexcept
{
    return {{
        { lo.x, lo.y, lo.z },
        { lo.x, lo.y, hi.z },
        { lo.x, hi.y, lo.z },
        { lo.x, hi.y, hi.z },
        { hi.x, lo.y, lo.z },
        { hi.x, lo.y, hi.z },
        { hi.x, hi.y, lo.z },
        { hi.x, hi.y, hi.z },
    }};
}


///////////////////////////////////////////////////////////////////////////////
/// Returns the squared minimum distance from the AABB to a point.
template <size_t S, typename T>
T cruft::geom::distance2 (aabb<S,T> a, point<S,T> b)
{
    auto const clamped = cruft::max (
        a.lo - b,
        vector<S,T> (0),
        b - a.hi
    );

    return sum (clamped * clamped);
}


///////////////////////////////////////////////////////////////////////////////
namespace cruft::debug {
    template <size_t S, typename T>
    struct validator<aabb<S,T>> {
        static bool is_valid (const aabb<S,T> &b)
        {
            return all (b.lo <= b.hi);
        }
    };
}


//-----------------------------------------------------------------------------
template <size_t S, typename T>
std::ostream&
cruft::geom::operator<< (std::ostream &os, cruft::geom::aabb<S,T> b)
{
    return os << "[ " << b.lo << ", " << b.hi << " ]";
}


//-----------------------------------------------------------------------------
#define INSTANTIATE_S_T(S,T)                             \
namespace cruft::geom { template struct aabb<S,T>; }      \
template bool cruft::debug::is_valid (const aabb<S,T>&);  \
template std::ostream& cruft::geom::operator<< (std::ostream&, aabb<S,T>); \
template T cruft::geom::distance2 (point<S,T>, aabb<S,T>); \
template T cruft::geom::distance2 (aabb<S,T>, point<S,T>);
#define INSTANTIATE(T)  \
INSTANTIATE_S_T(2,T)    \
INSTANTIATE_S_T(3,T)

INSTANTIATE( int32_t)
INSTANTIATE( int64_t)
INSTANTIATE(uint32_t)
INSTANTIATE(uint64_t)
INSTANTIATE(float)
INSTANTIATE(double)