/*
 * (C) Copyright Ian D. Romanick 2007
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * on the rights to use, copy, modify, merge, publish, distribute, sub
 * license, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
 * AUTHORS, COPYRIGHT HOLDERS, AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "aabb.h"

#define MIN(a,b)  (((a) < (b)) ? (a) : (b))
#define MAX(a,b)  (((a) > (b)) ? (a) : (b))

void aabb::add_point(const idr_vec4 &p)
{
    if (! initialized) {
	corners[0] = p;
	corners[1] = p;
	initialized = true;
    }
    else {
	corners[0][0] = MIN(corners[0][0], p[0]);
	corners[0][1] = MIN(corners[0][1], p[1]);
	corners[0][2] = MIN(corners[0][2], p[2]);

	corners[1][0] = MAX(corners[1][0], p[0]);
	corners[1][1] = MAX(corners[1][1], p[1]);
	corners[1][2] = MAX(corners[1][2], p[2]);
    }
}


void aabb::union_with(const aabb &a)
{
    if (!initialized || !a.initialized) {
	return;
    }

    corners[0][0] = MAX(corners[0][0], a.corners[0][0]);
    corners[0][1] = MAX(corners[0][1], a.corners[0][1]);
    corners[0][2] = MAX(corners[0][2], a.corners[0][2]);

    corners[1][0] = MIN(corners[1][0], a.corners[1][0]);
    corners[1][1] = MIN(corners[1][1], a.corners[1][1]);
    corners[1][2] = MIN(corners[1][2], a.corners[1][2]);

    const idr_vec4 d(corners[1] - corners[0]);
    if ((d[0] < 0.0) || (d[1] < 0.0) || (d[2] < 0.0)) {
	initialized = false;
    }
}


void aabb::intersect_with(const aabb &a)
{
    if (a.initialized) {
	if (! initialized) {
	    corners[0] = a.corners[0];
	    corners[1] = a.corners[1];
	    initialized = a.initialized;
	}
	else {
	    corners[0][0] = MIN(corners[0][0], a.corners[0][0]);
	    corners[0][1] = MIN(corners[0][1], a.corners[0][1]);
	    corners[0][2] = MIN(corners[0][2], a.corners[0][2]);

	    corners[1][0] = MAX(corners[1][0], a.corners[1][0]);
	    corners[1][1] = MAX(corners[1][1], a.corners[1][1]);
	    corners[1][2] = MAX(corners[1][2], a.corners[1][2]);
	}
    }
}


float aabb::volume(void) const
{
    if (!initialized) return 0.0;

    const float l = fabs(corners[1][0] - corners[0][0]);
    const float w = fabs(corners[1][1] - corners[0][1]);
    const float h = fabs(corners[1][2] - corners[0][2]);

    return l * w * h;
}


float aabb::surface_area(void) const
{
    if (!initialized) return 0.0;

    const float l = fabs(corners[1][0] - corners[0][0]);
    const float w = fabs(corners[1][1] - corners[0][1]);
    const float h = fabs(corners[1][2] - corners[0][2]);

    return 2 * ((l * w) + (l * h) + (h * w));
}


float aabb::margin(void) const
{
    if (!initialized) return 0.0;

    const float l = fabs(corners[1][0] - corners[0][0]);
    const float w = fabs(corners[1][1] - corners[0][1]);
    const float h = fabs(corners[1][2] - corners[0][2]);

    return l + w + h;
}


bool aabb::overlaps(const aabb &a) const
{
    aabb temp = *this;
    
    temp.union_with(a);
    return temp.initialized;
}
