/***************************************************************************
                          collisionface.cpp  -  description
                             -------------------
    begin                : wo nov 26 2003
    copyright            : (C) 2003 by CJP
    email                : cornware-cjp@users.sourceforge.net
 ***************************************************************************/

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

#include <cmath>

#include "collisionface.h"

CCollisionFace::CCollisionFace()
{
	material = NULL;
}

CCollisionFace::CCollisionFace(const CCollisionFace &f)
{
	for(unsigned int i=0; i < f.size(); i++)
		push_back(f[i]);

	d = f.d;
	nor = f.nor;
	reverse = f.reverse;
	material = f.material;

	isWater = f.isWater;
	isSurface = f.isSurface;
}

const CCollisionFace &CCollisionFace::operator=(const CCollisionFace &f)
{
	clear();
	for(unsigned int i=0; i < f.size(); i++)
		push_back(f[i]);

	d = f.d;
	nor = f.nor;
	reverse = f.reverse;
	material = f.material;

	isWater = f.isWater;
	isSurface = f.isSurface;

	return *this;
}

void CCollisionFace::cull(const CCollisionFace &plane, const CVector &dr)
{
	//translation of the plane
	CVector plane_nor = plane.nor;
	float plane_d = plane.d + dr.dotProduct(plane.nor);

	CCollisionFace theFace2;
	theFace2.nor = nor;
	theFace2.d = d;
	theFace2.material = material;
	theFace2.isSurface = isSurface;
	theFace2.isWater = isWater;

	//The first vertex
	float dist_first = operator[](0).dotProduct(plane_nor);
	bool inside_first = (dist_first < plane_d);
	if(inside_first)
	{
		theFace2.push_back(operator[](0));
	}
	float dist_prev = dist_first;
	bool inside_prev = inside_first;

	//The next vertices
	for(unsigned int j=1; j < size(); j++)
	{
		float dist = operator[](j).dotProduct(plane_nor);
		bool inside = (dist < plane_d);

		if(inside)
		{
			if(!inside_prev) //going inward
			{
				CVector mid = ((dist_prev-plane_d)*operator[](j) + (plane_d-dist)*operator[](j-1)) / (dist_prev-dist);
				theFace2.push_back(mid);
			}

			theFace2.push_back(operator[](j));
		}
		else if(inside_prev) //going outward
		{
			CVector mid = ((dist_prev-plane_d)*operator[](j) + (plane_d-dist)*operator[](j-1)) / (dist_prev-dist);
			theFace2.push_back(mid);
		}

		inside_prev = inside;
		dist_prev = dist;
	}

	//The last vertex
	if(inside_prev != inside_first) //first+last not the same
	{
		unsigned int j = size()-1;
		CVector mid = ((dist_prev-plane_d)*operator[](0) + (plane_d-dist_first)*operator[](j)) / (dist_prev-dist_first);
		theFace2.push_back(mid);
	}

	operator=(theFace2);
}

const CCollisionFace &CCollisionFace::operator+=(const CVector &r)
{
	for(unsigned int i=0; i < size(); i++)
		operator[](i) += r;

	d += r.dotProduct(nor);

	return *this;
}

const CCollisionFace &CCollisionFace::operator*=(const CMatrix &m)
{
	for(unsigned int i=0; i < size(); i++)
		operator[](i) *= m;

	nor *= m;

	return *this;
}

const CCollisionFace &CCollisionFace::operator/=(const CMatrix &m)
{
	for(unsigned int i=0; i < size(); i++)
		operator[](i) /= m;

	nor /= m;

	return *this;
}

bool CCollisionFace::isInside(const CVector &pt) const
{
	float angle = 0.0;
	for(unsigned int j=1; j<size(); j++)
	{
		CVector p1 = (*this)[j] - pt;
		CVector p2 = (*this)[j-1] - pt;
		float inpr = p1.dotProduct(p2);
		inpr /= (p1.abs() * p2.abs());
		angle += acos(inpr);
	}
	{
		CVector p1 = (*this)[0] - pt;
		CVector p2 = back() - pt;
		float inpr = p1.dotProduct(p2);
		inpr /= (p1.abs() * p2.abs());
		angle += acos(inpr);
	}

	return (angle > 6.2831 && angle < 6.2832); //!= 2*pi: outside face
}
