#include "Brush.h"

Brush::Brush( const Plane * planearray, int size ) {
	for( int i = 0; i < size; i++ ) {
		this->planes.push_back( planearray[ i ] );
	}
}

BrushIntersection Brush::Intersect( const Ray & ray ) const {
	float startFraction = -1.0f;
	float endFraction = std::numeric_limits< float >::infinity( );
	const Plane * startplane, * endplane;

	for( std::list< Plane >::const_iterator iter = this->planes.begin( ); iter != this->planes.end( ); iter++ ) {
		const Plane & plane = *iter;

		// find intersection point
		float startDistance = plane.Distance( ray.origin );
		float intersection = plane.Intersect( ray );
		
		if( intersection == std::numeric_limits< float >::infinity( ) ) {
			if( startDistance > 0.0f ) {
				// ray is outside the volume and doesn't intersect any planes
				return BrushIntersection( );
			} else {
				// ray is inside the volume, but doesn't intersect this plane
				continue;
			}
		} else {
			if( startDistance > 0.0f ) {
				// front intersection
				if( intersection > startFraction ) {
					// record furthest intersection
					startFraction = intersection;
					startplane = &plane;
				}
			} else {
				// back intersection
				if( intersection < endFraction ) {
					// record closest intersection
					endFraction = intersection;
					endplane = &plane;
				}
			}
		}
	}

	if( startFraction < 0.0f ) {
		return BrushIntersection( endFraction, endplane->GetNormal( ) );
	} else if( startFraction < endFraction && startFraction > 0.0f ) {
		return BrushIntersection( startFraction, startplane->GetNormal( ) );
	} else {
		return BrushIntersection( );
	}
}

BrushIntersection Brush::Intersect( const LineSegment & line ) const {
	float startFraction = -1.0f;
	float endFraction = 2.0f;
	const Plane * startplane, * endplane;

	for( std::list< Plane >::const_iterator iter = this->planes.begin( ); iter != this->planes.end( ); iter++ ) {
		const Plane & plane = *iter;

		// find distances between the endpoints and the plane
		float startDistance = plane.Distance( line.a );
		float endDistance = plane.Distance( line.b );

		if( startDistance >= 0.0f && endDistance >= 0.0f ) {
			// both endpoints lie outside this plane; it can not intersect the volume
			return BrushIntersection( );
		} else if( startDistance < 0.0f && endDistance < 0.0f ) {
			// both endpoints lie inside the plane; continue looping
			continue;
		} else if( startDistance > endDistance ) {
			// endpoints straddle the plane, this is a front intersection
			// find how far down this line the plane intersects
			float fraction = startDistance / ( startDistance - endDistance );
			if( fraction > startFraction ) {
				// save the furthest intersection
				startFraction = fraction;
				startplane = &plane;
			}
		} else {
			// back intersection
			float fraction = startDistance / ( startDistance - endDistance );
			if( fraction < endFraction ) {
				// save the closest intersection
				endFraction = fraction;
				endplane = &plane;
			}
		}
	}

	if( startFraction < 0.0f && endFraction < 2.0f ) {
		// line started inside
		return BrushIntersection( endFraction, endplane->GetNormal( ) );
	} else if( startFraction < endFraction ) {
		// line started outside
		return BrushIntersection( startFraction, startplane->GetNormal( ) );
	} else {
		// no intersection
		return BrushIntersection( );
	}
}
