/*
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
*/

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

#include "DShape.h"

#include <list>
#include <time.h>
#include "str.h"

#include "DPoint.h"
#include "DPlane.h"
#include "DBrush.h"
#include "DEPair.h"
#include "DPatch.h"
#include "DEntity.h"

//#include "dialogs-gtk.h"

#include "misc.h"
#include "shapes.h"

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

bool bFacesAll[6] = {true, true, true, true, true, true};

DShape::DShape()
{
	m_nNextBrush = 0;
}

DShape::~DShape()
{

}

void DShape::BuildRegularPrism(vec3_t min, vec3_t max, int nSides, bool bAlignTop)
{
	vec3_t vc[MAX_POLYGON_FACES+2], vd[MAX_POLYGON_FACES+2];

	vec3_t radius;
	vec3_t origin;

	VectorSubtract(max, min, radius);
	VectorScale(radius, 0.5f, radius);
	// calc 3d radius and origin
	VectorAdd(max, min, origin);
	VectorScale(origin, 0.5f, origin);

	float phase = 0.0f;

	if(bAlignTop)
	{
		phase = -(Q_PI/nSides);
		VectorScale(radius, static_cast<float>(1.0 / cos(phase)), radius);
	}

	//----- Build Polygon Vertices -----

	int i;
	for(i = 0; i < nSides; i++)
	{
		VectorCopy(origin, vc[i]);
		VectorCopy(origin, vd[i]);
		
		vc[i][2] = min[2];
		vd[i][2] = max[2];

		vc[i][0] += radius[0] * sinf( ( 2 * Q_PI * i / nSides ) + phase );
		vc[i][1] += radius[1] * cosf( ( 2 * Q_PI * i / nSides ) + phase );

		vd[i][0] = vc[i][0];
		vd[i][1] = vc[i][1];
	}

	VectorCopy(vc[0], vc[nSides]);
	VectorCopy(vd[0], vd[nSides]);
	VectorCopy(vc[1], vc[nSides+1]);
	VectorCopy(vd[1], vd[nSides+1]);
	
	//----------------------------------

	DBrush* pB = m_Container.GetWorldSpawn()->NewBrush(m_nNextBrush++);

	for(i = 1; i <= nSides; i++)
		pB->AddFace(vc[i-1], vc[i], vd[i], GetCurrentTexture(), false);

	pB->AddFace(vc[2], vc[1], vc[0], "textures/common/caulk", false);
	pB->AddFace(vd[0], vd[1], vd[2], "textures/common/caulk", false);
}

void DShape::Commit()
{
	m_Container.GetWorldSpawn()->FixBrushes();
	m_Container.BuildInRadiant(true);
}

void DShape::BuildInversePrism(vec3_t min, vec3_t max, int nSides, bool bAlignTop)
{
	vec3_t va[MAX_POLYGON_FACES+1], vb[MAX_POLYGON_FACES+1];
	vec3_t radius;
	vec3_t origin;

	VectorSubtract(max, min, radius);
	VectorScale(radius, 0.5f, radius);
	// calc 3d radius and origin
	VectorAdd(max, min, origin);
	VectorScale(origin, 0.5f, origin);

	float phase = 0.0f;

	if(bAlignTop)
	{
		phase = -(Q_PI/nSides);
		VectorScale(radius, static_cast<float>(1.0 / cos(phase)), radius);
	}

	//----- Build Polygon Vertices -----

	int i;
	for(i = 0; i < nSides; i++)
	{
		VectorCopy(origin, va[i]);
		VectorCopy(origin, vb[i]);

		va[i][2] = min[2];
		vb[i][2] = max[2];

		va[i][0] += radius[0] * sinf( ( 2 * Q_PI * i / nSides ) + phase );
		va[i][1] += radius[1] * cosf( ( 2 * Q_PI * i / nSides ) + phase );

		vb[i][0] = va[i][0];
		vb[i][1] = va[i][1];
	}

	VectorCopy(va[0], va[nSides]);
	VectorCopy(vb[0], vb[nSides]);
	
	//----------------------------------

	for(i = 1; i <= nSides; i++)
	{
		DBrush* pB = GetBoundingCube(min, max, "textures/common/caulk");

		vec3_t top, bottom;
		VectorCopy(va[i-1], top);
		VectorCopy(va[i], bottom);

		if(va[i-1][1] > va[i][1])
		{
			top[0] += 5;
			bottom[0] += 5;
		}
		else	// flip direction of plane on crossover
		{
			top[0] -= 5;
			bottom[0] -= 5;
		}
		
		if(top[1] != bottom[1])	// internal line is flat already if true
		{			
			pB->AddFace(va[i-1], top, vb[i-1], "textures/common/caulk", false);
			pB->AddFace(va[i], vb[i], bottom, "textures/common/caulk", false);
		}	// add cut-off planes

		pB->AddFace(va[i-1], vb[i-1], vb[i], GetCurrentTexture(), false);
		// add internal polygon plane
	}
}

