#include <stdio.h>
#include <string.h>

#include "vcollide_parse_private.h"
#include "physics_material.h"

#include "vstdlib/strtools.h" 
#include "vphysics/constraints.h"
#include "vphysics/vehicles.h"
#include "filesystem_helpers.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

static void ReadVector( const char *pString, Vector& out )
{
	float x, y, z;
	sscanf( pString, "%f %f %f", &x, &y, &z );
	out[0] = x;
	out[1] = y;
	out[2] = z;
}

static void ReadAngles( const char *pString, QAngle& out )
{
	float x, y, z;
	sscanf( pString, "%f %f %f", &x, &y, &z );
	out[0] = x;
	out[1] = y;
	out[2] = z;
}

static void ReadVector4D( const char *pString, Vector4D& out )
{
	float x, y, z, w;
	sscanf( pString, "%f %f %f %f", &x, &y, &z, &w );
	out[0] = x;
	out[1] = y;
	out[2] = z;
	out[3] = w;
}


class CVPhysicsParse : public IVPhysicsKeyParser
{
public:
				~CVPhysicsParse() {}

				CVPhysicsParse( const char *pKeyData );
	void		NextBlock( void );

	const char *GetCurrentBlockName( void );
	bool		Finished( void );
	void		ParseSolid( solid_t *pSolid, IVPhysicsKeyHandler *unknownKeyHandler );
	void		ParseFluid( fluid_t *pFluid, IVPhysicsKeyHandler *unknownKeyHandler );
	void		ParseRagdollConstraint( constraint_ragdollparams_t *pConstraint, IVPhysicsKeyHandler *unknownKeyHandler );
	void		ParseSurfaceTable( int *table, IVPhysicsKeyHandler *unknownKeyHandler );
	void		ParseVehicle( vehicleparams_t *pVehicle, IVPhysicsKeyHandler *unknownKeyHandler );
	void		ParseCustom( void *pCustom, IVPhysicsKeyHandler *unknownKeyHandler );
	void		SkipBlock( void ) { ParseCustom(NULL, NULL); }

private:
	void		ParseVehicleAxle( vehicle_axleparams_t &axle );
	void		ParseVehicleWheel( vehicle_wheelparams_t &wheel );
	void		ParseVehicleSuspension( vehicle_suspensionparams_t &suspension );
	void		ParseVehicleBody( vehicle_bodyparams_t &body );
	void		ParseVehicleEngine( vehicle_engineparams_t &engine );
	void		ParseVehicleEngineBoost( vehicle_engineparams_t &engine );
	void		ParseVehicleSteering( vehicle_steeringparams_t &steering );

	const char *m_pText;
	char m_blockName[MAX_KEYVALUE];
};




CVPhysicsParse::CVPhysicsParse( const char *pKeyData )
{
	m_pText = pKeyData;
	NextBlock();
}

void CVPhysicsParse::NextBlock( void )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];
	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( !Q_strcmp(value, "{") )
		{
			Q_strcpy( m_blockName, key );
			return;
		}
	}
}


const char *CVPhysicsParse::GetCurrentBlockName( void )
{
	if ( m_pText )
		return m_blockName;

	return NULL;
}


bool CVPhysicsParse::Finished( void )
{
	if ( m_pText )
		return false;

	return true;
}


