/*

	g_botf_vehicle.c

	Author: AFZ
	Date: 6/01/02

	- This file contains the implementations for all of the vehicle specific code
*/

#include "g_vehicles.h"

#include "g_local.h"

/////////////////////////////////
// vehicle entity helper funcs //
/////////////////////////////////
void Vehicle_Touch (gentity_t *ent, gentity_t *other, trace_t *trace) {
   vec3_t   v;

   G_Printf("Vehicle: Touched.\n");

	// make sure whoever touched this vehicle is a client
   if (!other->client)
		return;

   /*
   // make sure this client has his WBUTTON_USE_VEHICLE button held down
   if ( other->client->ps.stats[STAT_VEHICLE] || !(other->client->buttons & WBUTTON_USE_VEHICLE) ) {
      return;
   }
   */

   // set the vehicleId to a stat for the client
   other->client->ps.stats[STAT_VEHICLE] = ent->s.otherEntityNum2;

   /*
   // get a unit vector in the direction of the velocity of the toucher
   VectorCopy(other->client->ps.velocity, v);
   VectorNormalize(v);

   // scale the vector to a speed of 10units/sec
   VectorScale(v, 10, v);

   // set the vel vector to the trDelta of the entity being touched...
   VectorCopy(v, ent->s.pos.trDelta);
   
   VectorCopy( ent->s.origin, ent->s.pos.trBase );
	VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );
   
   //VectorCopy(ent->s.pos.trBase, other->r.currentOrigin);
   //VectorCopy(ent->s.origin, other->r.currentOrigin);
   //VectorCopy(ent->r.currentOrigin, other->r.currentOrigin);
   ent->s.pos.trType = TR_LINEAR;
   //VectorAdd(ent->s.origin, v, ent->s.origin);
   */

   trap_LinkEntity(ent);
}

void Vehicle_Pain(gentity_t *self, gentity_t *attacker, int damage)
{
	G_Printf("Vehicle in pain...\n");
	// Play damaging sound...
	G_AddEvent(self, EV_GENERAL_SOUND, G_SoundIndex("sound/effects/bumpfield.wav"));
	return;
}

// monster dies
void Vehicle_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
	GibEntity( self, 0 );

	G_Printf("Vehicle died...");

   // remove the vehicle from the server list
   VehicleArray_RemoveVehicleForId(self->s.otherEntityNum2);
   // tell the client to remove the vehicle as well
   //...

	G_FreeEntity( self );
}

void Vehicle_Use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
	G_Printf("Vehicle used...");

	return;
}

void Vehicle_Think( gentity_t *ent ) {
	vec3_t R[3];
	//G_Printf("Vehicle thinking...\n");
	
	QuaternionToAxis(g_quats[ent->s.number], R);
	ent->s.pos.trType = TR_LINEAR;
	ent->s.pos.trTime = level.time;
	VectorCopy(ent->r.currentOrigin, ent->s.pos.trBase);
	VectorCopy(R[0], ent->s.pos.trDelta);
	G_Printf("dir: %s\n", vtos(R[0]));
	VectorNormalize(ent->s.pos.trDelta);
	VectorScale(ent->s.pos.trDelta, 10, ent->s.pos.trDelta);

	VectorCopy(ent->s.origin, ent->r.currentOrigin);
	
	//g_angles[ent->s.number][YAW]++;
	//g_angles[ent->s.number][PITCH]++;
	//g_angles[ent->s.number][ROLL]++;
	
	//G_SetAngles(ent, g_angles[ent->s.number]);

	//ent->r.contents = CONTENTS_BODY;
	//ent->clipmask = MASK_PLAYERSOLID;
	//ent->r.contents = CONTENTS_SOLID;
	//ent->clipmask = CONTENTS_SOLID;
	
	ent->nextthink = level.time + 1500;

	trap_LinkEntity(ent);
}

