/*-
 * Copyright (c) 1999,2000 Qube Software, Ltd.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 * 1. Redistributions of the source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Any redistribution solely in binary form must conspicuously
 *    reproduce the following disclaimer in documentation provided with the
 *    binary redistribution.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'', WITHOUT ANY WARRANTIES, EXPRESS
 * OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  LICENSOR SHALL
 * NOT BE LIABLE FOR ANY LOSS OR DAMAGES RESULTING FROM THE USE OF THIS
 * SOFTWARE, EITHER ALONE OR IN COMBINATION WITH ANY OTHER SOFTWARE.
 * 
 * 	$Id: box2.h,v 1.17 2000/04/20 16:21:57 n_hibma Exp $
 */

#ifndef __box2_h__
#define __box2_h__

#include <limits.h>
#include <float.h>

MATHS_BEGIN

#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif

template<class T> struct Box2
{
    Vec2<T>	min;
    Vec2<T>	max;

    // Constructors
    Box2()
    {
    }

    Box2(T minx, T miny, T maxx, T maxy)
	: min(minx, miny), max(maxx, maxy)
    {
    }

    Box2(const Vec2<T>& vmin, const Vec2<T>& vmax)
	: min(vmin), max(vmax)
    {
    }
    
    // Accessors
    Vec2<T>&		operator[](int i) { return (&min)[i]; }
    const Vec2<T>&	operator[](int i) const { return (&min)[i]; }
    T			width() const;
    T			height() const;
    Vec2<T>		centre() const
    {
	return (min + max) * 0.5f;
    }
    // Modifiers
    void		invalidate();
    Box2		intersect(const Box2&) const;
    Box2		add(const Box2&) const; // union
    Box2		add(const Vec2<T>&) const;
    
    // Testing
    bool		operator==(const Box2& box) const
    {
	return min == box.min && max == box.max;
    }
    bool		operator!=(const Box2& box) const
    {
	return min != box.min || max != box.max;
    }
    bool		equals(const Box2&, float e = Epsilon) const;
    bool		contains(const Box2&) const;
    bool		intersects(const Box2&) const;


    // Matrix operations
    Box2		transform(const Matrix4f& m) const;
};

template<class T> T
max(T a, T b)
{
    if (a > b)
	return a;
    else
	return b;
}

template<class T> T
min(T a, T b)
{
    if (a < b)
	return a;
    else
	return b;
}

template<class T> T
Box2<T>::width() const
{
    return max.x - min.x;
}

template<class T> T
Box2<T>::height() const
{
    return max.y - min.y;
}

inline void
Box2<float>::invalidate()
{
    min.x = min.y = FLT_MAX;
    max.x = max.y = -FLT_MAX;
}

inline void
Box2<int>::invalidate()
{
    min.x = min.y = INT_MAX;
    max.x = max.y = INT_MIN;
}

template<class T> void
Box2<T>::invalidate()
{
    assert(0);
}

template<class T> Box2<T>
Box2<T>::intersect(const Box2<T>& box) const
{
    Vec2<T> vmin(Maths::max(min.x, box.min.x), Maths::max(min.y, box.min.y));
    Vec2<T> vmax(Maths::min(max.x, box.max.x), Maths::min(max.y, box.max.y));

    if (vmin.x < vmax.x && vmin.y < vmax.y)
	return Box2<T>(vmin, vmax);
    else
	return Box2<T>(0, 0, 0, 0);
}

template<class T> Box2<T>
Box2<T>::add(const Box2<T>& box) const
{
    Vec2<T> vmin(Maths::min(min.x, box.min.x), Maths::min(min.y, box.min.y));
    Vec2<T> vmax(Maths::max(max.x, box.max.x), Maths::max(max.y, box.max.y));

    return Box2<T>(vmin, vmax);
}

template<class T> Box2<T>
Box2<T>::add(const Vec2<T>& v) const
{
    Vec2<T> vmin(Maths::min(min.x, v.x), Maths::min(min.y, v.y));
    Vec2<T> vmax(Maths::max(max.x, v.x), Maths::max(max.y, v.y));

    return Box2<T>(vmin, vmax);
}

template<class T> bool
Box2<T>::equals(const Box2<T>& box, float e) const
{
    return min.equals(box.min, e) && max.equals(box.max, e);
}

template<class T> bool
Box2<T>::contains(const Box2<T>& box) const
{
    return min.x <= box.min.x && min.y <= box.min.y
	&& max.x >= box.max.x && max.y >= box.max.y;
}

template<class T> bool
Box2<T>::intersects(const Box2<T>& box) const
{
    if (min.x >= box.max.x || max.x <= box.min.x)
	return false;
    if (min.y >= box.max.y || max.y <= box.min.y)
	return false;
    return true;
}

template<class T> Box2<T>
Box2<T>::transform(const Matrix4f& m) const
{
    Box2 b;
    b.invalidate();

    for (unsigned int x = 0; x < 2; x++)
	for (unsigned int y = 0; y < 2; y++) {
	    Vec3f v((*this)[x].x, (*this)[y].y, 0);
	    Vec3f tv = v * m;
	    if (tv.x < b.min.x) b.min.x = tv.x;
	    if (tv.y < b.min.y) b.min.y = tv.y;
	    if (tv.x > b.max.x) b.max.x = tv.x;
	    if (tv.y > b.max.y) b.max.y = tv.y;
	}
    return b;
}

typedef Box2<float>		Box2f;
typedef Box2<double>		Box2d;
typedef Box2<short>		Box2s;
typedef Box2<int>		Box2i;
typedef Box2<unsigned char>	Box2b;
typedef Box2<unsigned int>	Box2ui;

MATHS_END

#endif

