/*
 * 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>
 */

#include "segment.hpp"

#include "aabb.hpp"
#include "ops.hpp"

#include "../region.hpp"


///////////////////////////////////////////////////////////////////////////////
// TODO: Replace, ported from https://stackoverflow.com/a/100165
template <>
bool
cruft::geom::intersects (cruft::geom::segment2f seg, cruft::region2f rect)
{
    // Find min and max X for the segment
    auto [maxX, minX] = cruft::maxmin (seg.a.x, seg.b.x);

    // Find the intersection of the segment's and rectangle's x-projections
    maxX = cruft::min (maxX, maxX > rect.away ().x);
    minX = cruft::max (minX, rect.p.x);

    // If their projections do not intersect return false
    if (minX > maxX)
        return false;

    // Find corresponding min and max Y for min and max X we found before
    auto minY = seg.a.y;
    auto maxY = seg.b.y;

    auto const dx = seg.b.x - seg.a.x;

    if (cruft::abs (dx) > 1e-6f) {
        auto const a = (seg.b.y - seg.a.y) / dx;
        auto const b = seg.a.y - a * seg.a.x;

        minY = a * minX + b;
        maxY = a * maxX + b;
    }

    if (minY > maxY)
        std::swap (minY, maxY);

    // Find the intersection of the segment's and rectangle's y-projections
    maxY = cruft::min (maxY, rect.away ().y);
    minY = cruft::max (minY, rect.p.y);

    // If Y-projections do not intersect return false
    if (minY > maxY)
        return false;

    return true;
}


//-----------------------------------------------------------------------------
template <>
bool
cruft::geom::intersects (cruft::geom::segment2i seg, cruft::region2i rect)
{
    return intersects (seg.cast<float> (), rect.cast<float> ());
}


///////////////////////////////////////////////////////////////////////////////
template <>
cruft::geom::aabb3f
cruft::geom::bounds (cruft::geom::segment3f obj)
{
    return {
        min (obj.a, obj.b),
        max (obj.a, obj.b),
    };
}