#ifndef __BBOX3_H__
#define __BBOX3_H__

#include <iostream>
#include <cassert>
#include <cmath>

#include "vector3.h"
#include "bsphere3.h"

#include "public.h"


namespace xrt {


// The BBox3 class is for axis-aligned 3D bounding boxes.
//
// We GUARANTEE that the memory layout and size of a BBox3 is
// identical to a float[6], and thus it's presumed relatively
// safe to cast between a (float *) and a (BBox3 *).
//
// Semantics are as described below: construction from six floats
// or two Vector3's, unions and tests against points, boxes, and spheres.
// Plus queries for volume, diagonal length, and surface area,
// and methods for transformation by a Matrix4.

class PUBLIC BBox3 {

public:

    // Default constructor leaves contents undefined
    BBox3 (void) { }

    // Constructor from six floats fill in the bbox values
    BBox3 (float xmin, float xmax, float ymin, float ymax,
           float zmin, float zmax) {
        b[0] = xmin;  b[1] = xmax;
        b[2] = ymin;  b[3] = ymax;
        b[4] = zmin;  b[5] = zmax;
    }

    // Constructor from an array of floats
    explicit BBox3 (const float *bounds) {
	assert (bounds != NULL);
#ifdef VECMAT_LOOPS
	for (int i = 0;  i < 6;  ++i)
            b[i] = bounds[i];
#else
	b[0] = bounds[0]; b[1] = bounds[1]; 
	b[2] = bounds[2]; b[3] = bounds[3]; 
	b[4] = bounds[1]; b[5] = bounds[2];
#endif
    }

	// Constructor from two Vector3 
    BBox3 (const Vector3 &u, const Vector3 &v) {
		setCorners (u, v);
    }

    // Constructor from a BSphere3 
    BBox3 (const BSphere3 &s) {
        b[0] = s[0] - s.radius;
        b[1] = s[0] + s.radius;
        b[2] = s[1] - s.radius;
        b[3] = s[1] + s.radius;
        b[4] = s[2] - s.radius;
        b[5] = s[2] + s.radius;
    }

    // Constructor from a Vector3 creates a box that exactly contains
    // the point
    BBox3 (const Vector3 &v) {
        b[0] = v[0];  b[1] = v[0];
        b[2] = v[1];  b[3] = v[1];
        b[4] = v[2];  b[5] = v[2];
    }

    // Special BBox3's -- BBox3::Empty() contains nothing (the next point
    // added will be exactly contained), BBox3::All() contains everything.
    static const BBox3& Empty() { 
	static const BBox3 _empty ( 1.0e38f, -1.0e38f,  1.0e38f,
                                   -1.0e38f,  1.0e38f, -1.0e38f);
	return _empty;
    }
    static const BBox3& All() { 
        static const BBox3 _all (-1.0e38f,  1.0e38f, -1.0e38f, 
                                  1.0e38f, -1.0e38f,  1.0e38f);
	return _all;
    }
    // Direct access to the data -- use with caution
    const float *data(void) const { return b; }

    // Component access
    float operator[] (int i) const {
	assert(i>=0 && i < 6);  // range check -- only in DEBUG mode
	return b[i];
    }
    float& operator[] (int i) {
	assert(i>=0 && i < 6);  // range check -- only in DEBUG mode
	return b[i];
    }

    void setCorners(const Vector3& min, const Vector3& max) {
        b[0] = min[0]; b[3] = max[0];
        b[1] = min[1]; b[4] = max[1];
        b[2] = min[2]; b[5] = max[2];
    }

    // BBox3 = BBox3   uses the default assignment operator (bitwise copy)

    // BBox3 = Vector3   makes a BBox that exactly contains one point
    const BBox3& operator= (const Vector3 &v) {
        b[0] = v[0];  b[1] = v[0];
        b[2] = v[1];  b[3] = v[1];
        b[4] = v[2];  b[5] = v[2];
	return *this;
    }

    // BBox3 | BBox3   gives a new BBox that is the union
    friend const BBox3 operator| (const BBox3& bb1, const BBox3& bb2) {
	BBox3 b;
	register float *b1 = (float *)&(bb1), *b2 = (float *)&(bb2);
	b[0] = (*b1 < *b2 ? *b1 : *b2);  ++b1;  ++b2;
	b[1] = (*b1 > *b2 ? *b1 : *b2);  ++b1;  ++b2;
	b[2] = (*b1 < *b2 ? *b1 : *b2);  ++b1;  ++b2;
	b[3] = (*b1 > *b2 ? *b1 : *b2);  ++b1;  ++b2;
	b[4] = (*b1 < *b2 ? *b1 : *b2);  ++b1;  ++b2;
	b[5] = (*b1 > *b2 ? *b1 : *b2);
	return b;
    }
    const BBox3& operator|= (const BBox3& b2) {
	if (b[0] > b2.b[0]) b[0] = b2.b[0];
	if (b[1] < b2.b[1]) b[1] = b2.b[1];
	if (b[2] > b2.b[2]) b[2] = b2.b[2];
	if (b[3] < b2.b[3]) b[3] = b2.b[3];
	if (b[4] > b2.b[4]) b[4] = b2.b[4];
	if (b[5] < b2.b[5]) b[5] = b2.b[5];
	return *this;
    }

