#include "region.hpp"

#include "point.hpp"
#include "tap.hpp"

#include <vector>

//-----------------------------------------------------------------------------
int
main (int, char **)
{
    cruft::TAP::logger tap;

    // check that two overlapping regions successfully test for intersection
    {
        const cruft::point2d  ap { 32.7, -6.09703 };
        const cruft::extent2d ae { 0.8, 2. };
        const cruft::region2d a (ap, ae);

        const cruft::point2d  bp {33.5, -4.5};
        const cruft::extent2d be { 0.5, 0.5 };
        const cruft::region2d b (bp, be);

        tap.expect (!a.intersects (b), "simple 2d intersection");
    }

    // check various floating point maximums successfully test for
    // intersection. the concern is that infinities are incorrectly handled
    // in some comparisons.
    tap.expect (
        cruft::region2d::max ().intersects (
            cruft::region2d::unit ()
        ),
        "maximal region2d intersection"
    );

    tap.expect (
        cruft::region2f::max ().intersects (
            cruft::region2f::unit ()
        ),
        "maximal region2f intersection"
    );

    // ensure unit regions are... unit sized...
    tap.expect_eq (cruft::region2d::unit ().area (), 1.0,  "unit region2d area");
    tap.expect_eq (cruft::region2f::unit ().area (), 1.0f, "unit region2f area");

    // test boundary cases of includes and contains
    {
        const cruft::point2f  p0 { 0 };
        const cruft::extent2f e0 { 2 };
        const cruft::region2f r  {p0, e0};

        tap.expect (!r.inclusive (cruft::point2f {-1, 1}), "region/point inclusive, invalid x");
        tap.expect (!r.inclusive (cruft::point2f { 1, 3}), "region/point inclusive, invalid y ");

        tap.expect (r.inclusive (cruft::point2f {1, 1}), "region/point inclusive, centre");
        tap.expect (r.inclusive (cruft::point2f {0, 0}), "region/point inclusive, base");
        tap.expect (r.inclusive (cruft::point2f {2, 2}), "region/point inclusive, corner");
    }

    // ensure union operator behaves as expected
    {
        const cruft::point2f  p { -1 };
        const cruft::extent2f e {  2 };
        const cruft::region2f r { p, e };

        tap.expect_eq (r | cruft::point2f { 0, 0 }, r, "identity union");
        tap.expect_eq (
            r | cruft::point2f { 2, 3 },
            cruft::region2f { p, cruft::extent2f { 3, 4 } },
            "positive expanding union"
        );
        tap.expect_eq (
            r | cruft::point2f { -3, -2 },
            cruft::region2f {
                cruft::point2f { -3, -2 },
                cruft::extent2f { 4, 3 }
            },
            "negative expanding union"
        );
    };

    // ensure make_region covers the expected values
    {
        const cruft::region2i REGION {
            cruft::point2i { 0, 0 },
            cruft::point2i { 3, 2 }
        };

        const cruft::point2i EXPECTED[] = {
            { 0, 0 }, { 1, 0 }, { 2, 0 },
            { 0, 1 }, { 1, 1 }, { 2, 1 },
        };

        std::vector<cruft::point2i> values;
        std::copy (std::cbegin (REGION.step ()), std::cend (REGION.step ()), std::back_inserter (values));

        bool success = values.size () == std::size (EXPECTED)
                       && std::equal (std::cbegin (values),   std::cend (values),
                                      std::cbegin (EXPECTED), std::cend (EXPECTED));
        tap.expect (success, "make_range(region2i)");
    };

    // test distance2 for points
    {
        struct {
            cruft::region2f r;
            cruft::point2f  p;
            float distance2;
            char const *message;
        } const TESTS[] = {
            {
                { cruft::point2f { 1, 1 }, cruft::point2f { 3, 5 } },
                { 0, 0 },
                2.f,
                "origin to offset rect"
            },
            {
                { cruft::point2f { 1, 1 }, cruft::point2f { 3, 5 } },
                { 2, 2 },
                0.f,
                "point inside region"
            },
            {
                { cruft::point2f { 1, 1 }, cruft::point2f { 3, 5 } },
                { 4, 7 },
                1 + 4,
                "offset point to offset rect"
            },
        };

        for (auto const &t: TESTS) {
            auto const d2 = distance2 (t.r, t.p);
            tap.expect_eq (d2, t.distance2, "region-point distance2: %!", t.message);
        }
    };

    return tap.status ();
}