// axisbox_funcs.h (Axis-aligned box implementation)
//
//  The WorldForge Project
//  Copyright (C) 2000, 2001  The WorldForge Project
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
//  For information about WorldForge and its authors, please contact
//  the Worldforge Web Site at http://www.worldforge.org.
//

// Author: Ron Steinke

// The implementation of this class is based on the geometric
// parts of the Tree and Placement classes from stage/shepherd/sylvanus

#ifndef WFMATH_AXIS_BOX_FUNCS_H
#define WFMATH_AXIS_BOX_FUNCS_H

#include <wfmath/axisbox.h>

#include <wfmath/point.h>
#include <wfmath/ball.h>

namespace WFMath {

template<int dim>
bool Intersection(const AxisBox<dim>& a1, const AxisBox<dim>& a2, AxisBox<dim>& out) {
	for (int i = 0; i < dim; ++i) {
		out.m_low[i] = FloatMax(a1.m_low[i], a2.m_low[i]);
		out.m_high[i] = FloatMin(a1.m_high[i], a2.m_high[i]);
		if (out.m_low[i] > out.m_high[i])
			return false;
	}

	out.m_low.setValid(a1.m_low.isValid() && a2.m_low.isValid());
	out.m_high.setValid(a1.m_high.isValid() && a2.m_high.isValid());

	return true;
}

template<int dim>
AxisBox<dim> Union(const AxisBox<dim>& a1, const AxisBox<dim>& a2) {
	AxisBox<dim> out;

	for (int i = 0; i < dim; ++i) {
		out.m_low[i] = FloatMin(a1.m_low[i], a2.m_low[i]);
		out.m_high[i] = FloatMax(a1.m_high[i], a2.m_high[i]);
	}

	out.m_low.setValid(a1.m_low.isValid() && a2.m_low.isValid());
	out.m_high.setValid(a1.m_high.isValid() && a2.m_high.isValid());

	return out;
}

template<int dim>
AxisBox<dim>& AxisBox<dim>::setCorners(const Point<dim>& p1, const Point<dim>& p2,
									   bool ordered) {
	if (ordered) {
		m_low = p1;
		m_high = p2;
		return *this;
	}

	for (int i = 0; i < dim; ++i) {
		if (p1[i] > p2[i]) {
			m_low[i] = p2[i];
			m_high[i] = p1[i];
		} else {
			m_low[i] = p1[i];
			m_high[i] = p2[i];
		}
	}

	m_low.setValid();
	m_high.setValid();

	return *this;
}

template<int dim>
Point<dim> AxisBox<dim>::getCorner(size_t i) const {
	if (i < 1)
		return m_low;
	if (i >= (1 << dim) - 1)
		return m_high;

	Point<dim> out;

	for (int j = 0; j < dim; ++j)
		out[j] = (i & (1 << j)) ? m_high[j] : m_low[j];

	out.setValid(m_low.isValid() && m_high.isValid());

	return out;
}

template<int dim>
inline Ball<dim> AxisBox<dim>::boundingSphere() const {
	return Ball<dim>(getCenter(), Distance(m_low, m_high) / 2);
}

template<int dim>
inline Ball<dim> AxisBox<dim>::boundingSphereSloppy() const {
	return Ball<dim>(getCenter(), SloppyDistance(m_low, m_high) / 2);
}


template<int dim, template<class, class> class container>
AxisBox<dim> BoundingBox(const container<AxisBox<dim>, std::allocator<AxisBox<dim> > >& c) {
	typename container<AxisBox<dim>, std::allocator<AxisBox<dim> > >::const_iterator i = c.begin(), end = c.end();

	if (i == end) {
		return AxisBox<dim>();
	}

	Point<dim> low = i->lowCorner(), high = i->highCorner();
	bool low_valid = low.isValid(), high_valid = high.isValid();

	while (++i != end) {
		const Point<dim>& new_low = i->lowCorner(), & new_high = i->highCorner();
		low_valid = low_valid && new_low.isValid();
		high_valid = high_valid && new_high.isValid();
		for (int j = 0; j < dim; ++j) {
			low[j] = FloatMin(low[j], new_low[j]);
			high[j] = FloatMax(high[j], new_high[j]);
		}
	}

	low.setValid(low_valid);
	high.setValid(high_valid);

	return AxisBox<dim>(low, high, true);
}

template<int dim, template<class, class> class container>
AxisBox<dim> BoundingBox(const container<Point<dim>, std::allocator<Point<dim> > >& c) {
	typename container<Point<dim>, std::allocator<Point<dim> > >::const_iterator i = c.begin(), end = c.end();

	if (i == end) {
		return AxisBox<dim>();
	}

	Point<dim> low = *i, high = *i;
	bool valid = i->isValid();

	while (++i != end) {
		valid = valid && i->isValid();
		for (int j = 0; j < dim; ++j) {
			low[j] = FloatMin(low[j], (*i)[j]);
			high[j] = FloatMax(high[j], (*i)[j]);
		}
	}

	low.setValid(valid);
	high.setValid(valid);

	return AxisBox<dim>(low, high, true);
}

// This is here, instead of defined in the class, to
// avoid include order problems

template<int dim>
inline AxisBox<dim> Point<dim>::boundingBox() const {
	return AxisBox<dim>(*this, *this, true);
}

template<int dim>
Point<dim> Point<dim>::toParentCoords(const AxisBox<dim>& coords) const {
	return coords.lowCorner() + (*this - Point().setToOrigin());
}

template<int dim>
Point<dim> Point<dim>::toLocalCoords(const AxisBox<dim>& coords) const {
	return Point().setToOrigin() + (*this - coords.lowCorner());
}

} // namespace WFMath

#endif  // WFMATH_AXIS_BOX_FUNCS_H
