#include "tap.hpp"

#include "geom/ellipse.hpp"
#include "geom/ray.hpp"
#include "point.hpp"
#include "vector.hpp"


///////////////////////////////////////////////////////////////////////////////
void
test_intersection (cruft::TAP::logger &tap)
{
    static struct {
        cruft::geom::ellipse3f shape;
        cruft::geom::ray3f caster;
        float distance;
        const char *message;
    } const TESTS[] = {
        {
            .shape  = { .origin = {0}, .radius = {1}, .up = {0,1,0} },
            .caster = { .origin = { 0, 0, -2 }, .direction = cruft::vector3f{ 0, 0, 1 } },
            .distance = 1,
            .message = "sphere on origin, ray mostly on origin"
        },

        {
            .shape = { .origin = {0}, .radius = {1,1,1.5}, .up = {0,1,0} },
            .caster = { .origin = {0,0,-2}, .direction = cruft::vector3f{0,0,1} },
            .distance = .5f,
            .message = "ellipse on origin, extended on z, ray along z",
        },

        {
            .shape = { .origin = 1, .radius = 1, .up = { 0, 1, 0 } },
            .caster = { .origin = {1,1,3}, .direction = {0,0,-1} },
            .distance = 1.f,
            .message = "ellipse at 1,1,1, ray along -z",
        }
    };

    for (auto const& t: TESTS) {
        tap.expect_eq (t.distance, distance (t.caster, t.shape), "%s", t.message);
    }
}


void
test_area (cruft::TAP::logger &tap)
{
    static struct {
        cruft::geom::ellipse3f shape;
        float value;
        const char *message;
    } const TESTS[] = {
        { .shape = { .origin = {0}, .radius = {1}, .up = {0} }, .value = 12.57f, .message = "unit radius" },
    };

    for (auto const& t: TESTS) {
        auto found = area (t.shape);
        tap.expect (cruft::relatively_equal (found, t.value, 1.08e-2f), "%!", t.message);
    }
}


void
test_cover (cruft::TAP::logger &tap)
{
    static struct {
        std::vector<cruft::point3f> cloud;
        const char *message;
    } const TESTS[] = {
        { {
            {-1, 0, 0},
            { 0, 0, 0},
            { 1, 0, 0},
          }, "three spaced across x"
        },
        { {
            {-1, 0, 0},
            { 0, 0, 0},
            { 1, 0, 0},
            { 0,-1, 0},
            { 0, 1, 0},
          }, "planar circle about origin"
        },
        { {
              {-1, 0, 0},
              { 0, 0, 0},
              { 1, 0, 0},
              { 0,-1, 0},
              { 0, 1, 0},
              { 0, 0,-1},
              { 0, 0, 1},
          }, "sphere about origin"
        },
    };

    for (auto const &t: TESTS) {
        auto const shape = cruft::geom::cover (cruft::view<const cruft::point3f*> {t.cloud});

        auto const success = std::all_of (
            t.cloud.begin (),
            t.cloud.end (),
            [shape] (auto p) {
                return intersects (shape, p);
        });

        tap.expect (success, "%!", t.message);
    }
}


///////////////////////////////////////////////////////////////////////////////
int
main ()
{
    cruft::TAP::logger tap;

    test_intersection (tap);
    test_area (tap);
    test_cover (tap);

    return tap.status ();
}