/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program 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 General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/******************************************************************************
*
* FILE
*
* DESCRIPTION
*
* PROGRAMMER
*     Denzil E. Long, Jr.
*
* VERSION INFO
*     $Author: Byon_g $
*     $Revision: 85 $
*     $Modtime: 7/05/01 12:12p $
*     $Archive: /Commando/Code/Scripts/scripts.cpp $
*
******************************************************************************/

#include "scripts.h"
#include "scriptfactory.h"
#include "dprint.h"
#include "strtrim.h"
#include <string.h>
#include <stdio.h>

#define	CHUNKID_SCRIPTHEADER				'shdr'
#define	CHUNKID_SCRIPTDATA				'sdat'
#define	CHUNKID_SCRIPT_AUTO_VARIABLES	'csav'

enum {
	DATAID_PARAMETER_STRING = 1,
	DATAID_OWNERPTR,
};

// Script commands
ScriptCommands* Commands = NULL;
void (*ScriptImpClass::Request_Destroy_Script)(ScriptClass*) =  NULL;

void ScriptImpClass::Set_Request_Destroy_Func(void (*function)(ScriptClass*))
{
	Request_Destroy_Script = function;
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::ScriptImpClass
*
* DESCRIPTION
*     Constructor for concrete script implementation class
*
* INPUTS
*     NONE
*
* RESULTS
*     NONE
*
******************************************************************************/

ScriptImpClass::ScriptImpClass()
	: mOwner(NULL),
		mArgC(0),
		mArgV(NULL),
		AutoVariableList( NULL )
{
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::~ScriptImpClass
*
* DESCRIPTION
*     Destructor
*
* INPUTS
*     NONE
*
* RESULTS
*     NONE
*
******************************************************************************/

ScriptImpClass::~ScriptImpClass()
{
	#ifdef _DEBUG
	if (Commands != NULL) {
		DebugPrint("Script '%s' for object '%d' deleted.\n",
			Get_Name(), ((mOwner != NULL) ? Commands->Get_ID(mOwner) : 0 ));
	}
	#endif

	Clear_Parameters();

	// Delete the auto variables
	while ( AutoVariableList != NULL ) {
		ScriptVariableClass * next = AutoVariableList->Get_Next();
		delete AutoVariableList;
		AutoVariableList = next;
	}
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Get_Name
*
* DESCRIPTION
*     Retrieve name of script
*
* INPUTS
*     NONE
*
* RESULTS
*     Name - Script name string.
*
******************************************************************************/

const char* ScriptImpClass::Get_Name(void)
{
	return mFactory->GetName();
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Destroy
*
* DESCRIPTION
*     Destroy script
*
* INPUTS
*     NONE
*
* RESULTS
*     NONE
*
******************************************************************************/

void ScriptImpClass::Destroy_Script(void)
{
	if (Request_Destroy_Script != NULL) {
		Request_Destroy_Script(this);
	}
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Attach
*
* DESCRIPTION
*     Handle attachment of script to an object
*
* INPUTS
*     Object - Object script is attaching to.
*
* RESULTS
*     NONE
*
******************************************************************************/

void ScriptImpClass::Attach(GameObject* obj)
{
	#ifdef _DEBUG
	if (Commands != NULL) {
		DebugPrint("Attaching script '%s' to object %d\n",
			Get_Name(), Commands->Get_ID(obj));
	}
	#endif

	mOwner = obj;
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Detach
*
* DESCRIPTION
*     Handle detachment of script from an object.
*
* INPUTS
*     Object - Object script is detaching from.
*
* RESULTS
*     NONE
*
******************************************************************************/

void ScriptImpClass::Detach(GameObject* obj)
{
	mOwner = NULL;
	Destroy_Script();
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Set_Parameters_String
*
* DESCRIPTION
*     Set script parameters.
*
* INPUTS
*     Params - Parameter string
*
* RESULTS
*     NONE
*
******************************************************************************/

void ScriptImpClass::Set_Parameters_String(const char* params)
{
	if ((params == NULL) || (strlen(params) == 0)) {
		return;
	}

	DebugPrint("Setting parameters for script '%s', params = '%s'\n",
		Get_Name(), params);

	char* working = strdup(params);

	if ( strlen( working ) && working[ strlen( working ) - 1 ] == '\n' ) {
		working[ strlen( working ) - 1 ] = 0;
	}

	// Count the parameters
	int count = 0;
	char * param_ptr = working;
	if ( *param_ptr ) count++;
	for ( param_ptr = working; *param_ptr; param_ptr++ ) {
		if ( *param_ptr == ',' )	count++;
	}

	if (count == 0) {
		return;
	}

	// Release old argument vector
	Clear_Parameters();

	// Create new argument vector
	mArgC = count;
	mArgV = new char*[count];
	assert(mArgV != NULL);
	memset(mArgV, 0, (sizeof(char*) * count));

	// Copy the parameters
	strcpy(working, params);

	count = 0;
	param_ptr = working;
	while ( *param_ptr ) {
		// Find where this parameter ends
		char * next = param_ptr;
		while ( *next && *next != ',' ) next++;
		if ( *next ) *next++ = 0;

		// Set param
		Set_Parameter(count++, param_ptr);

		param_ptr = next;
	}

	if (count == mArgC - 1) {
		Set_Parameter(count, "");
	}

	free(working);
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Get_Parameters_String
*
* DESCRIPTION
*     Retrieve a copy of the scripts parameters string.
*
* INPUTS
*     Buffer - Buffer to copy parameter string into.
*     Size   - Size of buffer.
*
* RESULTS
*     NONE
*
******************************************************************************/

void ScriptImpClass::Get_Parameters_String(char* buffer, unsigned int size)
{
	assert(buffer != NULL);

	buffer[0] = '\0';

	// Build parameter string (comma seperated)
	int count = Get_Parameter_Count();
	int i = 0;
	unsigned int stringLen = 0;

	while ((i < count) && (stringLen < size)) {
		if (i > 0) {
			stringLen++;
			strcat(buffer, ",");
		}

		const char* param = Get_Parameter(i);
		stringLen += strlen(param);

		if (stringLen <= size) {
			strcat(buffer, param);
			i++;
		}
	}
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Clear_Parameters
*
* DESCRIPTION
*     Release script parameters
*
* INPUTS
*     NONE
*
* RESULTS
*     NONE
*
******************************************************************************/

void ScriptImpClass::Clear_Parameters(void)
{
	if (mArgV != NULL) {
		while (mArgC--) {
			if (mArgV[mArgC] != NULL) {
				free((void*)mArgV[mArgC]);
			}
		}

		delete (void*)mArgV;
	}

	mArgC = 0;
	mArgV = NULL;
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Set_Parameter
*
* DESCRIPTION
*     Set a script parameter
*
* INPUTS
*     Index - Ordinal position of parameter
*     Param - Parameter string
*
* RESULTS
*     NONE
*
******************************************************************************/

void ScriptImpClass::Set_Parameter(int index, const char* str)
{
	// Release old parameter
	if (mArgV[index] != NULL) {
		free((void*)mArgV[index]);
	}

	mArgV[index] = strdup(str);
	assert(mArgV[index] != NULL);
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Get_Parameter
*
* DESCRIPTION
*     Retrieve paramter by name
*
* INPUTS
*     Name - Paramter name
*
* RESULTS
*     Param - Parameter
*
******************************************************************************/

const char* ScriptImpClass::Get_Parameter(const char* name)
{
	int index = Get_Parameter_Index(name);
	return Get_Parameter(index);
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Get_Parameter
*
* DESCRIPTION
*     Retrieve parameter at specified index.
*
* INPUTS
*     Index - Ordinal position of paramater
*
* RESULTS
*     Param - Parameter string.
*
******************************************************************************/

const char* ScriptImpClass::Get_Parameter(int index)
{
	if ((index < 0) || (index >= mArgC)) {
		return "";
	}

	return mArgV[index];
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Get_Int_Parameter
*
* DESCRIPTION
*     Retrieve parameter as an integer.
*
* INPUTS
*     Name - Name of parameter
*
* RESULTS
*     Value - Integer value.
*
******************************************************************************/

int ScriptImpClass::Get_Int_Parameter(const char* parameterName)
{
	int index = Get_Parameter_Index(parameterName);
	return Get_Int_Parameter(index);
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Get_Float_Parameter
*
* DESCRIPTION
*     Retrieve parameter as a floating point number.
*
* INPUTS
*     Name - Name of parameter
*
* RESULTS
*     Value - Floating point value
*
******************************************************************************/

float ScriptImpClass::Get_Float_Parameter(const char* parameterName)
{
	int index = Get_Parameter_Index(parameterName);
	return Get_Float_Parameter(index);
}


/******************************************************************************
**
******************************************************************************/
Vector3 ScriptImpClass::Get_Vector3_Parameter( int index )
{
	float		x,y,z;
	::sscanf( Get_Parameter( index ), "%f %f %f", &x, &y, &z );
	return Vector3( x,y,z );
}


Vector3 ScriptImpClass::Get_Vector3_Parameter(const char* parameterName)
{
	int index = Get_Parameter_Index(parameterName);
	return Get_Vector3_Parameter(index);
}



/******************************************************************************
*
* NAME
*     ScriptImpClass::Get_Parameter_Index
*
* DESCRIPTION
*     Obtain index of parameter with specified name.
*
* INPUTS
*     Name - Name of parameter
*
* RESULTS
*     Index - Ordinal position of parameter
*
******************************************************************************/

int ScriptImpClass::Get_Parameter_Index(const char* parameterName)
{
	const char* paramDesc = mFactory->GetParamDescription();

	// Make copy of parameter description string to work with.
	char string[512];
	strncpy(string, paramDesc, (sizeof(string) - 1));
	string[sizeof(string)-1] = '\0';

	// Search for the specified parameter
	int index = 0;
	char * param_ptr = string;
	while ( *param_ptr ) {
		// Find where this parameter ends
		char * next = param_ptr;
		while ( *next && *next != ',' ) next++;
		if ( *next ) *next++ = 0;

		// Strip the name portion from the parameter definition
		char* tokenEnd = strpbrk(param_ptr, "=:\n");

		if (tokenEnd != NULL) {
			*tokenEnd = '\0';
		}

		// Check for specified parameter
		strtrim(param_ptr);

		if (stricmp(param_ptr, parameterName) == 0) {
			return index;
		}

		// Advance to next parameter definition
		index++;

		param_ptr = next;
	}

	return -1;
}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Save
*
* DESCRIPTION
*     Save script data
*
* INPUTS
*     ScriptSaver& saver
*
* RESULTS
*     NONE
*
******************************************************************************/

void ScriptImpClass::Save(ScriptSaver& saver)
{
/*	Commands->Begin_Chunk(saver, CHUNKID_SCRIPTDATA);
	Save_Data(saver);
	Commands->End_Chunk(saver);
*/

	ScriptVariableClass * var = AutoVariableList;
	if ( var != NULL ) {
		Commands->Begin_Chunk(saver, CHUNKID_SCRIPT_AUTO_VARIABLES);
	 	while ( var != NULL ) {
			Commands->Save_Data(saver, var->Get_ID(), var->Get_Data_Size(), var->Get_Data_Ptr() );
	 		var = var->Get_Next();
	 	}
		Commands->End_Chunk(saver);
	}

}


/******************************************************************************
*
* NAME
*     ScriptImpClass::Load
*
* DESCRIPTION
*     Load script data
*
* INPUTS
*     ScriptLoader& loader
*
* RESULTS
*     NONE
*
******************************************************************************/

void ScriptImpClass::Load(ScriptLoader& loader)
{
	unsigned int chunkID;

	while (Commands->Open_Chunk(loader, &chunkID)) {

		switch (chunkID) {

/*			case CHUNKID_SCRIPTDATA:
				DebugPrint("Encountered data chunk.\n");
				Load_Data(loader);
			break;
*/

			case CHUNKID_SCRIPT_AUTO_VARIABLES:
			{
				int id;
				while (Commands->Load_Begin(loader, &id)) {
					// If we find this ID, load it
					ScriptVariableClass * var = AutoVariableList;
					while ( var != NULL ) {
						if ( var->Get_ID() == id ) {
							Commands->Load_Data(loader, var->Get_Data_Size(), var->Get_Data_Ptr() );
						}
				 		var = var->Get_Next();
					}
					Commands->Load_End(loader);
				}
			}
			break;

			default:
				DebugPrint("***** ScriptImpClass::Load() - Unrecognized chunk %0x!\n", chunkID);
			break;
		}

		Commands->Close_Chunk(loader);
	}
}


/*
**
*/
void	ScriptImpClass::Auto_Save_Variable( void * data_ptr, int data_size, int id )
{
	if ( ( id < 0 ) || ( id > 255 ) ) {
		DebugPrint("***** ScriptImpClass::Auto_Save_Variable  Bad ID %d\n", id );
		return;
	}

	// First be sure we don't already have this ID
	ScriptVariableClass * var = AutoVariableList;
	while ( var != NULL ) {
		if ( var->Get_ID() == id ) {
			DebugPrint("***** ScriptImpClass::Auto_Save_Variable  Dupe ID %d\n", id );
			return;
		}
		var = var->Get_Next();
	}

	// Never save more than 250 bytes
	if ( data_size > 250 ) {
		DebugPrint("***** ScriptImpClass::Too Much Save Data on ID %d\n", id );
		return;
	}


	// Then create and add the variable;
	AutoVariableList = new ScriptVariableClass( data_ptr, data_size, id, AutoVariableList );
}