void CVPhysicsParse::ParseSolid( solid_t *pSolid, IVPhysicsKeyHandler *unknownKeyHandler )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];
	key[0] = 0;

	if ( unknownKeyHandler )
	{
		unknownKeyHandler->SetDefaults( pSolid );
	}
	else
	{
		memset( pSolid, 0, sizeof(*pSolid) );
	}
	
	// disable these until the ragdoll is created
	pSolid->params.enableCollisions = false;

	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( key[0] == '}' )
		{
			NextBlock();
			return;
		}

		if ( !Q_stricmp( key, "index" ) )
		{
			pSolid->index = atoi(value);
		}
		else if ( !Q_stricmp( key, "name" ) )
		{
			Q_strncpy( pSolid->name, value, sizeof(pSolid->name) );
		}
		else if ( !Q_stricmp( key, "parent" ) )
		{
			Q_strncpy( pSolid->parent, value, sizeof(pSolid->parent) );
		}
		else if ( !Q_stricmp( key, "surfaceprop" ) )
		{
			Q_strncpy( pSolid->surfaceprop, value, sizeof(pSolid->surfaceprop) );
		}
		else if ( !Q_stricmp( key, "mass" ) )
		{
			pSolid->params.mass = atof(value);
		}
		else if ( !Q_stricmp( key, "massCenterOverride" ) )
		{
			ReadVector( value, pSolid->massCenterOverride );
			pSolid->params.massCenterOverride = &pSolid->massCenterOverride;
		}
		else if ( !Q_stricmp( key, "inertia" ) )
		{
			pSolid->params.inertia = atof(value);
		}
		else if ( !Q_stricmp( key, "damping" ) )
		{
			pSolid->params.damping = atof(value);
		}
		else if ( !Q_stricmp( key, "rotdamping" ) )
		{
			pSolid->params.rotdamping = atof(value);
		}
		else if ( !Q_stricmp( key, "volume" ) )
		{
			pSolid->params.volume = atof(value);
		}
		else if ( !Q_stricmp( key, "drag" ) )
		{
			pSolid->params.dragCoefficient = atof(value);
		}
		else if ( !Q_stricmp( key, "rollingdrag" ) )
		{
			pSolid->params.rollingDrag = atof(value);
		}
		else
		{
			if ( unknownKeyHandler )
			{
				unknownKeyHandler->ParseKeyValue( pSolid, key, value );
			}
		}
	}
}

void CVPhysicsParse::ParseRagdollConstraint( constraint_ragdollparams_t *pConstraint, IVPhysicsKeyHandler *unknownKeyHandler )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];

	key[0] = 0;
	if ( unknownKeyHandler )
	{
		unknownKeyHandler->SetDefaults( pConstraint );
	}
	else
	{
		memset( pConstraint, 0, sizeof(*pConstraint) );
		pConstraint->childIndex = -1;
		pConstraint->parentIndex = -1;
	}
	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( key[0] == '}' )
		{
			NextBlock();
			return;
		}

		if ( !Q_stricmp( key, "parent" ) )
		{
			pConstraint->parentIndex = atoi( value );
		}
		else if ( !Q_stricmp( key, "child" ) )
		{
			pConstraint->childIndex = atoi( value );
		}
		else if ( !Q_stricmp( key, "xmin" ) )
		{
			pConstraint->axes[0].minRotation = atof( value );
		}
		else if ( !Q_stricmp( key, "xmax" ) )
		{
			pConstraint->axes[0].maxRotation = atof( value );
		}
		else if ( !Q_stricmp( key, "xfriction" ) )
		{
			pConstraint->axes[0].angularVelocity = 0;
			pConstraint->axes[0].torque = atof( value );
		}
		else if ( !Q_stricmp( key, "ymin" ) )
		{
			pConstraint->axes[1].minRotation = atof( value );
		}
		else if ( !Q_stricmp( key, "ymax" ) )
		{
			pConstraint->axes[1].maxRotation = atof( value );
		}
		else if ( !Q_stricmp( key, "yfriction" ) )
		{
			pConstraint->axes[1].angularVelocity = 0;
			pConstraint->axes[1].torque = atof( value );
		}
		else if ( !Q_stricmp( key, "zmin" ) )
		{
			pConstraint->axes[2].minRotation = atof( value );
		}
		else if ( !Q_stricmp( key, "zmax" ) )
		{
			pConstraint->axes[2].maxRotation = atof( value );
		}
		else if ( !Q_stricmp( key, "zfriction" ) )
		{
			pConstraint->axes[2].angularVelocity = 0;
			pConstraint->axes[2].torque = atof( value );
		}
		else
		{
			if ( unknownKeyHandler )
			{
				unknownKeyHandler->ParseKeyValue( pConstraint, key, value );
			}
		}
	}
}


