#include "blobgroup.hpp"
#include "blobinvertedrect.hpp"
#include "blobshape.hpp"

BlobGroup::BlobGroup(QObject* parent)
	: QObject(parent) {
}

BlobGroup::~BlobGroup() {
	for (auto* shape : std::as_const(m_shapes))
		shape->m_group = nullptr;
	if (m_invertedRect)
		static_cast<BlobShape*>(m_invertedRect)->m_group = nullptr;
}

void BlobGroup::setSmoothing(qreal s) {
	if (qFuzzyCompare(m_smoothing, s))
		return;
	m_smoothing = s;
	emit smoothingChanged();
	markDirty();
}

void BlobGroup::setColor(const QColor& c) {
	if (m_color == c)
		return;
	m_color = c;
	emit colorChanged();
	markDirty();
}

void BlobGroup::addShape(BlobShape* shape) {
	if (!shape || m_shapes.contains(shape))
		return;
	m_shapes.append(shape);
	markDirty();
}

void BlobGroup::removeShape(BlobShape* shape) {
	m_shapes.removeOne(shape);
	markDirty();
}

void BlobGroup::setInvertedRect(BlobInvertedRect* rect) {
	if (m_invertedRect == rect)
		return;
	m_invertedRect = rect;
	markDirty();
}

void BlobGroup::clearInvertedRect(BlobInvertedRect* rect) {
	if (m_invertedRect != rect)
		return;
	m_invertedRect = nullptr;
	markDirty();
}

void BlobGroup::markDirty() {
	m_physicsUpdated = false;
	for (auto* shape : std::as_const(m_shapes)) {
		shape->polish();
		shape->update();
	}
	if (m_invertedRect) {
		static_cast<BlobShape*>(m_invertedRect)->polish();
		static_cast<BlobShape*>(m_invertedRect)->update();
	}
}

void BlobGroup::markShapeDirty(BlobShape* source) {
	m_physicsUpdated = false;

	source->polish();
	source->update();

	// Use cached padded rects to find spatial neighbors
	const float pad = static_cast<float>(m_smoothing) * 2.0f;
	const QRectF srcRect(static_cast<double>(source->m_cachedPaddedX - pad),
	                     static_cast<double>(source->m_cachedPaddedY - pad), static_cast<double>(source->m_cachedPaddedW + pad * 2.0f),
	                     static_cast<double>(source->m_cachedPaddedH + pad * 2.0f));

	for (auto* shape : std::as_const(m_shapes)) {
		if (shape == source)
			continue;
		const QRectF otherRect(static_cast<double>(shape->m_cachedPaddedX), static_cast<double>(shape->m_cachedPaddedY),
		                       static_cast<double>(shape->m_cachedPaddedW), static_cast<double>(shape->m_cachedPaddedH));
		if (srcRect.intersects(otherRect)) {
			shape->polish();
			shape->update();
		}
	}

	if (m_invertedRect && static_cast<BlobShape*>(m_invertedRect) != source) {
		static_cast<BlobShape*>(m_invertedRect)->polish();
		static_cast<BlobShape*>(m_invertedRect)->update();
	}
}

void BlobGroup::ensurePhysicsUpdated() {
	if (m_physicsUpdated)
		return;
	m_physicsUpdated = true;
	for (auto* shape : std::as_const(m_shapes))
		shape->updatePhysics();
}
