// ObjectRev.cpp: implementation of the CObjectRev class.
//
//////////////////////////////////////////////////////////////////////

#include "ObjectRev.h"

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

LINK_ENTITY_TO_CLASS ( object_rev, CObjectRev);

CObjectRev::CObjectRev()
{
	fromarrows = NULL;
}

void CObjectRev::AddArrow(CObjectRev* aptr, ArrowType atype, Contrib acontrib)
{
	CObjectRevArrow* na = new CObjectRevArrow;
	na->ptr = aptr;
	na->type = atype;
	na->contrib = acontrib;
	na->next = fromarrows;
	fromarrows = na;
}

void CObjectRev::Spawn()
{
	Precache( );
	char* thismodel;
	switch (type)
	{
	default:
	case EDIT: thismodel = "models/rev/w_edit.mdl"; break;
	case ADD: thismodel = "models/rev/w_add.mdl"; break;
	case DEL: thismodel = "models/rev/w_delete.mdl"; break;
	case INTEG: thismodel = "models/rev/w_integ.mdl"; break;
	case BRANCH: thismodel = "models/rev/w_add.mdl"; break;
	}

	from = NULL;

	SET_MODEL( ENT(pev), thismodel);
	
	UTIL_SetOrigin( pev, pev->origin );
	target = (Vector)pev->origin;  //by default, we're already where we want to be.

	UTIL_SetSize( pev, Vector(-2.5, -2.5, -2.5), Vector( 2.5, 2.5, 2.5) );  //collision box!

	pev->movetype = MOVETYPE_FLY;
	pev->solid = SOLID_BBOX; 

	SetThink( Think );
	SetTouch( Touch );

	pev->nextthink = gpGlobals->time + 0.5;
}

void CObjectRev::Precache()
{
	PRECACHE_MODEL( "models/rev/w_edit.mdl" );
	PRECACHE_MODEL( "models/rev/w_add.mdl" );
	PRECACHE_MODEL( "models/rev/w_delete.mdl" );
	PRECACHE_MODEL( "models/rev/w_integ.mdl" );
	BeamSprite = PRECACHE_MODEL( "sprites/smoke.spr" ); 
}

void CObjectRev :: Think( void )
{
/*
	if (!IsInWorld())
	{
		parent->disown( this );
		UTIL_Remove( this );
		return;
	}
	Commenting this out because there might be "from" records pointing to this.  Screw it.  Generating
	"object fell out of level" errors is better than segfaulting.  If it ever needs to be fixed, then it can
	be done by making the "from" relationship doubly-linked, but I don't have the energy right now.
*/  
	if (UTIL_VecUpdate( &(pev->origin), target )) {
		pev->nextthink = gpGlobals->time + 0.05;
		UTIL_SetSize( pev, Vector(-2.5, -2.5, -2.5), Vector( 2.5, 2.5, 2.5) );  //collision box!
	}
	else 
	{
		CObjectRevArrow* arrow = fromarrows;
		short int red, green, blue, bright, width;
		pev->nextthink = gpGlobals->time + 0.5;
		while (arrow != NULL && arrow->ptr->target == arrow->ptr->pev->origin)
		{
			switch(arrow->contrib)
			{
			case all:
				width = 6; bright = 255; break;
			case some:
				width = 4; bright = 200; break;
			case none:
				width = 2; bright = 128; break;
			}
			// edit, branch, copy, ignore, impure, merge
			switch(arrow->type)
			{
			case edit:
				red = 200; green = 200; blue = 200; break;
			case branch:
				red = 255; green = 255; blue = 0; break;
			case copy:
				red = 0; green = 255; blue = 0; break;
			case ignore:
				red = 150; green = 0; blue = 255; break;
			case impure:
				red = 255; green = 0; blue = 0; break;
			case merge:
				red = 0; green = 0; blue = 255; break;
			}
			MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, arrow->ptr->pev->origin);

			WRITE_BYTE( TE_BEAMENTPOINT ); 
			WRITE_SHORT( entindex() ); 
			WRITE_COORD( arrow->ptr->pev->origin.x ); 
			WRITE_COORD( arrow->ptr->pev->origin.y ); 
			WRITE_COORD( arrow->ptr->pev->origin.z ); 
			WRITE_SHORT( BeamSprite ); // Beam sprite index. 
			WRITE_BYTE( 0 ); // Starting frame 
			WRITE_BYTE( 0 ); // Framerate 
			WRITE_BYTE( 5 ); // How long the beam stays on. 
			WRITE_BYTE( width ); //width
			WRITE_BYTE( 0 ); // Noise 
			WRITE_BYTE( red ); //red
			WRITE_BYTE( green ); 
			WRITE_BYTE( blue ); 
			WRITE_BYTE( bright ); 
			WRITE_BYTE( 0 ); // Speed, sort of.

			MESSAGE_END( );

			arrow = arrow->next;
		}
	}
}

void CObjectRev :: Touch( CBaseEntity* Thing )
{ 
	CBasePlayer *pl;
	CBaseEntity *be = CBaseEntity::Instance(Thing->pev->owner);

	if (Thing->IsPlayer()) pl = (CBasePlayer*) Thing;
	else if (be->IsPlayer()) pl = (CBasePlayer*) be;
	else return;

	if (Thing->IsLong()) {
		Long( pl );
		pl->m_flNextP4Time = gpGlobals->time + 0.5;
		return;
	}

	if (Thing->IsExpand()) {
		Expand( pl );
		pl->m_flNextP4Time = gpGlobals->time + 0.5;
		return;
	}

	if (pl->m_flNextP4Time > gpGlobals->time) return;
	pl->m_flNextP4Time = gpGlobals->time + 0.2;
	UTIL_PrintHud(pl, filerev.Text());
}

void CObjectRev::Long( CBasePlayer* pl )
{
	ClientLuser ui;
	ClientApi client;
	if (P4PORT.Length()) client.SetPort(P4PORT.Text());
	Error e;
	StrBuf msg = StrBuf();

	client.Init( &e );
	if (e.GetSeverity()) {
		e.Fmt(&msg);
		UTIL_FatalHud(pl, msg.Text());
		pl->m_flNextP4Time = gpGlobals->time + 20.0; //20 second delay on connect error
		return;
	}
	
	char* args[1];
	args[0] = filerev.Text();

	client.SetArgv(1, args);
	client.Run( "filelog", &ui );

	client.Final( &e );
	if (e.GetSeverity()) {
		e.Fmt(&msg);
		UTIL_WarningHud(pl, msg.Text());
	}

	UTIL_PrintHud( pl, ui.message.Text());
}

void CObjectRev::Expand( CBasePlayer* pl )
{
	if (!istext)
	{
		UTIL_PrintHud(pl, "===non-text file===\n");
		return;
	}

	ClientLuser ui;
	ClientApi client;
	if (P4PORT.Length()) client.SetPort(P4PORT.Text());
	Error e;
	StrBuf msg = StrBuf();

	client.Init( &e );
	if (e.GetSeverity()) {
		e.Fmt(&msg);
		UTIL_FatalHud(pl, msg.Text());
		pl->m_flNextP4Time = gpGlobals->time + 20.0; //20 second delay on connect error
		return;
	}
	
	char* args[2];
	args[0] = "-q";
	args[1] = filerev.Text();

	client.SetArgv(2, args);
	client.Run( "print", &ui );

	client.Final( &e );
	if (e.GetSeverity()) {
		e.Fmt(&msg);
		UTIL_WarningHud(pl, msg.Text());
	}

	UTIL_PrintHud( pl, ui.message.Text());
}