#include "p4objects.h"

LINK_ENTITY_TO_CLASS( object_info, CObjectInfo)

void CObjectInfo :: Spawn( ) 
{ 
	Precache( );
			
	SET_MODEL( ENT(pev), STRING(pev->model) );

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

	UTIL_SetSize( pev, Vector(-30, -30, 38), Vector(30, 30, 90) );  //collision box!

	level = 0;

	pev->movetype = MOVETYPE_FLY;
/* This is the animating code. */
	pev->sequence = LookupActivity (ACT_IDLE); //Use the ACT_IDLE sequence.
	pev->frame = 0; //Start on frame 0.
	pev->framerate = 1; //And set a framerate of 1.
/*                             */
	pev->solid = SOLID_BBOX; 

	SetThink( Think );
	SetTouch( Touch );

	pev->nextthink = gpGlobals->time + 0.8;
	nextRefreshTime = gpGlobals->time + 30;
} 

/* The object_info is the only one that's in the world when the map starts up, so we need
 * to precache everything, since the object_info will ultimately be spawning them later. */
void CObjectInfo :: Precache( ) 
	{ 
		/* Precache all the models, starting with this one. */
		PRECACHE_MODEL( "models/info/w_info.mdl" );
		PRECACHE_MODEL( "models/depot/w_depot.mdl" );
		PRECACHE_MODEL( "models/dir/w_dir.mdl" );
		PRECACHE_MODEL( "models/file/w_file.mdl");
		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" );
		/* Before I put this next bit of code in, the program would lag for three
		 * seconds the first time I accessed Perforce.  Now I have it in the precache 
		 * function so it's not as noticeable - still not sure exactly why it's slow
		 * the first time, though. */
		ClientLuser ui;
		ClientApi client;
		Error e;
		client.Init( &e );
		client.Run( "info", &ui );
		client.Final( &e );
} 

		void CObjectInfo :: Think( void )
		{
			if (!IsInWorld())
			{
				UTIL_Remove( this );
				return;
			}

			if (nextRefreshTime < gpGlobals->time) 
			{
				ClientLuser ui;
				ClientApi client;
				Error e;
				client.Init( &e );
				client.Run( "info", &ui );
				client.Final( &e );
				nextRefreshTime += 30;
			}

			if (UTIL_VecUpdate( &(pev->origin), target )) 
			{
				pev->nextthink = gpGlobals->time + 0.05;
				UTIL_SetSize( pev, Vector(-30, -30, 38), Vector(30, 30, 90) );  //collision box!
			}
			else pev->nextthink = gpGlobals->time + 0.5;
		}

        void CObjectInfo :: Touch( CBaseEntity* Player ) 
		{

			CBasePlayer *pl;
			CBaseEntity *be = CBaseEntity::Instance(Player->pev->owner);

			if (Player->IsPlayer()) pl = (CBasePlayer*) Player;
			else if (be->IsPlayer()) pl = (CBasePlayer*) be;
			else return;
			if (pl->m_flNextP4Time > gpGlobals->time) return;

			if (Player->IsExpand()) {
				Expand( pl );
				return;
			}

			//after this point we just spit "p4 info" to player "pl".
			
			
			//run the command "p4 info"			
			ClientLuser ui;
			ClientApi client;
			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;
			}

			client.Run( "info", &ui );

			client.Final( &e );
			if (e.GetSeverity()) {
				e.Fmt(&msg);
				UTIL_WarningHud(pl, msg.Text());
			}
			// Print contents of the message buffer
			UTIL_PrintHud( pl, ui.message.Text());
			pl->m_flNextP4Time = gpGlobals->time + 0.5; //0.5 second delay
		}

		void CObjectInfo :: Expand( CBasePlayer *pl)
		{
			if (level == 1) return;
			UTIL_PrintHud( pl, "Expanding root node...\n");

			killKids(this);

			newLevel(1);

			ClientDepotUser ui;
			ui.specflag = FALSE;
			ClientApi client;
			Error e;

			client.Init( &e );
			if (e.GetSeverity()) {
				StrBuf msg = StrBuf();
				e.Fmt(&msg);
				UTIL_FatalHud(pl, msg.Text());
				pl->m_flNextP4Time = gpGlobals->time + 20.0;
				return;
			}

			client.Run( "depots", &ui );

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

			StrBuf depot = ui.depots.SPop();
			float ycoord = 0.0;

			CObjectDepot *newdepot;

			while (depot.Length() > 0)
			{
					newdepot = GetClassPtr( (CObjectDepot *)NULL );
					depots.Append(newdepot);
					newdepot->pev->classname = MAKE_STRING("object_depot");
					newdepot->Spawn();
					newdepot->pev->origin = pev->origin;
					newdepot->target = target - Vector(0,0,100);
					newdepot->target.y -= ycoord*100;
					newdepot->path = depot;
					newdepot->parent = this;
					newdepot->pev->nextthink = gpGlobals->time;
					depot = ui.depots.SPop();
					ycoord++;
			}
			return;
		}

	void CObjectInfo :: newLevel( short newlev)
	{
		target.z += (newlev - level)*100;
		level = newlev;
	}

	void CObjectInfo :: disown( CBaseEntity *child)
	{
		depots.EKill(child);
	}

	void CObjectInfo :: killKids( CBaseEntity *Caller )
	{
		BOOL callerFlag = FALSE;
		CBaseEntity* kid = depots.EPop();
		while (kid != NULL)
		{
			if (kid == Caller) 
			{
				callerFlag = TRUE;
				kid = depots.EPop();
				continue;
			}
			kid -> killKids(this);
			UTIL_Remove(kid);
			kid = depots.EPop();
		}
		if (callerFlag) depots.Append(Caller);
	}
