#pragma once

#include <types/base/types.h>
#include <types/math/vec2.h>
#include <types/math/size.h>

namespace extra2d {

struct Rect {
    float x, y, w, h;

    Rect() : x(0.0f), y(0.0f), w(0.0f), h(0.0f) {}
    Rect(float x, float y, float w, float h) : x(x), y(y), w(w), h(h) {}
    Rect(const Vec2& pos, const Size& sz) : x(pos.x), y(pos.y), w(sz.w), h(sz.h) {}

    Vec2 pos() const { return {x, y}; }
    Vec2 center() const { return {x + w * 0.5f, y + h * 0.5f}; }
    Size size() const { return {w, h}; }

    float left() const { return x; }
    float right() const { return x + w; }
    float top() const { return y; }
    float bottom() const { return y + h; }

    float area() const { return w * h; }
    bool empty() const { return w <= 0.0f || h <= 0.0f; }

    bool contains(const Vec2& p) const {
        return p.x >= x && p.x <= x + w && p.y >= y && p.y <= y + h;
    }

    bool contains(const Rect& o) const {
        return o.x >= x && o.y >= y && o.right() <= right() && o.bottom() <= bottom();
    }

    bool intersects(const Rect& o) const {
        return x < o.right() && right() > o.x && y < o.bottom() && bottom() > o.y;
    }

    Rect intersection(const Rect& o) const {
        if (!intersects(o)) return Rect();
        float nx = (std::max)(x, o.x);
        float ny = (std::max)(y, o.y);
        float nw = (std::min)(right(), o.right()) - nx;
        float nh = (std::min)(bottom(), o.bottom()) - ny;
        return {nx, ny, nw, nh};
    }

    Rect unite(const Rect& o) const {
        float nx = (std::min)(x, o.x);
        float ny = (std::min)(y, o.y);
        float nr = (std::max)(right(), o.right());
        float nb = (std::max)(bottom(), o.bottom());
        return {nx, ny, nr - nx, nb - ny};
    }

    void setPos(const Vec2& p) { x = p.x; y = p.y; }
    void setSize(const Size& s) { w = s.w; h = s.h; }
    void setCenter(const Vec2& c) { x = c.x - w * 0.5f; y = c.y - h * 0.5f; }

    bool operator==(const Rect& o) const { return x == o.x && y == o.y && w == o.w && h == o.h; }
    bool operator!=(const Rect& o) const { return !(*this == o); }

    static const Rect Zero;
};

} // namespace extra2d