void DShape::BuildBorderedPrism(vec3_t min, vec3_t max, int nSides, int nBorder, bool bAlignTop)
{
	vec3_t va[MAX_POLYGON_FACES+2], vb[MAX_POLYGON_FACES+2];
	vec3_t vc[MAX_POLYGON_FACES+2], vd[MAX_POLYGON_FACES+2];

	vec3_t radius;
	vec3_t origin;

	VectorSubtract(max, min, radius);
	VectorScale(radius, 0.5f, radius);
	// calc 3d radius and origin
	VectorAdd(max, min, origin);
	VectorScale(origin, 0.5f, origin);

	if(nBorder >= Min(radius[0], radius[1]))
	{
//		DoMessageBox("Border is too large", "Error", MB_OK);
		return;
	}

	float phase = 0.0f;

	if(bAlignTop)
	{
		phase = -(Q_PI/nSides);
		VectorScale(radius, static_cast<float>(1.0 / cos(phase)), radius);
	}

	//----- Build Polygon Vertices -----

	int i;
	for(i = 0; i < nSides; i++)
	{
		VectorCopy(origin, va[i]);
		VectorCopy(origin, vb[i]);
		VectorCopy(origin, vc[i]);
		VectorCopy(origin, vd[i]);

		va[i][2] = min[2];
		vb[i][2] = max[2];

		va[i][0] += (radius[0] - nBorder) * sinf( ( 2 * Q_PI * i / nSides ) + phase );
		va[i][1] += (radius[1] - nBorder) * cosf( ( 2 * Q_PI * i / nSides ) + phase );

		vb[i][0] = va[i][0];
		vb[i][1] = va[i][1];


		
		vc[i][2] = min[2];
		vd[i][2] = max[2];

		vc[i][0] += radius[0] * sinf( ( 2 * Q_PI * i / nSides ) + phase );
		vc[i][1] += radius[1] * cosf( ( 2 * Q_PI * i / nSides ) + phase );

		vd[i][0] = vc[i][0];
		vd[i][1] = vc[i][1];
	}

	VectorCopy(va[0], va[nSides]);
	VectorCopy(vb[0], vb[nSides]);
	VectorCopy(va[1], va[nSides+1]);
	VectorCopy(vb[1], vb[nSides+1]);

	VectorCopy(vc[0], vc[nSides]);
	VectorCopy(vd[0], vd[nSides]);
	VectorCopy(vc[1], vc[nSides+1]);
	VectorCopy(vd[1], vd[nSides+1]);
	
	//----------------------------------

	for(i = 1; i <= nSides; i++)
	{
		DBrush* pB = GetBoundingCube(min, max, "textures/common/caulk");

		pB->AddFace(origin, vc[i-1], vd[i-1], "textures/common/caulk", false);
		pB->AddFace(origin, vd[i], vc[i], "textures/common/caulk", false); 

		pB->AddFace(vc[i-1], vc[i], vd[i], GetCurrentTexture(), false);
		pB->AddFace(vb[i], va[i], va[i-1], GetCurrentTexture(), false);
	}
}

DBrush* DShape::GetBoundingCube_Ext(vec3_t min, vec3_t max, const char *textureName, bool* bUseFaces, bool detail)
{
	DBrush* pB = new DBrush;
	//----- Build Outer Bounds ---------

	vec3_t v1, v2, v3, v5, v6, v7;
	VectorCopy(min, v1);
	VectorCopy(min, v2);
	VectorCopy(min, v3);
	VectorCopy(max, v5);
	VectorCopy(max, v6);
	VectorCopy(max, v7);

	v2[0] = max[0];
	v3[1] = max[1];

	v6[0] = min[0];
	v7[1] = min[1];

	//----------------------------------

	//----- Add Six Cube Faces ---------

	if(bUseFaces[0])
		pB->AddFace(v1, v2, v3, textureName, detail);
	if(bUseFaces[1])
		pB->AddFace(v1, v3, v6, textureName, detail);
	if(bUseFaces[2])
		pB->AddFace(v1, v7, v2, textureName, detail);

	if(bUseFaces[3])
		pB->AddFace(v5, v6, v3, textureName, detail);
	if(bUseFaces[4])
		pB->AddFace(v5, v2, v7, textureName, detail);
	if(bUseFaces[5])
		pB->AddFace(v5, v7, v6, textureName, detail);

	//----------------------------------

	return pB;
}

