////////////////////////////////////////////////////////////////////////////////
// BoundingBox.cpp
// Copyright Bjoern Ganster
// Developed since 2021
////////////////////////////////////////////////////////////////////////////////

#include "BoundingBox.h"

// Default constructor
BoundingBox::BoundingBox()
: VirtualClass("BoundingBox", BoundingBoxID),
  mValid(false)
{
}

// Construct with single point
BoundingBox::BoundingBox(const Point3D& initVal)
: VirtualClass("BoundingBox", BoundingBoxID),
  mMin(initVal),
  mMax(initVal),
  mValid(true)
{
}

// Construct with known min/max
BoundingBox::BoundingBox(const Point3D& min, const Point3D& max)
: VirtualClass("BoundingBox", BoundingBoxID),
  mMin(min),
  mMax(max),
  mValid(true)
{
}

// Destructor
BoundingBox::~BoundingBox()
{
}

// Update
void BoundingBox::update(const Point3D& newP)
{
	if (mValid) {
		mMin.x = std::min(mMin.x, newP.x);
		mMin.y = std::min(mMin.y, newP.y);
		mMin.z = std::min(mMin.z, newP.z);
		mMax.x = std::max(mMax.x, newP.x);
		mMax.y = std::max(mMax.y, newP.y);
		mMax.z = std::max(mMax.z, newP.z);
	} else {
		mMin = newP;
		mMax = newP;
		mValid = true;
	}
}

// Check if two bounding boxes intersect
bool BoundingBox::intersects(const BoundingBox& other) const
{
	bool result = boxesIntersect(mMin, mMax, other.mMin, other.mMax);
	return result;
}

// Update bounding box to reflect movement
// Should be set once after all points have been processed
bool BoundingBox::applyMovement(const Point3D& v)
{
	if (mValid) {
		if (v.x > 0.0) {
			mMax.x += v.x;
		} else {
			mMin.x += v.x;
		}

		if (v.y > 0.0) {
			mMax.y += v.y;
		} else {
			mMin.y += v.y;
		}

		if (v.z > 0.0) {
			mMax.z += v.z;
		} else {
			mMin.z += v.z;
		}

		return true;
	} else {
		return false;
	}
}

// Getters
const Point3D& BoundingBox::min() const
{
	return mMin;
}
const Point3D& BoundingBox::max() const
{
	return mMax;
}
bool BoundingBox::valid() const
{
	return mValid;
}

// Compute distance to point (approx. from corners)
double BoundingBox::getDistance(const Point3D& p) const
{
	Point3D p000(mMin.x, mMin.y, mMin.z);
	Point3D p001(mMin.x, mMin.y, mMax.z);
	Point3D p010(mMin.x, mMax.y, mMin.z);
	Point3D p011(mMin.x, mMax.y, mMax.z);

	Point3D p100(mMax.x, mMin.y, mMin.z);
	Point3D p101(mMax.x, mMin.y, mMax.z);
	Point3D p110(mMax.x, mMax.y, mMin.z);
	Point3D p111(mMax.x, mMax.y, mMax.z);

	double d[8];
	d[0] = (p000 - p).length();
	d[1] = (p001 - p).length();
	d[2] = (p010 - p).length();
	d[3] = (p011 - p).length();

	d[4] = (p100 - p).length();
	d[5] = (p101 - p).length();
	d[6] = (p110 - p).length();
	d[7] = (p111 - p).length();

	double min = d[0];
	for (size_t i = 1; i < 8; ++i) {
		if (d[i] < min) {
			min = d[i];
		}
	}

	return min;
}

// Get center of this BondingBox
Point3D BoundingBox::getCenter() const
{
	return 0.5 * (mMin + mMax);
}

