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

// BobView.cpp: implementation of the DBobView class.
//
//////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "DBobView.h"
#include "DListener.h"
#include "misc.h"
#include "funchandlers.h"

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

DBobView::DBobView()
{
	nPathCount = 0;
	refCount = 1;
	
	m_bHooked = FALSE;

	path = NULL;
	eyes = NULL;

	boundingShow = BOUNDS_APEX;
}

DBobView::~DBobView()
{
	if(path)
		delete[] path;

	// oops forgot to remove our eyes, was causing access violation when it tried
	// to talk to it's parent
	if(eyes)
		delete eyes;

	if(m_bHooked)
		UnRegister();

	g_PathView = NULL;
}

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

void DBobView::Draw2D(VIEWTYPE vt)
{
	if(!path)
		return;

	g_QglTable.m_pfn_qglPushAttrib(GL_ALL_ATTRIB_BITS);

	g_QglTable.m_pfn_qglDisable(GL_BLEND);
	g_QglTable.m_pfn_qglEnable(GL_LINE_SMOOTH);

	g_QglTable.m_pfn_qglPushMatrix();
	
	switch(vt)
	{
	case XY:
		break;
	case XZ:
		g_QglTable.m_pfn_qglRotatef(270.0f, 1.0f, 0.0f, 0.0f);
		break;
	case YZ:
		g_QglTable.m_pfn_qglRotatef(270.0f, 1.0f, 0.0f, 0.0f);
		g_QglTable.m_pfn_qglRotatef(270.0f, 0.0f, 0.0f, 1.0f);
		break;
	}

	g_QglTable.m_pfn_qglLineWidth(1.0f);
	g_QglTable.m_pfn_qglColor4f(1.0f, 0.0f, 0.0f, 1.0f);

	int i;

	g_QglTable.m_pfn_qglBegin(GL_LINE_STRIP);

	for(i = 0; i < nPathCount; i++)
		g_QglTable.m_pfn_qglVertex3fv(path[i]);

	g_QglTable.m_pfn_qglEnd();

	if(m_bShowExtra)
	{
		// +mars
		// for the bounding box stuff
		g_QglTable.m_pfn_qglColor4f(0.25f, 0.75f, 0.75f, 1.0f);

		g_QglTable.m_pfn_qglTranslatef( 16.0f, 16.0f, 28.0f );

		g_QglTable.m_pfn_qglBegin( GL_LINE_STRIP );
	
			for ( i = 0; i < nPathCount; i++ )
				g_QglTable.m_pfn_qglVertex3fv( path[i] );

		g_QglTable.m_pfn_qglEnd();
	
	// ---------------

		g_QglTable.m_pfn_qglTranslatef( -16.0f, -16.0f, -28.0f );	// back to where we were
		g_QglTable.m_pfn_qglTranslatef( -16.0f, 16.0f, 28.0f );		// move to new postion
	
		g_QglTable.m_pfn_qglBegin( GL_LINE_STRIP );

			for ( i = 0; i < nPathCount; i++ )
				g_QglTable.m_pfn_qglVertex3fv( path[i] );

		g_QglTable.m_pfn_qglEnd();

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

		g_QglTable.m_pfn_qglTranslatef( 16.0f, -16.0f, -28.0f );		// back to where we were
		g_QglTable.m_pfn_qglTranslatef( 16.0f, -16.0f, -28.0f );		// new pos

		g_QglTable.m_pfn_qglBegin( GL_LINE_STRIP );
		
			for ( i = 0; i < nPathCount; i++ )
				g_QglTable.m_pfn_qglVertex3fv( path[i] );

		g_QglTable.m_pfn_qglEnd();

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

		g_QglTable.m_pfn_qglTranslatef( -16.0f, 16.0f, 28.0f );		// back to where we were

/*		g_QglTable.m_pfn_qglTranslatef( -16.0f, -16.0f, -28.0f );		// new pos

		g_QglTable.m_pfn_qglBegin( GL_LINE_STRIP );
		
		if ( boundingShow == BOUNDS_ALL )
		{
			for ( i = 0; i < nPathCount; i++ )
				g_QglTable.m_pfn_qglVertex3fv( path[i] );
		}
		else if ( boundingShow == BOUNDS_APEX )
		{
			for ( i = (nPathCount/4); i < (nPathCount/4) * 3; i++ )
				g_QglTable.m_pfn_qglVertex3fv( path[i] );
		}

		g_QglTable.m_pfn_qglEnd();*/ // djbob: er, um doesn't really seem to do anyhting
	}

	// -mars
	
	g_QglTable.m_pfn_qglPopMatrix();

	g_QglTable.m_pfn_qglPopAttrib();
}

