/*
**	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/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : Combat																		  *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/commando/inputconfigmgr.cpp       $*
 *                                                                                             *
 *                       Author:: Patrick Smith                                                *
 *                                                                                             *
 *                     $Modtime:: 1/09/02 11:40a                                              $*
 *                                                                                             *
 *                    $Revision:: 6                                                           $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "inputconfigmgr.h"
#include "dlgcontrols.h"
#include "input.h"
#include "rawfile.h"
#include "ffactory.h"
#include "registry.h"
#include "chunkio.h"
#include "debug.h"
#include "translatedb.h"
#include "string_ids.h"
#include "slavemaster.h"


////////////////////////////////////////////////////////////////
//	Local constants
////////////////////////////////////////////////////////////////
static const char *CFG_DICTIONARY_FILENAME = "CONFIG.DAT";

////////////////////////////////////////////////////////////////
//	Registry key names
////////////////////////////////////////////////////////////////
//static const char *REGISTRY_SUB_KEY		= "Software\\Westwood\\Renegade\\Input";
static const char *REG_CURRENT_CONFIG	= "Current Config";

////////////////////////////////////////////////////////////////
//	Static member initialization
////////////////////////////////////////////////////////////////
DynamicVectorClass<InputConfigClass>	InputConfigMgrClass::ConfigList;
int												InputConfigMgrClass::CurrentConfigIndex = -1;


////////////////////////////////////////////////////////////////
//	Save/load constants
////////////////////////////////////////////////////////////////
enum
{
	CHUNKID_CONFIG_LIST			= 0x07181105,
	CHUNKID_VARIABLES,
	CHUNKID_CONFIG_OBJ,
};

enum
{
	VARID_CURRENT_CONFIG			= 1,
};


////////////////////////////////////////////////////////////////
//
//	Initialize
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Initialize (void)
{
	//
	//	Build the configuration list and load the current configuration
	//
	Load ();
	Load_Current_Configuration ();
	return ;
}


////////////////////////////////////////////////////////////////
//
//	Shutdown
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Shutdown (void)
{
	//
	//	Save our current configuration list
	//
	if (!SlaveMaster.Am_I_Slave()) {
		Save ();
	}

	//
	//	Reset our data
	//
	ConfigList.Delete_All ();
	CurrentConfigIndex = -1;
	return ;
}


////////////////////////////////////////////////////////////////
//
//	Get_Current_Configuration
//
////////////////////////////////////////////////////////////////
bool
InputConfigMgrClass::Get_Current_Configuration (InputConfigClass &config)
{
	if (CurrentConfigIndex < 0 || CurrentConfigIndex >= ConfigList.Count ()) {
		return false;
	}

	//
	//	Simply index into the config list and return the object to the caller
	//
	config = ConfigList[CurrentConfigIndex];
	return true;
}


////////////////////////////////////////////////////////////////
//
//	Load_Current_Configuration
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Load_Current_Configuration (void)
{
	if (CurrentConfigIndex < 0 || CurrentConfigIndex >= ConfigList.Count ()) {

		//
		//	The current configuration is invalid, so load the
		//	default configuration
		//
		Load_Default_Configuration ();
	} else {

		//
		//	Load this configuration
		//
		Load_Configuration (ConfigList[CurrentConfigIndex]);
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	Save_Current_Configuration
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Save_Current_Configuration (void)
{
	if (CurrentConfigIndex < 0 || CurrentConfigIndex >= ConfigList.Count ()) {
		return ;
	}

	//
	//	Save this configuration
	//
	Save_Configuration (ConfigList[CurrentConfigIndex]);
	return ;
}


////////////////////////////////////////////////////////////////
//
//	Add_Configuration
//
////////////////////////////////////////////////////////////////
int
InputConfigMgrClass::Add_Configuration (const WCHAR *display_name)
{
	//
	//	Get a filename for the configuration
	//
	StringClass filename;
	Get_Unique_Config_Filename (filename);

	//
	//	Build a new input config object
	//
	InputConfigClass config;
	config.Set_Display_Name (display_name);
	config.Set_Filename (filename);
	config.Set_Is_Custom (true);

	//
	//	Add the object to our list
	//
	CurrentConfigIndex = ConfigList.Add (config);

	//
	//	Now save the current configuration to this file
	//
	Save_Configuration (config);
	return CurrentConfigIndex;
}


////////////////////////////////////////////////////////////////
//
//	Delete_Configuration
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Delete_Configuration (int index)
{
	if (index < 0 || index >= ConfigList.Count ()) {
		return ;
	}

	//
	//	Reset the current configuration (if necessary)
	//
	bool load_default = false;
	if (index == CurrentConfigIndex) {
		CurrentConfigIndex = -1;		
		load_default = true;
	}

	//
	//	Get the local path of the configuration files
	//
	StringClass config_path;
	Get_Config_Path (config_path);

	StringClass full_path;
	full_path.Format ("%s\\%s", config_path, ConfigList[index].Get_Filename ());

	//
	//	Delete the configuration file
	//
	::DeleteFile (full_path);

	//
	//	Now remove this entry from the list
	//
	ConfigList.Delete (index);

	//
	//	Load the default configuration is we just deleted the current configuration
	//
	if (load_default) {
		Load_Default_Configuration ();
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	Delete_Configuration
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Delete_Configuration (const char *filename)
{
	//
	//	Try to find the configuration in our list
	//
	int index = Find_Configuration (filename);
	if (index != -1) {

		//
		//	We've found the conversation, so delete it
		//
		Delete_Configuration (index);
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	Find_Configuration
//
////////////////////////////////////////////////////////////////
int
InputConfigMgrClass::Find_Configuration (const char *filename)
{
	int retval = -1;

	for (int index = 0; index < ConfigList.Count (); index ++) {

		//
		//	Is this the entry we're looking for?
		//
		if (::lstrcmpi (ConfigList[index].Get_Filename (), filename) == 0) {
			retval = index;
			break;
		}
	}

	return retval;
}


////////////////////////////////////////////////////////////////
//
//	Load_Default_Configuration
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Load_Default_Configuration (void)
{
	for (int index = 0; index < ConfigList.Count (); index ++) {

		//
		//	Is this the default configuration?
		//
		if (ConfigList[index].Is_Default ()) {
			Load_Configuration (ConfigList[index]);
			break;
		}
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	Load_Configuration
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Load_Configuration (const InputConfigClass &config)
{
	//
	//	Anytime we loada configuration, it becomes our current config
	//
	CurrentConfigIndex = Find_Configuration (config.Get_Filename ());

	//
	//	Load the configuration into memory
	//
	Input::Load_Configuration (config.Get_Filename ());

	//
	//	Reload the UI (if necessary)
	//
	if (ControlsMenuClass::Get_Instance () != NULL) {
		ControlsMenuClass::Get_Instance ()->Reload ();
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	Save_Configuration
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Save_Configuration (const InputConfigClass &config)
{
	//
	//	We can only save custom configurations
	//
	if (config.Is_Custom ()) {

		//
		//	Anytime we save a configuration, it becomes our current config...
		//
		CurrentConfigIndex = Find_Configuration (config.Get_Filename ());
		Input::Save_Configuration (config.Get_Filename ());

		//
		//	Update the copy of this config in our internal list
		//
		if (CurrentConfigIndex != -1) {
			ConfigList[CurrentConfigIndex] = config;
		}

	} else {

		//
		//	Make a name for a new configuration
		//
		WideStringClass new_name = TRANSLATE (IDS_MENU_CUSTOM);
		new_name += config.Get_Display_Name ();

		//
		//	Add a new configuration to list (this saves the new
		// configuration as well)
		//
		Add_Configuration (new_name);
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	Get_Unique_Config_Filename
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Get_Unique_Config_Filename (StringClass &filename)
{
	//
	//	Get the local path of the configuration files
	//
	StringClass config_path;
	Get_Config_Path (config_path);

	int slot	= 1;

	StringClass full_path;
	do
	{
		//
		//	Build this filename
		//
		filename.Format ("input%.2d.cfg", slot ++);

		//
		//	Check to see if this file exists
		//
		full_path.Format ("%s\\%s", (const char *)config_path, filename);

	} while (::GetFileAttributes (full_path) != 0xFFFFFFFF);

	return ;
}


////////////////////////////////////////////////////////////////
//
//	Save
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Save (void)
{
	FileClass *file = _TheFileFactory->Get_File (CFG_DICTIONARY_FILENAME);
	WWASSERT (file != NULL);
	if (file == NULL) {
		return ;
	}

	//
	//	Open the file for writing
	//
	file->Open (FileClass::WRITE);
	ChunkSaveClass csave(file);

	//
	//	Save the config list to its own chunk
	//
	csave.Begin_Chunk (CHUNKID_CONFIG_LIST);
		Save_Config_List (csave);
	csave.End_Chunk ();

	//
	//	Save the
	//
	csave.Begin_Chunk (CHUNKID_VARIABLES);
		Save_Variables (csave);
	csave.End_Chunk ();

	//
	//	Close the file
	//
	file->Close ();
	_TheFileFactory->Return_File (file);
	return ;
}


////////////////////////////////////////////////////////////////
//
//	Load
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Load (void)
{
	FileClass *file = _TheFileFactory->Get_File (CFG_DICTIONARY_FILENAME);
	WWASSERT (file != NULL);
	if (file == NULL) {
		return ;
	}

	//
	//	Open the file for reading
	//
	file->Open (FileClass::READ);
	ChunkLoadClass cload(file);

	//
	//	Read all the chunks stored in this file
	//
	while (cload.Open_Chunk ()) {
		switch(cload.Cur_Chunk_ID ()) {

			case CHUNKID_CONFIG_LIST:
				Load_Config_List (cload);
				break;

			case CHUNKID_VARIABLES:
				Load_Variables (cload);
				break;

			default:
				Debug_Say (("Unrecognized InputConfigMgrClass Chunk ID\n"));
				break;
		}

		cload.Close_Chunk ();
	}

	//
	//	If the configuration list is empty, then add the default
	// configuration file to the list
	//
	if (ConfigList.Count () == 0) {
		InputConfigClass config;
		config.Set_Display_Name (TRANSLATE (IDS_MENU_DEFAULT_SETTINGS));
		config.Set_Filename (DEFAULT_INPUT_FILENAME);
		config.Set_Is_Custom (false);
		config.Set_Is_Default (true);
		ConfigList.Add (config);
		CurrentConfigIndex = 0;
	}

	//
	//	Close the file
	//
	file->Close ();
	_TheFileFactory->Return_File (file);
	return ;
}


////////////////////////////////////////////////////////////////
//
//	Save_Config_List
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Save_Config_List (ChunkSaveClass &csave)
{
	for (int index = 0; index < ConfigList.Count (); index ++) {

		//
		//	Save this configuration object to its own chunk
		//
		csave.Begin_Chunk (CHUNKID_CONFIG_OBJ);
			ConfigList[index].Save (csave);
		csave.End_Chunk ();
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	Load_Config_List
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Load_Config_List (ChunkLoadClass &cload)
{
	//
	//	Read all the sub-chunks
	//
	while (cload.Open_Chunk ()) {
		switch (cload.Cur_Chunk_ID ()) {

			case CHUNKID_CONFIG_OBJ:
			{
				//
				//	Load this configuration object
				//
				InputConfigClass config;
				config.Load (cload);

				//
				//	Add the object to our list
				//
				ConfigList.Add (config);
				break;
			}

			default:
				Debug_Say (("Unrecognized InputConfigMgrClass::Load_Config_List Chunk ID\n"));
				break;
		}

		cload.Close_Chunk ();
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	Save_Variables
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Save_Variables (ChunkSaveClass &csave)
{
	//
	//	Determine what the filename of the current configuration is
	//
	StringClass filename;
	if (CurrentConfigIndex != -1) {
		filename = ConfigList[CurrentConfigIndex].Get_Filename ();
	} else {
		filename = DEFAULT_INPUT_FILENAME;
	}

	WRITE_MICRO_CHUNK_WWSTRING (csave, VARID_CURRENT_CONFIG, filename);
	return ;
}


////////////////////////////////////////////////////////////////
//
//	Load_Variables
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Load_Variables (ChunkLoadClass &cload)
{
	StringClass filename;

	//
	//	Read all the microchunks
	//
	while (cload.Open_Micro_Chunk ()) {
		switch(cload.Cur_Micro_Chunk_ID ()) {
			READ_MICRO_CHUNK_WWSTRING (cload, VARID_CURRENT_CONFIG, filename);
		}

		cload.Close_Micro_Chunk ();
	}


	//
	//	Store the index of this configuration
	//
	CurrentConfigIndex = Find_Configuration (filename);
	return ;
}


////////////////////////////////////////////////////////////////
//
//	Get_Config_Path
//
////////////////////////////////////////////////////////////////
void
InputConfigMgrClass::Get_Config_Path (StringClass &full_path)
{
	//
	//	Lookup the path of the executable
	//
	char path[MAX_PATH] = { 0 };
	::GetModuleFileName (NULL, path, sizeof (path));

	//
	//	Strip off the filename
	//
	char *filename_portion = ::strrchr (path, '\\');
	if (filename_portion != NULL) {
		filename_portion[0] = 0;
	}

	//
	//	Build the full path from the EXE's directory
	//
	full_path.Format ("%s\\data\\config", path);
	return ;
}