#pragma once

#include <pixman.h>
#include <vector>
#include "Vector2D.hpp"
#include "Box.hpp"

namespace Hyprutils::Math {
    class CRegion {
      public:
        /* Create an empty region */
        CRegion();
        /* Create from a reference. Copies, does not own. */
        CRegion(const pixman_region32_t* const ref);
        /* Create from a box */
        CRegion(double x, double y, double w, double h);
        /* Create from a CBox */
        CRegion(const CBox& box);
        /* Create from a pixman_box32_t */
        CRegion(pixman_box32_t* box);

        CRegion(const CRegion&);
        CRegion(CRegion&&) noexcept;

        ~CRegion();

        CRegion& operator=(CRegion&& other) noexcept {
            if (this != &other)
                pixman_region32_copy(&m_rRegion, other.pixman());

            return *this;
        }

        CRegion& operator=(const CRegion& other) {
            if (this != &other)
                pixman_region32_copy(&m_rRegion, other.pixman());

            return *this;
        }

        CRegion&                    clear();
        CRegion&                    set(const CRegion& other);
        CRegion&                    add(const CRegion& other);
        CRegion&                    add(double x, double y, double w, double h);
        CRegion&                    add(const CBox& other);
        CRegion&                    subtract(const CRegion& other);
        CRegion&                    intersect(const CRegion& other);
        CRegion&                    intersect(double x, double y, double w, double h);
        CRegion&                    translate(const Vector2D& vec);
        CRegion&                    transform(const eTransform t, double w, double h);
        CRegion&                    invert(pixman_box32_t* box);
        CRegion&                    invert(const CBox& box);
        CRegion&                    scale(float scale);
        CRegion&                    scale(const Vector2D& scale);
        CRegion&                    expand(double units);
        CRegion&                    rationalize();
        CBox                        getExtents();
        bool                        containsPoint(const Vector2D& vec) const;
        bool                        empty() const;
        Vector2D                    closestPoint(const Vector2D& vec) const;
        CRegion                     copy() const;

        std::vector<pixman_box32_t> getRects() const;
        template <typename T>
        void forEachRect(T&& cb) const {
            int         rectsNum = 0;
            const auto* rects    = pixman_region32_rectangles(&m_rRegion, &rectsNum);
            for (int i = 0; i < rectsNum; ++i) {
                std::forward<T>(cb)(rects[i]);
            }
        }

        //
        pixman_region32_t* pixman() {
            return &m_rRegion;
        }

        const pixman_region32_t* pixman() const {
            return &m_rRegion;
        }

      private:
        pixman_region32_t m_rRegion;
    };
}