void CVPhysicsParse::ParseFluid( fluid_t *pFluid, IVPhysicsKeyHandler *unknownKeyHandler )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];

	key[0] = 0;
	pFluid->index = -1;
	if ( unknownKeyHandler )
	{
		unknownKeyHandler->SetDefaults( pFluid );
	}
	else
	{
		memset( pFluid, 0, sizeof(*pFluid) );
	}

	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( key[0] == '}' )
		{
			NextBlock();
			return;
		}

		if ( !Q_stricmp( key, "index" ) )
		{
			pFluid->index = atoi( value );
		}
		else if ( !Q_stricmp( key, "density" ) )
		{
			pFluid->params.density = atof( value );
		}
		else if ( !Q_stricmp( key, "damping" ) )
		{
			pFluid->params.damping = atof( value );
		}
		else if ( !Q_stricmp( key, "surfaceplane" ) )
		{
			ReadVector4D( value, pFluid->params.surfacePlane );
		}
		else if ( !Q_stricmp( key, "currentvelocity" ) )
		{
			ReadVector( value, pFluid->params.currentVelocity );
		}
		else
		{
			if ( unknownKeyHandler )
			{
				unknownKeyHandler->ParseKeyValue( pFluid, key, value );
			}
		}
	}
}

void CVPhysicsParse::ParseSurfaceTable( int *table, IVPhysicsKeyHandler *unknownKeyHandler )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];

	key[0] = 0;
	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( key[0] == '}' )
		{
			NextBlock();
			return;
		}

		int propIndex = physprops->GetSurfaceIndex( key );
		int tableIndex = atoi(value);
		if ( tableIndex < 128 )
		{
			table[tableIndex] = propIndex;
		}
	}
}

void CVPhysicsParse::ParseVehicleAxle( vehicle_axleparams_t &axle )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];

	memset( &axle, 0, sizeof(axle) );
	key[0] = 0;
	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( key[0] == '}' )
			return;

		// parse subchunks
		if ( value[0] == '{' )
		{
			if ( !Q_stricmp( key, "wheel" ) )
			{
				ParseVehicleWheel( axle.wheels );
			}
			else if ( !Q_stricmp( key, "suspension" ) )
			{
				ParseVehicleSuspension( axle.suspension );
			}
			else
			{
				SkipBlock();
			}
		}
		else if ( !Q_stricmp( key, "offset" ) )
		{
			ReadVector( value, axle.offset );
		}
		else if ( !Q_stricmp( key, "wheeloffset" ) )
		{
			ReadVector( value, axle.wheelOffset );
		}
		else if ( !Q_stricmp( key, "torquefactor" ) )
		{
			axle.torqueFactor = atof( value );
		}
		else if ( !Q_stricmp( key, "brakefactor" ) )
		{
			axle.brakeFactor = atof( value );
		}
	}
}

void CVPhysicsParse::ParseVehicleWheel( vehicle_wheelparams_t &wheel )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];

	key[0] = 0;
	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( key[0] == '}' )
			return;
		
		if ( !Q_stricmp( key, "radius" ) )
		{
			wheel.radius = atof( value );
		}
		else if ( !Q_stricmp( key, "mass" ) )
		{
			wheel.mass = atof( value );
		}
		else if ( !Q_stricmp( key, "inertia" ) )
		{
			wheel.inertia = atof( value );
		}
		else if ( !Q_stricmp( key, "damping" ) )
		{
			wheel.damping = atof( value );
		}
		else if ( !Q_stricmp( key, "rotdamping" ) )
		{
			wheel.rotdamping = atof( value );
		}
		else if ( !Q_stricmp( key, "frictionscale" ) )
		{
			wheel.frictionScale = atof( value );
		}
		else if ( !Q_stricmp( key, "material" ) )
		{
			wheel.materialIndex = physprops->GetSurfaceIndex( value );
		}
		else if ( !Q_stricmp( key, "skidmaterial" ) )
		{
			wheel.skidMaterialIndex = physprops->GetSurfaceIndex( value );
		}

	}
}


void CVPhysicsParse::ParseVehicleSuspension( vehicle_suspensionparams_t &suspension )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];

	key[0] = 0;
	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( key[0] == '}' )
			return;
		
		if ( !Q_stricmp( key, "springconstant" ) )
		{
			suspension.springConstant = atof( value );
		}
		else if ( !Q_stricmp( key, "springdamping" ) )
		{
			suspension.springDamping = atof( value );
		}
		else if ( !Q_stricmp( key, "stabilizerconstant" ) )
		{
			suspension.stabilizerConstant = atof( value );
		}
		else if ( !Q_stricmp( key, "springdampingcompression" ) )
		{
			suspension.springDampingCompression = atof( value );
		}
		else if ( !Q_stricmp( key, "maxbodyforce" ) )
		{
			suspension.maxBodyForce = atof( value );
		}
	}
}