void G_PositionVehicle( gentity_t *ent) {
	if(ent && ent->s.eType == ET_VEHICLE) {
		vec3_t pos;
		
		BG_EvaluateTrajectory(&(ent->s.pos), level.time, pos);
		VectorCopy(ent->s.origin, ent->s.origin2);
		VectorCopy(pos, ent->r.currentOrigin);
		VectorCopy(pos, ent->s.origin);
	}
}

void G_OrientVehicle( gentity_t *ent ) {
	static int		delta;
	vec3_t			R[3], pos;

	vec3_t			currentAngles, previousAngles, deltaAngles;
	quat_t			q_previous, q, qtemp;
	
	QuaternionToApos(&(ent->s.apos), &(g_quats[ent->s.number]));

	delta += delta + 2;
	delta *= -1;

	// slerp JUST started
	if(level.time >= (ent->s.apos.trTime + ent->s.apos.trDuration)) {
		//ent->s.angles[PITCH] += delta;
		ent->s.angles[YAW] += delta;
		//ent->s.angles[ROLL] += delta;
		
		ent->s.apos.trDuration = 200;
		ent->s.apos.trTime = level.time;

		// lerp started
		ent->s.apos.trType = 1;

	// slerp DIDNT JUST start
	} else {
		// new lerp didnt just start
		ent->s.apos.trType = 0;
	}






	///////// ripped from cg_vehicles...
	//////////////////////
	/////////////////////
	// calculate the axis
	//VectorCopy( s1->angles, cent->lerpAngles);

	/*
	VectorCopy( s1->angles, cent->lerpAngles);
	VectorCopy( cent->lerpOrigin, cockpitEnt.origin);
	VectorCopy( cent->lerpOrigin, cockpitEnt.oldorigin);
	*/

	/*
	// set up current and previous angles
   if(cent->currentState.number == cg.predictedPlayerEntity.currentState.number) {
      VectorCopy(cg.refdefViewAngles, currentAngles);
   } else {
		VectorCopy(cent->currentState.angles, currentAngles);
		//QuaternionCopy(cg_quats[cent->currentState.number], &q_previous);
   }
	*/
	VectorCopy(ent->s.angles, currentAngles);
   VectorCopy(g_angles[ent->s.number], previousAngles);

	if(!QuaternionIsEmpty(&(g_quats[ent->s.number])))
		QuaternionCopy(g_quats[ent->s.number], &q_previous);
	else
		QuaternionIdentity(&q_previous);
	
	//Com_Printf("%d ", cent->currentState.number);
	
	// check to see if we're an NPV (non player vehicle)
	if( ((ent->s.number < 0) || (ent->s.number >= MAX_CLIENTS) ) && (ent->s.apos.trTime)){
		//Com_Printf("drawing an npv.\n");
		
		// slerp...
		// if cg.time > trtime + duration
		//  - copy new a and make new b
		//  - set a and b into cg_lerpquats
		//  - begin to lerp
		//  - copy the quat to q
		
		// did the slerp just restart?
		if(ent->s.apos.trType && !g_slerping[ent->s.number] ) {
			g_slerping[ent->s.number] = qtrue;

			//QuaternionFromApos(&(cent->currentState.apos), &((cg_lerpquats[s1->number])[0]));
			
			// copy new A
			QuaternionCopy(g_quats[ent->s.number], &(g_lerpquats[ent->s.number][0]));

         // get delta angles and convert to quat
			VectorSubtract(currentAngles, previousAngles, deltaAngles);
			QuaternionFromAngles(deltaAngles, &q);

         // concatenate quat with previous
			//IF (relative orientation) (or NOT absolute)
			QuaternionCopy(q, &qtemp);
			QuaternionMultiply(&(g_lerpquats[ent->s.number][0]), &qtemp, &(g_lerpquats[ent->s.number][1]));

		   // store old angles
			VectorCopy(currentAngles, g_angles[ent->s.number]);
		
		// if we're currently starting to do some slerping (only do this once (in previous case))
		} else if(ent->s.apos.trType && g_slerping[ent->s.number] ) {
			// dont do anything
		
		// if the server says we just finished starting our slerping
		} else if(!ent->s.apos.trType && g_slerping[ent->s.number] ) {
			g_slerping[ent->s.number] = qfalse;
		}

		// interpolate
		QuaternionIdentity(&q);
		QuaternionSlerp(g_lerpquats[ent->s.number][0], g_lerpquats[ent->s.number][1], ent->s.apos.trTime, ent->s.apos.trDuration, level.time, &q);

		//Com_Printf("q: %f,%f,%f,%f\n", q.x, q.y, q.z, q.w);
		//Com_Printf("lerped with %f\n", (float)(cg.time - s1->apos.trTime)/(float)s1->apos.trDuration);
	
	// if we're a player controlled vehicle
	} else {
		
		// get the previous orientation
		QuaternionCopy(g_quats[ent->s.number], &q_previous);
		
		if(QuaternionIsEmpty(&q_previous)){
			Com_Printf("quat prev is empty\n");
			QuaternionIdentity(&q_previous);
		}

		// get delta angles and convert to a quat
		VectorSubtract(currentAngles, previousAngles, deltaAngles);
		QuaternionFromAngles(deltaAngles, &q);

		// concatenate with previous
		// IF (relative orientation) (NOT absolute orientation)
		QuaternionCopy(q, &qtemp);
		QuaternionMultiply(&qtemp, &q_previous, &q);	
	}

	// normalize our orientation
	QuaternionNormalize(&q);

   // generate rotation matrix
   //QuaternionToAxis(q, R);
	QuaternionCopy(q, &(g_quats[ent->s.number]));
}

