#include "testlib.h"

using Coordinate = int32_t;
using Area = int32_t;

struct Point2D {
    Coordinate x, y;
    Point2D operator+(const Point2D &other) const {
        return {
            .x = other.x + x,
            .y = other.y + y,
        };
    }
    Point2D operator-(const Point2D &other) const {
        return {
            .x = x - other.x,
            .y = y - other.y,
        };
    }
    bool operator==(const Point2D &other) const {
        return x == other.x && y == other.y;
    }
    Area operator%(const Point2D &other) const {
        return static_cast<Area>(x) * other.y - static_cast<Area>(y) * other.x;
    }
    Area operator*(const Point2D &other) const {
        return static_cast<Area>(x) * other.x + static_cast<Area>(y) * other.y;
    }
};

std::ostream& operator<<(std::ostream& out, const Point2D& p) {
    return out << p.x << ' ' << p.y;
}

struct Rectangle2D {
    Point2D bl, tr;
    bool in(const Point2D& p) {
        return p.x >= bl.x && p.x <= tr.x &&
               p.y >= bl.y && p.y <= tr.y;
    }
};

Point2D get_random_point(const Rectangle2D& bb) {
    Point2D p;
    p.x = rnd.next(bb.bl.x, bb.tr.x);
    p.y = rnd.next(bb.bl.y, bb.tr.y);
    return p;
}

struct Test {
    std::array<Point2D, 4> points;
};

std::ostream& operator<<(std::ostream& out, const Test& t) {
    for (size_t i = 0; i < t.points.size(); i++) {
        if (i)
            out << ' ';
        out << t.points[i];
    }
    return out;
}

Test generate_random_test() {
    Test test;
    Rectangle2D bb{
        .bl = {
            .x = opt<Coordinate>("MIN_X"),
            .y = opt<Coordinate>("MIN_Y"),
        },
        .tr = {
            .x = opt<Coordinate>("MAX_X"),
            .y = opt<Coordinate>("MAX_Y"),
        },
    };
    while (true) {
        for (size_t i = 0; i < 3; ++i)
            test.points[i] = get_random_point(bb);
        test.points[3] = test.points[0] + test.points[2] - test.points[1];
        if (!bb.in(test.points[3]))
            continue;
        if ((test.points[0] - test.points[1]) % (test.points[0] - test.points[3]) <= 0)
            continue;
        if ((test.points[0] - test.points[1]) * (test.points[0] - test.points[3]) != 0)
            continue;
        return test;
    }
}

int main(int argc, char *argv[]) {
    registerGen(argc, argv, 1);
    Test test;
    std::string type = opt<std::string>("type");
    if (type == "random")
        test = generate_random_test();
    else
        ensuref(false, "%s", std::format("unknown test type: \"{}\"", type).c_str());
    std::cout << test << '\n';
}