DBrush* DShape::GetBoundingCube(vec3_t min, vec3_t max, const char *textureName, DEntity* ent, bool* bUseFaces)
{
	DBrush* pB;
	if(ent == NULL)
		pB = m_Container.GetWorldSpawn()->NewBrush(m_nNextBrush++);
	else
		pB = ent->NewBrush(m_nNextBrush++);

	//----- Build Outer Bounds ---------

	vec3_t v1, v2, v3, v5, v6, v7;
	VectorCopy(min, v1);
	VectorCopy(min, v2);
	VectorCopy(min, v3);
	VectorCopy(max, v5);
	VectorCopy(max, v6);
	VectorCopy(max, v7);

	v2[0] = max[0];
	v3[1] = max[1];

	v6[0] = min[0];
	v7[1] = min[1];

	//----------------------------------

	//----- Add Six Cube Faces ---------

	if(bUseFaces[0])
		pB->AddFace(v1, v2, v3, textureName, false);
	if(bUseFaces[1])
		pB->AddFace(v1, v3, v6, textureName, false);
	if(bUseFaces[2])
		pB->AddFace(v1, v7, v2, textureName, false);

	if(bUseFaces[3])
		pB->AddFace(v5, v6, v3, textureName, false);
	if(bUseFaces[4])
		pB->AddFace(v5, v2, v7, textureName, false);
	if(bUseFaces[5])
		pB->AddFace(v5, v7, v6, textureName, false);

	//----------------------------------

	return pB;
}

bool DShape::BuildPit(vec3_t min, vec3_t max)
{
	if((max[2] - min[2]) < 196)
		return false;

	srand(time(NULL));

	vec3_t centre;
	VectorAdd(min, max, centre);
	VectorScale(centre, 0.5f, centre);

	char buffer[256];

	int team = (rand()%10000)+5000;

// ************* SPEAKER ***************
	sprintf(buffer, "t%i_1", team);

// trigger for speaker
	vec3_t triggerVoiceBtm;
	VectorCopy(min, triggerVoiceBtm);
	triggerVoiceBtm[2] = max[2] - 16;

	DEntity* triggerVoice = m_Container.AddEntity("trigger_multiple");
	GetBoundingCube(triggerVoiceBtm, max, "textures/common/trigger", triggerVoice);
	triggerVoice->AddEPair("target", buffer);
//--------------------

// target for speaker
	vec3_t voiceOrigin;
	VectorCopy(centre, voiceOrigin);
	voiceOrigin[2] = max[2]+16;


	DEntity* targetVoice = m_Container.AddEntity("target_speaker");
	targetVoice->AddEPair("targetname", buffer);

	sprintf(buffer, "%f %f %f", voiceOrigin[0], voiceOrigin[1], voiceOrigin[2]);
	targetVoice->AddEPair("origin", buffer);
	targetVoice->AddEPair("spawnflags", "8");
	targetVoice->AddEPair("noise", "*falling1.wav");
//--------------------

// *********** END SPEAKER *************

// ********* POWERUP REMOVAL ***********
	sprintf(buffer, "t%i_2", team);

// trigger for powerup removal
	vec3_t triggerPwrRmvTop, triggerPwrRmvBtm;
	VectorCopy(min, triggerPwrRmvBtm);
	VectorCopy(max, triggerPwrRmvTop);

	triggerPwrRmvTop[2] = triggerVoiceBtm[2] - 64;
	triggerPwrRmvBtm[2] = triggerPwrRmvTop[2] - 16;

	DEntity* triggerPwrRmv = m_Container.AddEntity("trigger_multiple");
	GetBoundingCube(triggerPwrRmvBtm, triggerPwrRmvTop, "textures/common/trigger", triggerPwrRmv);
	triggerPwrRmv->AddEPair("target", buffer);
//--------------------

// target for powerup removal
	vec3_t pwrRmvOrigin;
	VectorCopy(centre, pwrRmvOrigin);
	pwrRmvOrigin[2] = triggerPwrRmvTop[2]+16;

	DEntity* targetPwrRmv = m_Container.AddEntity("target_remove_powerups");
	targetPwrRmv->AddEPair("targetname", buffer);

	sprintf(buffer, "%f %f %f", pwrRmvOrigin[0], pwrRmvOrigin[1], pwrRmvOrigin[2]);
	targetPwrRmv->AddEPair("origin", buffer);
//--------------------

// ****** END POWERUP REMOVAL ********

// ********* DAMAGE ***********

// trigger for damage
	vec3_t triggerDmgTop, triggerDmgBtm;
	VectorCopy(min, triggerDmgBtm);
	VectorCopy(max, triggerDmgTop);

	triggerDmgBtm[2] = min[2] + 64;
	triggerDmgTop[2] = triggerDmgBtm[2] + 16;

	DEntity* triggerDmg = m_Container.AddEntity("trigger_hurt");
	GetBoundingCube(triggerDmgBtm, triggerDmgTop, "textures/common/trigger", triggerDmg);
	triggerDmg->AddEPair("dmg", "9999");
	triggerDmg->AddEPair("spawnflags", "12");
//--------------------

// ****** END DAMAGE ********

// ********* NODROP ***********

	vec3_t nodropTop;
	VectorCopy(max, nodropTop);

	nodropTop[2] = min[2] + 64;

	GetBoundingCube(min, nodropTop, "textures/common/nodrop");

// ****** END NODROP ********

	return true;
}