void G_RunVehicle( gentity_t *ent ) {
	G_OrientVehicle(ent);
	G_PositionVehicle(ent);	

	G_RunThink(ent);
}

int G_SpawnVehicle(vehicle_t *v, gentity_t *e, gentity_t *owner) {
   vec3_t	dir;
   vec3_t maxs, mins;
   int i;

	// spawn klonky
	e = G_Spawn();

	// tell klonky who his master is...
	if(owner) {
		/*
		e->parent = owner;
		e->r.ownerNum = owner->s.number;
		*/
		e->parent = e;
		e->r.ownerNum = e->s.number;
	}
	
	VectorCopy(owner->r.currentOrigin, e->s.origin);
	VectorCopy(e->s.origin, e->r.currentOrigin);

	// set its initial position and zero out 2 and 3
	
	//VectorCopy(owner->s.angles, e->s.angles);
	//AngleVectors(owner->s.angles, dir, NULL, NULL);
	
	//VectorSet(e->pos2, 0, 0, 0);
	//VectorSet(e->pos3, 0, 0, 0);
	
	// set vehicles bounding box
   
   //e->r.contents = CONTENTS_TRIGGER|CONTENTS_BODY|CONTENTS_SOLID;
	//e->r.contents = CONTENTS_BODY;
	//e->r.contents = CONTENTS_SOLID;
	//e->r.contents = CONTENTS_BODY;
	//e->clipmask = MASK_SHOT;

	//e->r.contents = CONTENTS_SOLID|CONTENTS_BODY;
	e->r.contents = MASK_SHOT|CONTENTS_TRIGGER;
	e->clipmask = MASK_SHOT|CONTENTS_TRIGGER;

	//e->use = Vehicle_Use;

	//e->speed = 0;

	//e->physicsObject = qtrue;

	/*
	e->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
	e->r.contents = CONTENTS_SOLID;
	VectorCopy(e->s.origin, e->s.pos.trBase);
	e->s.pos.trType = TR_STATIONARY;
	*/

   /*
   VectorSet(maxs, g_vehicleTemplates[v->templateNum].vehicleInfo.physicsInfo.width / 2, g_vehicleTemplates[v->templateNum].vehicleInfo.physicsInfo.width / 2, g_vehicleTemplates[v->templateNum].vehicleInfo.physicsInfo.height / 2);
   VectorSet(mins, -1 * g_vehicleTemplates[v->templateNum].vehicleInfo.physicsInfo.width / 2, -1 * g_vehicleTemplates[v->templateNum].vehicleInfo.physicsInfo.width / 2, -1 * g_vehicleTemplates[v->templateNum].vehicleInfo.physicsInfo.height / 2);
   */
   
	/*
	// set the default bounding size
	VectorSet(maxs, 512, 512, 512);
   VectorSet(mins, -512, -512, -512);

	// scale the bounding boxes
	VectorScale(maxs, g_vehicleTemplates[v->templateNum].vehicleInfo.displayInfo.scale, maxs);
	VectorScale(mins, g_vehicleTemplates[v->templateNum].vehicleInfo.displayInfo.scale, mins);
	*/

	// test values
	VectorSet(maxs, 10, 10, 10);
   VectorSet(mins, -10, -10, -10);

   // set the bounding mins and maxes to the entity
	VectorCopy(mins, e->r.mins);
	VectorCopy(maxs, e->r.maxs);
	
	//VectorCopy(mins, e->r.absmin);
	//VectorCopy(maxs, e->r.absmax);

	// give vehicle some attributes
	e->r.svFlags = SVF_USE_CURRENT_ORIGIN/* | SVF_BROADCAST*/;
	e->s.eType = ET_VEHICLE;
	e->classname = "Vehicle";

	//e->s.groundEntityNum = ENTITYNUM_NONE;

	e->think = Vehicle_Think;
	e->nextthink = level.time + 1500;
   e->touch = Vehicle_Touch;

	e->s.solid = 2;
		
   // setup mortality
	e->takedamage = qtrue;
   e->health = g_vehicleTemplates[v->templateNum].vehicleInfo.vehicleStats.health;
	e->pain = Vehicle_Pain;
	e->die = Vehicle_Die;

	e->s.modelGhoul2 = 1;
	e->s.g2radius = 20;
	//e->s.modelindex = G_ModelIndex("models/ships/ewing/ewing.glm");
	
	// ghoul2 stuff
	//trap_G2API_InitGhoul2Model(&(e->s), "models/ships/ewing/ewing.glm", e->s.modelindex, 0, 0, 0, 0);
	e->s.frame = 0;
	//trap_G2API_SetBoneAnim(e->ghoul2, 0, "model_root", 0, 12, BONE_ANIM_OVERRIDE_LOOP, 1.0f, level.time, -1, -1);

	//e->inuse = qtrue;

	//e->r.bmodel = qfalse;

	//G_SetOrigin (e, e->r.currentOrigin);
	G_SetOrigin (e, e->s.origin);

	//G_SetAngles (e, e->s.angles);
	//G_SetOrientation(e, owner);

	g_vehicles[v->vehicleId].entityId = e->s.number;
   
   // set vars to be used for asset lookup in cg_vehicle
   e->s.otherEntityNum = v->templateNum;
   e->s.otherEntityNum2 = v->vehicleId;

	// throw vehicle into the mix
	trap_LinkEntity (e);

   Com_Printf("templateNum: %d", e->s.otherEntityNum); 

   for(i = 0; i < MAX_GENTITIES; i++) {
      if(g_entities[i].client) {
         trap_SendServerCommand( i, va("vehicleSpawnCG %d %d %d\n", g_vehicles[v->vehicleId].templateNum, g_vehicles[v->vehicleId].vehicleId, g_vehicles[v->vehicleId].entityId) );
      }
   }

   G_Printf("GAME: Spawned vehicle: %s at index %d with entityNum %d\n", g_vehicles[v->vehicleId].vehicleInfo.vehicleName, g_vehicles[v->vehicleId].vehicleId, g_vehicles[v->vehicleId].entityId);
}