    // BBox3 | Vector3  includes both the box and the point
    const BBox3& operator|= (const Vector3& p) {
	if (p[0] < b[0]) b[0] = p[0];
	if (p[0] > b[1]) b[1] = p[0];
	if (p[1] < b[2]) b[2] = p[1];
	if (p[1] > b[3]) b[3] = p[1];
	if (p[2] < b[4]) b[4] = p[2];
	if (p[2] > b[5]) b[5] = p[2];
	return *this;
    }
    friend const BBox3 operator| (const BBox3& box, const Vector3& p) {
        BBox3 b(box);
	if (p[0] < b[0]) b[0] = p[0];
	if (p[0] > b[1]) b[1] = p[0];
	if (p[1] < b[2]) b[2] = p[1];
	if (p[1] > b[3]) b[3] = p[1];
	if (p[2] < b[4]) b[4] = p[2];
	if (p[2] > b[5]) b[5] = p[2];
	return b;
    }
    friend const BBox3 operator| (const Vector3& p, const BBox3& box) {
        return (box | p);
    }

    // update the box to include the sphere centered at p with radius
    const BBox3& operator|= (const BSphere3& s) {
        return *this |= BBox3 (s);
    }
    
    // Set this bound to the smallest bound containing p[0..n-1]
    void boundPoints (int n, const Vector3 *p);
    // Merge p[0..n-1] into our bounding box.
    void boundMorePoints (int n, const Vector3 *p);

    // BBox3 & BBox3   gives a new BBox that is the intersection
   const BBox3& operator&= (const BBox3& b2) {
	if (b[0] < b2.b[0]) b[0] = b2.b[0];
	if (b[1] > b2.b[1]) b[1] = b2.b[1];
	if (b[2] < b2.b[2]) b[2] = b2.b[2];
	if (b[3] > b2.b[3]) b[3] = b2.b[3];
	if (b[4] < b2.b[4]) b[4] = b2.b[4];
	if (b[5] > b2.b[5]) b[5] = b2.b[5];
	return *this;
    }
   // Comparisons between bounds
    friend bool operator== (const BBox3 &a, const BBox3 &b);
    friend bool operator!= (const BBox3 &a, const BBox3 &b);

    // Stream output -- very handy for debugging
    friend std::ostream& operator<< (std::ostream& out, const BBox3& a);

    void pad (float f) {
	b[0] -= f;  b[1] += f;
        b[2] -= f;  b[3] += f;
        b[4] -= f;  b[5] += f;
    }

    // Does the bound include a point?
    bool includes (const Vector3& p) const {
	return (p[0] >= b[0] && p[0] <= b[1] &&
                p[1] >= b[2] && p[1] <= b[3] &&
		p[2] >= b[4] && p[2] <= b[5]);
    }

    // Does the bound completely enclose the passed-in bound?
    bool includes (const BBox3& a) const {
        return a.b[0] >= b[0] && a.b[1] <= b[1] &&
            a.b[2] >= b[2] && a.b[3] <= b[3] &&
            a.b[4] >= b[4] && a.b[5] <= b[5];
    }
                
    // Return true if the bounding boxes overlap at all
    friend bool overlaps (const BBox3& b1, const BBox3& b2);

    // Return true if the bounding box overlaps with the given sphere
	bool overlaps (const BSphere3& a) const;

    // Return the volume of the box (clamp at float max)
    float volume (void) const;

    // Return the surface area of the box (clamp at float max)
    float area (void) const;

	// Return the length, width or height of the box
    // (not clamped, so dangerous for empty boxes)
    float length (int axis) const {
		assert(axis>=0 && axis < 3);  // range check -- only in DEBUG mode
		return b[2*axis+1]-b[2*axis];
	}

	// Return the length, width, height of the box
    // (not clamped, so dangerous for empty boxes)
    Vector3 diagonal (void) const {
	return Vector3 (b[1]-b[0], b[3]-b[2], b[5]-b[4]);
    }

    // Return the length of the diagonal of the box
    float diaglen (void) const {
		return diagonal().length();
    }

    // Return the center of the box
    Vector3 center (void) const {
	return Vector3 ((b[0]+b[1])*0.5f, (b[2]+b[3])*0.5f, (b[4]+b[5])*0.5f);
    }

    int longestAxis (void) const {
		return diagonal().longestAxis();
    }

    // Return one of the 8 corners of the box
    // 0:min,min,min 1:max,min,min 2:min,max,min 3:max,max,min
    // 4:min,min,max 5:max,min,max 6:min,max,max 7:max,max,max
    const Vector3 corner (int i) const {
        assert (i >= 0 && i < 8);
	return Vector3(b[i&1], b[2+((i/2)&1)], b[4+((i/4)&1)]);
    }

    // Retrieve all 8 corners of the box
    void getCorners (Vector3 *corners) const;

	bool intersect (const Vector3& origin, const Vector3& direction, float tNear, float tFar, float& tMin, float& tMax) const;
	bool intersect (const Vector3& origin, const Vector3& rcpD, const unsigned int signs[3], float tNear, float tFar) const;


public:
    union {
		float b[6];     // xmin, xmax, ymin, ymax, zmin, zmax
		struct { float xmin, xmax, ymin, ymax, zmin, zmax; };
	};
};


};  /* end namespace xrt */


#endif  /* !defined(__BBOX3_H__) */