/*
   BobToolz plugin for GtkRadiant
   Copyright (C) 2001 Gordon Biggans

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

// DPlane.cpp: implementation of the DPlane class.
//
//////////////////////////////////////////////////////////////////////

#include "DPlane.h"

#include <list>

#include "DPoint.h"
#include "DWinding.h"

#include "str.h"
#include "misc.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

DPlane::DPlane( const vec3_t va, const vec3_t vb, const vec3_t vc, const _QERFaceData* texData ){
	MakeNormal( va, vb, vc, normal );
	if ( VectorNormalize( normal, normal ) == 0 ) {  // normalizes and returns length
		globalErrorStream() << "DPlane::DPlane: Bad Normal.\n";
	}

	_d = ( normal[0] * va[0] ) + ( normal[1] * va[1] ) + ( normal[2] * va[2] );

	VectorCopy( va, points[0] );
	VectorCopy( vb, points[1] );
	VectorCopy( vc, points[2] );

	m_bChkOk = true;

	if ( texData ) {
		memcpy( &texInfo, texData, sizeof( _QERFaceData ) );
	}
	else{
		FillDefaultTexture( &texInfo, points[0], points[1], points[2], "textures/common/caulk" );
	}
}

DPlane::~DPlane(){

}

//////////////////////////////////////////////////////////////////////
// Implementation
//////////////////////////////////////////////////////////////////////

vec_t DPlane::DistanceToPoint( vec3_t pnt ){
	vec3_t tmp;
	VectorSubtract( pnt, points[0], tmp );
	return DotProduct( tmp, normal );
}

bool DPlane::PlaneIntersection( DPlane *pl1, DPlane *pl2, vec3_t out ){
	float a1, a2, a3;
	float b1, b2, b3;
	float c1, c2, c3;

	a1 = normal[0];         a2 = normal[1];         a3 = normal[2];
	b1 = pl1->normal[0];    b2 = pl1->normal[1];    b3 = pl1->normal[2];
	c1 = pl2->normal[0];    c2 = pl2->normal[1];    c3 = pl2->normal[2];

	float d = Determinant3x3( a1, a2, a3, b1, b2, b3, c1, c2, c3 );

	if ( d == 0 ) {
		return false;
	}

	float v1 = _d;
	float v2 = pl1->_d;
	float v3 = pl2->_d;

	float d1 = Determinant3x3( v1, a2, a3, v2, b2, b3, v3, c2, c3 );
	float d2 = Determinant3x3( a1, v1, a3, b1, v2, b3, c1, v3, c3 );
	float d3 = Determinant3x3( a1, a2, v1, b1, b2, v2, c1, c2, v3 );

	out[0] = d1 / d;
	out[1] = d2 / d;
	out[2] = d3 / d;

	return true;
}

bool DPlane::IsRedundant( std::list<DPoint*>& pointList ){
	int cnt = 0;

	//std::list<DPoint *>::const_iterator point=pointList.begin();
	for ( std::list<DPoint *>::const_iterator point = pointList.begin(); point != pointList.end(); point++ )
	{
		if ( fabs( DistanceToPoint( ( *point )->_pnt ) ) < MAX_ROUND_ERROR ) {
			cnt++;
		}

		if ( cnt == 3 ) {
			return false;
		}
	}
	return true;
}

bool DPlane::operator ==( DPlane& other ){
	vec3_t chk;
	VectorSubtract( other.normal, normal, chk );
	if ( fabs( VectorLength( chk ) ) > MAX_ROUND_ERROR ) {
		return false;
	}

	if ( fabs( other._d - _d ) > MAX_ROUND_ERROR ) {
		return false;
	}

	return true;
}

bool DPlane::operator !=( DPlane& other ){
	vec3_t chk;
	VectorAdd( other.normal, normal, chk );
	if ( fabs( VectorLength( chk ) ) > MAX_ROUND_ERROR ) {
		return false;
	}

	return true;
}

DWinding* DPlane::BaseWindingForPlane(){
	int i, x;
	vec_t max, v;
	vec3_t org, vright, vup;

// find the major axis

	max = -131072;
	x = -1;
	for ( i = 0 ; i < 3; i++ )
	{
		v = (float)fabs( normal[i] );
		if ( v > max ) {
			x = i;
			max = v;
		}
	}
	if ( x == -1 ) {
		globalOutputStream() << "BaseWindingForPlane: no axis found";
	}

	VectorCopy( vec3_origin, vup );
	switch ( x )
	{
	case 0:
	case 1:
		vup[2] = 1;
		break;
	case 2:
		vup[0] = 1;
		break;
	}

	v = DotProduct( vup, normal );
	VectorMA( vup, -v, normal, vup );
	VectorNormalize( vup, vup );

	VectorScale( normal, _d, org );

	CrossProduct( vup, normal, vright );

	VectorScale( vup, 131072, vup );
	VectorScale( vright, 131072, vright );

// project a really big	axis aligned box onto the plane
	DWinding* w = new DWinding;
	w->AllocWinding( 4 );

	VectorSubtract( org, vright, w->p[0] );
	VectorAdd( w->p[0], vup, w->p[0] );

	VectorAdd( org, vright, w->p[1] );
	VectorAdd( w->p[1], vup, w->p[1] );

	VectorAdd( org, vright, w->p[2] );
	VectorSubtract( w->p[2], vup, w->p[2] );

	VectorSubtract( org, vright, w->p[3] );
	VectorSubtract( w->p[3], vup, w->p[3] );

	return w;
}

void DPlane::Rebuild(){
	vec3_t v1, v2;
	VectorSubtract( points[0], points[1], v1 );
	VectorSubtract( points[2], points[1], v2 );
	CrossProduct( v1, v2, normal );

	if ( VectorNormalize( normal, normal ) == 0 ) {  // normalizes and returns length
		globalErrorStream() << "DPlane::Rebuild: Bad Normal.\n";
	}

	_d = ( normal[0] * points[0][0] ) + ( normal[1] * points[0][1] ) + ( normal[2] * points[0][2] );

	VectorCopy( points[0], texInfo.m_p0 );
	VectorCopy( points[1], texInfo.m_p1 );
	VectorCopy( points[2], texInfo.m_p2 );
}

bool DPlane::AddToBrush( scene::Node& brush ){
	bool changed = false;
	if ( !( m_bChkOk || !strcmp( m_shader.c_str(), "textures/common/caulk" ) ) ) {
		m_shader = "textures/common/caulk";
		changed = true;
	}

	_QERFaceData faceData;
	faceData.m_p0 = vector3_from_array( points[0] );
	faceData.m_p1 = vector3_from_array( points[1] );
	faceData.m_p2 = vector3_from_array( points[2] );
	faceData.m_texdef = texInfo.m_texdef;
	faceData.m_shader = m_shader.c_str();
	GlobalBrushCreator().Brush_addFace( brush, faceData );

	return changed;
}

void DPlane::ScaleTexture()
{ }

DPlane::DPlane( const vec3_t va, const vec3_t vb, const vec3_t vc, const char* textureName, bool bDetail ){
	vec3_t v1, v2;
	VectorSubtract( va, vb, v1 );
	VectorSubtract( vc, vb, v2 );
	CrossProduct( v1, v2, normal );

	if ( VectorNormalize( normal, normal ) == 0 ) {  // normalizes and returns length
		globalErrorStream() << "DPlane::DPlane: Bad Normal.\n";
	}

	_d = ( normal[0] * va[0] ) + ( normal[1] * va[1] ) + ( normal[2] * va[2] );

	VectorCopy( va, points[0] );
	VectorCopy( vb, points[1] );
	VectorCopy( vc, points[2] );

	m_bChkOk = true;

	FillDefaultTexture( &texInfo, points[0], points[1], points[2], textureName );
	if ( bDetail ) {
		texInfo.contents |= FACE_DETAIL;
	}
}