void CVPhysicsParse::ParseVehicleBody( vehicle_bodyparams_t &body )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];

	key[0] = 0;
	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( key[0] == '}' )
			return;
		
		if ( !Q_stricmp( key, "massCenterOverride" ) )
		{
			ReadVector( value, body.massCenterOverride );
		}
		else if ( !Q_stricmp( key, "addgravity" ) )
		{
			body.addGravity = atof( value );
		}
		else if ( !Q_stricmp( key, "massOverride" ) )
		{
			body.massOverride = atof( value );
		}
		else if ( !Q_stricmp( key, "tiltforce" ) )
		{
			body.tiltForce = atof( value );
		}
		else if ( !Q_stricmp( key, "tiltforceheight" ) )
		{
			body.tiltForceHeight = atof( value );
		}
		else if ( !Q_stricmp( key, "countertorquefactor" ) )
		{
			body.counterTorqueFactor = atof( value );
		}
		else if ( !Q_stricmp( key, "keepuprighttorque" ) )
		{
			body.keepUprightTorque = atof( value );
		}
	}
}


void CVPhysicsParse::ParseVehicleEngineBoost( vehicle_engineparams_t &engine )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];

	key[0] = 0;
	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( key[0] == '}' )
			return;
		// parse subchunks
		if ( !Q_stricmp( key, "force" ) )
		{
			engine.boostForce = atof( value );
		}
		else if ( !Q_stricmp( key, "duration" ) )
		{
			engine.boostDuration = atof( value );
		}
		else if ( !Q_stricmp( key, "delay" ) )
		{
			engine.boostDelay = atof( value );
		}
		else if ( !Q_stricmp( key, "maxspeed" ) )
		{
			engine.boostMaxSpeed = atof( value );
		}
		else if ( !Q_stricmp( key, "torqueboost" ) )
		{
			engine.torqueBoost = atoi( value ) != 0 ? true : false;
		}

	}
}

void CVPhysicsParse::ParseVehicleEngine( vehicle_engineparams_t &engine )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];

	key[0] = 0;
	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( key[0] == '}' )
			return;
		// parse subchunks
		if ( value[0] == '{' )
		{
			if ( !Q_stricmp( key, "boost" ) )
			{
				ParseVehicleEngineBoost( engine );
			}
			else
			{
				SkipBlock();
			}
		}
		else if ( !Q_stricmp( key, "gear" ) )
		{
			engine.gearRatio[engine.gearCount] = atof( value );
			engine.gearCount++;
		}
		else if ( !Q_stricmp( key, "horsepower" ) )
		{
			engine.horsepower = atof( value );
		}
		else if ( !Q_stricmp( key, "maxSpeed" ) )
		{
			engine.maxSpeed = atof( value );
		}
		else if ( !Q_stricmp( key, "maxReverseSpeed" ) )
		{
			engine.maxRevSpeed = atof( value );
		}
		else if ( !Q_stricmp( key, "axleratio" ) )
		{
			engine.axleRatio = atof( value );
		}
		else if ( !Q_stricmp( key, "maxRPM" ) )
		{
			engine.maxRPM = atof( value );
		}
		else if ( !Q_stricmp( key, "throttleTime" ) )
		{
			engine.throttleTime = atof( value );
		}
		else if ( !Q_stricmp( key, "AutoTransmission" ) )
		{
			engine.isAutoTransmission = atoi( value ) != 0 ? true : false;
		}
		else if ( !Q_stricmp( key, "shiftUpRPM" ) )
		{
			engine.shiftUpRPM = atof( value );
		}
		else if ( !Q_stricmp( key, "shiftDownRPM" ) )
		{
			engine.shiftDownRPM = atof( value );
		}
	}
}


void CVPhysicsParse::ParseVehicleSteering( vehicle_steeringparams_t &steering )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];

	key[0] = 0;
	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( key[0] == '}' )
			return;
		// parse subchunks
		if ( !Q_stricmp( key, "degrees" ) )
		{
			steering.degrees = atof( value );
		}
		else if ( !Q_stricmp( key, "fastcarspeed" ) )
		{
			steering.speedFast = atof( value );
		}
		else if ( !Q_stricmp( key, "slowcarspeed" ) )
		{
			steering.speedSlow = atof( value );
		}
		else if ( !Q_stricmp( key, "slowsteeringrate" ) )
		{
			steering.steeringRateSlow = atof( value );
		}
		else if ( !Q_stricmp( key, "faststeeringrate" ) )
		{
			steering.steeringRateFast = atof( value );
		}
		else if ( !Q_stricmp( key, "steeringRestFactor" ) )
		{
			steering.steeringRestFactor = atof( value );
		}
		else if ( !Q_stricmp( key, "skidallowed" ) )
		{
			steering.isSkidAllowed = atoi( value ) != 0 ? true : false;
		}
		else if ( !Q_stricmp( key, "dustcloud" ) )
		{
			steering.dustCloud = atoi( value ) != 0 ? true : false;
		}
	}
}

