/*
 * 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 2018 Danny Robson <danny@nerdcruft.net>
 */

#ifndef CRUFT_UTIL_GEOM_FRUSTUM_HPP
#define CRUFT_UTIL_GEOM_FRUSTUM_HPP

#include "../matrix.hpp"
#include "plane.hpp"
#include "aabb.hpp"

#include <array>

#include <cstddef>

namespace cruft::geom {
    /// a viewing frustrum comprised of 4 axis planes, a near plane, and a far
    /// plane. it may describe something other than a perspective projection
    /// (eg, an orthographic projection)
    template <typename ValueT>
    struct frustum {
        explicit frustum (const matrix<4,4,ValueT>&);

        std::array<plane<3,ValueT>, 6> planes;

        enum {
            X_NEG, X_POS,
            Y_NEG, Y_POS,
            Z_NEG, Z_POS,

            LEFT = X_NEG,
            RIGHT = X_POS,
            TOP = Y_POS,
            BOTTOM = Y_NEG,
            NEAR = Z_POS,
            FAR = Z_NEG
        };
    };

    using frustum3f = frustum<float>;


    /// tests whether a frustum and an aabb overlap
    ///
    /// XXX: there may be occasional false positives.
    template <size_t S, typename T>
    bool
    intersects (const frustum<T> &f, const aabb<S,T> &b)
    {
        for (const auto &p: f.planes) {
            // find the distance to the point, if it's positive it's inside
            // the frustum.
            const auto d = max (
                p.coefficients * b.lo.template redim<S+1> (1),
                p.coefficients * b.hi.template redim<S+1> (1)
            );

            if (sum (d) < 0)
                return false;
        }

        return true;
    }
};

#endif