#pragma once

#include <cstdint>

namespace td {
namespace utils {
namespace shape {

class Point {
private:
    float m_X, m_Y;
public:
    Point() : m_X(0), m_Y(0) {}
    Point(float x, float y) : m_X(x), m_Y(y) {}

    float getX() const { return m_X; }
    float getY() const { return m_Y; }

    void setX(float x) { m_X = x; }
    void setY(float y) { m_Y = y; }

    float distance(const Point& point) const;
    float distanceSquared(const Point& point) const;
};

class Circle;

class Rectangle {
private:
    Point m_Center;
    float m_Width, m_Height;
public:
    Rectangle() {}

    const Point& getCenter() const { return m_Center; }
    float getCenterX() const { return m_Center.getX(); }
    float getCenterY() const { return m_Center.getY(); }

    float getWidth() const { return m_Width; }
    float getHeight() const { return m_Height; }

    Point getTopLeft() const { return { m_Center.getX() - (m_Width / 2.0f), m_Center.getY() - (m_Height / 2.0f) }; }
    Point getBottomRight() const { return { m_Center.getX() + (m_Width / 2.0f), m_Center.getY() + (m_Height / 2.0f) }; }

    void setCenter(const Point& center) { m_Center = center; }
    void setCenterX(float x) { m_Center.setX(x); }
    void setCenterY(float y) { m_Center.setY(y); }

    void setSize(float width, float height) { setWidth(width); setHeight(height); }
    void setWidth(float width) { m_Width = width; }
    void setHeight(float height) { m_Height = height; }

    bool collidesWith(const Point& point) const;
    bool collidesWith(const Rectangle& rect) const;
    bool collidesWith(const Circle& circle) const;

    // distance from the closest side of the rectangle
    float distance(const Circle& circle) const;
    float distanceSquared(const Circle& circle) const;
};

class Circle {
private:
    Point m_Center;
    float m_Radius;
public:
    Circle(float x, float y, float radius) : m_Center(x, y), m_Radius(radius) {}
    Circle() : m_Radius(0) {}

    const Point& getCenter() const { return m_Center; }
    float getCenterX() const { return m_Center.getX(); }
    float getCenterY() const { return m_Center.getY(); }

    float getRadius() const { return m_Radius; }

    void setCenter(const Point& center) { m_Center = center; }
    void setCenterX(float x) { m_Center.setX(x); }
    void setCenterY(float y) { m_Center.setY(y); }

    void setRadius(float radius) { m_Radius = radius; }

    bool collidesWith(const Point& point) const;
    bool collidesWith(const Rectangle& rect) const;
    bool collidesWith(const Circle& circle) const;

    // distance from the closest side of the rectangle
    float distance(const Rectangle& rect) const;
    float distanceSquared(const Rectangle& rect) const;
};

} // namespace shape
} // namespace utils 
} // namespace td