////////////////////////////////////
// vehicle array helper functions //
////////////////////////////////////
int VehicleArray_InitVehicleArray() {
   int i;
   for(i = 0; i < MAX_GENTITIES; i++) {
      Vehicle_InitVehicle(&(g_vehicles[i]));
   }

   return 1;
}

vehicle_t *VehicleArray_GetVehicleForId(int vehicleId) {
   int i;
   if(vehicleId >= 0) {
      for(i = 0; i < MAX_GENTITIES; i++) {
         if(g_vehicles[i].vehicleId == vehicleId)
            return &(g_vehicles[i]);
      }
   }
   return (vehicle_t*)0;
}

int VehicleArray_SetVehicleForId(int vehicleId, vehicle_t *vehicle) {
   int i;
   if(vehicle && vehicleId) {
      for(i = 0; i < MAX_GENTITIES; i++) {
         if(g_vehicles[i].vehicleId == vehicleId) {
            Vehicle_CopyVehicle(&(g_vehicles[i]), vehicle);
            return 1;
         }
      }
   }
   return 0;
}


int VehicleArray_AddVehicle(vehicle_t *vehicle) {
   int i;
   if(vehicle) {
      for(i = 0; i < MAX_GENTITIES; i++) {
         if(g_vehicles[i].vehicleId == -1) {
            vehicle->vehicleId = i;
            return Vehicle_CopyVehicle(&(g_vehicles[i]), vehicle);
         }
      }
   }
   return -1;
}