void CVPhysicsParse::ParseVehicle( vehicleparams_t *pVehicle, IVPhysicsKeyHandler *unknownKeyHandler )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];

	key[0] = 0;
	if ( unknownKeyHandler )
	{
		unknownKeyHandler->SetDefaults( pVehicle );
	}
	else
	{
		memset( pVehicle, 0, sizeof(*pVehicle) );
	}

	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );
		if ( key[0] == '}' )
		{
			NextBlock();
			return;
		}

		// parse subchunks
		if ( value[0] == '{' )
		{
			if ( !Q_stricmp( key, "axle" ) )
			{
				ParseVehicleAxle( pVehicle->axles[pVehicle->axleCount] );
				pVehicle->axleCount++;
			}
			else if ( !Q_stricmp( key, "body" ) )
			{
				ParseVehicleBody( pVehicle->body );
			}
			else if ( !Q_stricmp( key, "engine" ) )
			{
				ParseVehicleEngine( pVehicle->engine );
			}
			else if ( !Q_stricmp( key, "steering" ) )
			{
				ParseVehicleSteering( pVehicle->steering );
			}
			else
			{
				SkipBlock();
			}
		}
		else if ( !Q_stricmp( key, "wheelsperaxle" ) )
		{
			pVehicle->wheelsPerAxle = atoi( value );
		}
	}
}

void CVPhysicsParse::ParseCustom( void *pCustom, IVPhysicsKeyHandler *unknownKeyHandler )
{
	char key[MAX_KEYVALUE], value[MAX_KEYVALUE];

	key[0] = 0;
	int indent = 0;
	if ( unknownKeyHandler )
	{
		unknownKeyHandler->SetDefaults( pCustom );
	}

	while ( m_pText )
	{
		m_pText = ParseKeyvalue( m_pText, key, value );

		if ( m_pText )
		{
			if ( key[0] == '{' )
			{
				indent++;
			}
			else if ( value[0] == '{' )
			{
				// They've got a named block here
				// Increase our indent, and let them parse the key
				indent++;
				if ( unknownKeyHandler )
				{
					unknownKeyHandler->ParseKeyValue( pCustom, key, value );
				}
			}
			else if ( key[0] == '}' )
			{
				indent--;
				if ( indent < 0 )
				{
					NextBlock();
					return;
				}
			}
			else
			{
				if ( unknownKeyHandler )
				{
					unknownKeyHandler->ParseKeyValue( pCustom, key, value );
				}
			}
		}
	}
}

IVPhysicsKeyParser *CreateVPhysicsKeyParser( const char *pKeyData )
{
	return new CVPhysicsParse( pKeyData );
}

void DestroyVPhysicsKeyParser( IVPhysicsKeyParser *pParser )
{
	delete pParser;
}

//-----------------------------------------------------------------------------
// Helper functions for parsing script file
//-----------------------------------------------------------------------------

const char *ParseKeyvalue( const char *pBuffer, char *key, char *value )
{
	char com_token[1024];

	pBuffer = (const char *)ParseFile( pBuffer, com_token, NULL );
	if ( Q_strlen( com_token ) < MAX_KEYVALUE )
	{
		Q_strncpy( key, com_token, MAX_KEYVALUE );
		Q_strlower( key );
	}
	// no value on a close brace
	if ( !Q_strcmp( key, "}" ) )
	{
		value[0] = 0;
		return pBuffer;
	}

	pBuffer = (const char *)ParseFile( pBuffer, com_token, NULL );
	if ( Q_strlen( com_token ) < MAX_KEYVALUE )
	{
		Q_strncpy( value, com_token, MAX_KEYVALUE );
		Q_strlower( value );
	}

	return pBuffer;
}