void DBobView::Draw3D()
{
	if(!path)
		return;

	g_QglTable.m_pfn_qglPushAttrib(GL_ALL_ATTRIB_BITS);

	g_QglTable.m_pfn_qglDisable(GL_BLEND);
	g_QglTable.m_pfn_qglEnable(GL_LINE_SMOOTH);

	g_QglTable.m_pfn_qglLineWidth(1.0f);
	g_QglTable.m_pfn_qglColor4f(1.0f, 0.0f, 0.0f, 1.0f);

	g_QglTable.m_pfn_qglBegin(GL_LINE_STRIP);

	for(int i = 0; i < nPathCount; i++)
		g_QglTable.m_pfn_qglVertex3fv(path[i]);

	g_QglTable.m_pfn_qglEnd();

	if(m_bShowExtra)
	{
		// +mars
		// ahhh -- a nice C&P job :)
		// for the bounding box stuff
		g_QglTable.m_pfn_qglColor4f(0.25f, 0.75f, 0.75f, 1.0f);

		g_QglTable.m_pfn_qglTranslatef( 16.0f, 16.0f, 28.0f );

		g_QglTable.m_pfn_qglBegin( GL_LINE_STRIP );
		
		int i;	
		for ( i = 0; i < nPathCount; i++ )
			g_QglTable.m_pfn_qglVertex3fv( path[i] );

		g_QglTable.m_pfn_qglEnd();
	
	// ---------------

		g_QglTable.m_pfn_qglTranslatef( -16.0f, -16.0f, -28.0f );	// back to where we were
		g_QglTable.m_pfn_qglTranslatef( -16.0f, 16.0f, 28.0f );		// move to new postion
	
		g_QglTable.m_pfn_qglBegin( GL_LINE_STRIP );
		
			for ( i = 0; i < nPathCount; i++ )
				g_QglTable.m_pfn_qglVertex3fv( path[i] );

		g_QglTable.m_pfn_qglEnd();

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

		g_QglTable.m_pfn_qglTranslatef( 16.0f, -16.0f, -28.0f );		// back to where we were
		g_QglTable.m_pfn_qglTranslatef( 16.0f, -16.0f, -28.0f );		// new pos

		g_QglTable.m_pfn_qglBegin( GL_LINE_STRIP );
		
			for ( i = 0; i < nPathCount; i++ )
				g_QglTable.m_pfn_qglVertex3fv( path[i] );

		g_QglTable.m_pfn_qglEnd();

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

		g_QglTable.m_pfn_qglTranslatef( -16.0f, 16.0f, 28.0f );		// back to where we were
		g_QglTable.m_pfn_qglTranslatef( -16.0f, -16.0f, -28.0f );		// new pos

		g_QglTable.m_pfn_qglBegin( GL_LINE_STRIP );
		
			for ( i = 0; i < nPathCount; i++ )
				g_QglTable.m_pfn_qglVertex3fv( path[i] );

		g_QglTable.m_pfn_qglEnd();
	}
	// -mars

	g_QglTable.m_pfn_qglPopAttrib();
}

void DBobView::Register()
{
	g_QglTable.m_pfnHookGL2DWindow( this );
	g_QglTable.m_pfnHookGL3DWindow( this );
	m_bHooked = TRUE;
}

void DBobView::UnRegister()
{
	g_QglTable.m_pfnUnHookGL2DWindow( this );
	g_QglTable.m_pfnUnHookGL3DWindow( this );
	m_bHooked = FALSE;
}

void DBobView::SetPath(vec3_t *pPath)
{
	if(path)
		delete[] path;

	path = pPath;
}

#define LOCAL_GRAVITY -800.0f

bool DBobView::CalculateTrajectory(vec3_t start, vec3_t apex, float multiplier, int points, float varGravity)
{
	if(apex[2] <= start[2])
	{
		SetPath(NULL);
		return FALSE;
	}
	// ----think q3a actually would allow these
	//scrub that, coz the plugin wont :]

	vec3_t dist, speed;
	VectorSubtract(apex, start, dist);

	vec_t speed_z = (float)sqrt(-2*LOCAL_GRAVITY*dist[2]);
	float flight_time = -speed_z/LOCAL_GRAVITY;


	VectorScale(dist, 1/flight_time, speed);
	speed[2] = speed_z;

//	Sys_Printf("Speed: (%.4f %.4f %.4f)\n", speed[0], speed[1], speed[2]);

	vec3_t* pPath = new vec3_t[points];

	float interval = multiplier*flight_time/points;
	for(int i = 0; i < points; i++)
	{
		float ltime = interval*i;

		VectorScale(speed, ltime, pPath[i]);
		VectorAdd(pPath[i], start, pPath[i]);

		// could do this all with vectors
		// vGrav = {0, 0, -800.0f}
		// VectorScale(vGrav, 0.5f*ltime*ltime, vAdd);
		// VectorScale(speed, ltime, pPath[i]);
		// _VectorAdd(pPath[i], start, pPath[i])
		// _VectorAdd(pPath[i], vAdd, pPath[i])

		pPath[i][2] = start[2] + (speed_z*ltime) + (varGravity*0.5f*ltime*ltime);
	}

	SetPath(pPath);
	return TRUE;
}

void DBobView::Begin(const char* trigger, const char *target, float multiplier, int points, float varGravity, bool bNoUpdate, bool bShowExtra)
{
	strcpy(entTrigger, trigger);
	strcpy(entTarget, target);

	fMultiplier = multiplier;
	fVarGravity = varGravity;
	nPathCount = points;
	m_bShowExtra = bShowExtra;

	Register();

	if(UpdatePath())
	{
		if(!bNoUpdate)
		{
			eyes = new DListener;
			eyes->parent = this;
			eyes->Register();
		}
	}
	else
	{
		Sys_ERROR("Initialization Failure in DBobView::Begin");
		delete this;
	}
}

bool DBobView::UpdatePath()
{
	vec3_t start, apex;

	if(GetEntityCentre(entTrigger, start))
	{
		if(GetEntityCentre(entTarget, apex))
		{
			CalculateTrajectory(start, apex, fMultiplier, nPathCount, fVarGravity);
			return TRUE;
		}
	}
	return FALSE;
}