int VehicleArray_RemoveVehicleForId(int vehicleId) {
   int i;
   if(vehicleId) {
      for(i = 0; i < MAX_GENTITIES; i++) {
         if(g_vehicles[i].vehicleId == vehicleId) {
            return Vehicle_InitVehicle(&(g_vehicles[i]));
         }
      }
   }
   return 0;
}

int VehicleArray_OutputVehicles(int clientNum) {
   int i, j, k;
   char buffer[64];

   parser_clear_string(buffer, 64);

   trap_SendServerCommand( clientNum, "print \"Vehicles:\n\n\"" );

   for(i = 0; i < MAX_GENTITIES; i++) {
      if(g_vehicles[i].vehicleId != -1) {
         trap_SendServerCommand( clientNum, va("print \"  Vehicle: %d\n\"", i) );
         trap_SendServerCommand( clientNum, va("print \"    Name:    %s\n", g_vehicles[i].vehicleInfo.vehicleName) );
         /*
         trap_SendServerCommand( clientNum, va("print \"    Type:    %s\n\"", VehicleInfo_GetNameForVehicleType(g_vehicleTemplates[i].vehicleInfo.vehicleType) ) );
         trap_SendServerCommand( clientNum, va("print \"    Weapons: \n\""));
         trap_SendServerCommand( clientNum, va("print \"             %s\n\"", ) );
         */

      }
   }
}

//////////////////////////////
// vehicle template helpers //
//////////////////////////////

int VehicleTemplateArray_InitVehicleTemplateArray() {
   int i;
   for(i = 0; i < MAX_GENTITIES; i++) {
      Vehicle_InitVehicle(&(g_vehicleTemplates[i]));
   }

   return 1;
}

int VehicleTemplateArray_OutputTemplates(int clientNum) {
   int i, j, k;
   char buffer[64];

   parser_clear_string(buffer, 64);

   trap_SendServerCommand( clientNum, "print \"Vehicle Templates:\n\n\"" );
   trap_SendServerCommand( clientNum, "print \"  Vehicle:\n\"" );

   for(i = 0; i < MAX_GENTITIES; i++) {
      if(g_vehicleTemplates[i].templateNum != -1) {
         trap_SendServerCommand( clientNum, va("print \"    Name:    %s\n\\n\"", g_vehicleTemplates[i].vehicleInfo.vehicleName) );
         /*
         trap_SendServerCommand( clientNum, va("print \"    Type:    %s\n\"", VehicleInfo_GetNameForVehicleType(g_vehicleTemplates[i].vehicleInfo.vehicleType) ) );
         trap_SendServerCommand( clientNum, va("print \"    Weapons: \n\""));
         trap_SendServerCommand( clientNum, va("print \"             %s\n\"", ) );
         */
      }
   }
}

int VehicleTemplateArray_GetTemplateIndexForName(char *name) {
   int i;
   for(i = 0; i < MAX_GENTITIES; i++) {
      if(Q_stricmp(g_vehicleTemplates[i].vehicleInfo.vehicleName, name) == 0)
         return i;
   }

   return -1;
}