#include "td/misc/Shapes.h"

#include <algorithm>
#include <cmath>

namespace td {
namespace utils {
namespace shape {

float Point::Distance(const Point& point) const {
	return std::sqrt(DistanceSquared(point));
}

float Point::DistanceSquared(const Point& point) const {
	return (m_X - point.GetX()) * (m_X - point.GetX()) + (m_Y - point.GetY()) * (m_Y - point.GetY());
}


bool Rectangle::CollidesWith(const Point& point) const {
	return point.GetX() > GetTopLeft().GetX() && point.GetX() < GetBottomRight().GetX() &&
		point.GetY() > GetTopLeft().GetY() && point.GetY() < GetBottomRight().GetY();
}

bool Rectangle::CollidesWith(const Rectangle& rect) const {

	Point point1{ rect.GetTopLeft().GetX(), rect.GetTopLeft().GetY() };
	Point point2{ rect.GetTopLeft().GetX(), rect.GetBottomRight().GetY() };
	Point point3{ rect.GetBottomRight().GetX(), rect.GetTopLeft().GetY() };
	Point point4{ rect.GetBottomRight().GetX(), rect.GetBottomRight().GetY() };

	if (CollidesWith(point1)) return true;
	if (CollidesWith(point2)) return true;
	if (CollidesWith(point3)) return true;
	if (CollidesWith(point4)) return true;

	return false;
}

bool Rectangle::CollidesWith(const Circle& circle) const {
	return circle.CollidesWith(*this);
}

float Rectangle::Distance(const Circle& circle) const {
	return circle.Distance(*this);
}

float Rectangle::DistanceSquared(const Circle& circle) const {
	return circle.DistanceSquared(*this);
}





bool Circle::CollidesWith(const Point& point) const {
	return m_Radius * m_Radius > m_Center.DistanceSquared(point);
}

bool Circle::CollidesWith(const Rectangle& rect) const {
	float DistanceSquared_ = DistanceSquared(rect);

	return DistanceSquared_ < m_Radius* m_Radius;
}

bool Circle::CollidesWith(const Circle& circle) const {
	return m_Radius + circle.GetRadius() > m_Center.DistanceSquared(circle.GetCenter());
}

float Circle::Distance(const Rectangle& rect) const {
	return std::sqrt(DistanceSquared(rect));
}

float Circle::DistanceSquared(const Rectangle& rect) const {
	float closestX = std::clamp(m_Center.GetX(), rect.GetTopLeft().GetX(), rect.GetBottomRight().GetX());
	float closestY = std::clamp(m_Center.GetY(), rect.GetTopLeft().GetY(), rect.GetBottomRight().GetY());

	float DistanceX = m_Center.GetX() - closestX;
	float DistanceY = m_Center.GetY() - closestY;

	return (DistanceX * DistanceX) + (DistanceY * DistanceY);
}

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