/*
**	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 : commando                                                    *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Commando/dlgconfigperformancetab.cpp                              $*
 *                                                                                             *
 *                       Author:: Patrick Smith                                                *
 *                                                                                             *
 *                     $Modtime:: 1/08/02 3:03p                                                $*
 *                                                                                             *
 *                    $Revision:: 11                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "dlgconfigperformancetab.h"
#include "resource.h"
#include "registry.h"
#include "comboboxctrl.h"
#include "sliderctrl.h"
#include "rendobj.h"
#include "phys.h"
#include "ww3d.h"
#include "pscene.h"
#include "combat.h"
#include "dx8wrapper.h"
#include "dx8caps.h"
#include "surfaceeffects.h"
#include "_globals.h"
#include "translatedb.h"
#include "string_ids.h"


/////////////////////////////////////////////////////////////////////////////
// Structures and typedefs
/////////////////////////////////////////////////////////////////////////////
typedef struct _PERFORMANCE_SETTING
{
	int ctrl_id;
	int value;
} PERFORMANCE_SETTING;


/////////////////////////////////////////////////////////////////////////////
// Constants
/////////////////////////////////////////////////////////////////////////////

const int MAX_PERFORMANCE_LEVELS	= 4;
const int MAX_EXPERT_OPTIONS		= 7;

PERFORMANCE_SETTING _PerformanceLevels[MAX_PERFORMANCE_LEVELS][MAX_EXPERT_OPTIONS] = 
{
	//
	//	Low detail
	//
	{
		{IDC_CHAR_SHADOWS_SLIDER, 0},
		{IDC_TEXTURE_DETAIL_SLIDER, 0},
		{IDC_PARTICLE_DETAIL_SLIDER, 0},
		{IDC_SURFACE_DETAIL_SLIDER, 0},
		{IDC_GEOMETRY_DETAIL_SLIDER, 0},
		{IDC_TERRAIN_SHADOW_CHECK, 0},
		{IDC_NPATCH_CHECK, 0}
	},

	{
		{IDC_CHAR_SHADOWS_SLIDER, 1},
		{IDC_TEXTURE_DETAIL_SLIDER, 1},
		{IDC_PARTICLE_DETAIL_SLIDER, 0},
		{IDC_SURFACE_DETAIL_SLIDER, 0},
		{IDC_GEOMETRY_DETAIL_SLIDER, 0},
		{IDC_TERRAIN_SHADOW_CHECK, 0},
		{IDC_NPATCH_CHECK, 0}
	},

	{
		{IDC_CHAR_SHADOWS_SLIDER, 2},
		{IDC_TEXTURE_DETAIL_SLIDER, 2},
		{IDC_PARTICLE_DETAIL_SLIDER, 1},
		{IDC_SURFACE_DETAIL_SLIDER, 1},
		{IDC_GEOMETRY_DETAIL_SLIDER, 1},
		{IDC_TERRAIN_SHADOW_CHECK, 1},
		{IDC_NPATCH_CHECK, 0}
	},

	//
	//	High detail
	//
	{
		{IDC_CHAR_SHADOWS_SLIDER, 3},
		{IDC_TEXTURE_DETAIL_SLIDER, 2},
		{IDC_PARTICLE_DETAIL_SLIDER, 2},
		{IDC_SURFACE_DETAIL_SLIDER, 2},
		{IDC_GEOMETRY_DETAIL_SLIDER, 2},
		{IDC_TERRAIN_SHADOW_CHECK, 1},
		{IDC_NPATCH_CHECK, 0}
	},
};

const char *VALUE_NAME_DYN_LOD			= "Dynamic_LOD_Budget";
const char *VALUE_NAME_STATIC_LOD		= "Static_LOD_Budget";
const char *VALUE_NAME_DYN_SHADOWS		= "Dynamic_Projectors";
const char *VALUE_NAME_SHADOW_MODE		= "Shadow_Mode";
const char *VALUE_NAME_STATIC_SHADOWS	= "Static_Projectors";
const char *VALUE_NAME_TEXTURE_RES		= "Texture_Resolution";
const char *VALUE_NAME_PARTICLE_DETAIL	= "Particle_Detail";
const char *VALUE_NAME_NPATCHES			= "NPatches";

const int MAX_LOD_HIGH	= 10000;
const int MAX_LOD_MED	= 5000;
const int MAX_LOD_LOW	= 0;


////////////////////////////////////////////////////////////////
//
//	DlgConfigPerformanceTabClass
//
////////////////////////////////////////////////////////////////
DlgConfigPerformanceTabClass::DlgConfigPerformanceTabClass (void)	:
	ChildDialogClass (IDD_CONFIG_PERFORMANCE)
{
	return ;
}


////////////////////////////////////////////////////////////////
//
//	~DlgConfigPerformanceTabClass
//
////////////////////////////////////////////////////////////////
DlgConfigPerformanceTabClass::~DlgConfigPerformanceTabClass (void)
{
	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_Init_Dialog
//
////////////////////////////////////////////////////////////////
void
DlgConfigPerformanceTabClass::On_Init_Dialog (void)
{
	//
	//	Configure the dialog controls
	//
	Setup_Controls ();
	Load_Values ();
	Determine_Performance_Setting ();

	//
	//	Set the visibility of the expert windows controls
	//
	Build_Expert_Window_List ();
	Display_Expert_Settings (false);

	ChildDialogClass::On_Init_Dialog ();
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Build_Expert_Window_List
//
/////////////////////////////////////////////////////////////////////////////
void
DlgConfigPerformanceTabClass::Build_Expert_Window_List (void)
{
	ExpertWindowList.Delete_All ();

	//
	//	Find the y-position of the expert mode checkbox
	//
	RectClass rect = Get_Dlg_Item (IDC_EXPERT_CHECK)->Get_Window_Rect ();
	float y_pos = rect.Top;

	//
	//	Loop over all the controls of the dialog
	//
	for (	int index = 0; index < Get_Control_Count (); index ++) {
		DialogControlClass *control = Get_Control (index);

		//
		//	If this control is below the expert checkbox then its
		// part of the expert settings
		//
		rect = control->Get_Window_Rect ();
		if (rect.Top > y_pos) {
			ExpertWindowList.Add (control);
		}
	}

	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Display_Expert_Settings
//
/////////////////////////////////////////////////////////////////////////////
void
DlgConfigPerformanceTabClass::Display_Expert_Settings (bool onoff)
{
	//
	//	Loop over all the expert settings controls and either
	// show or hide them...
	//
	for (int index = 0; index < ExpertWindowList.Count (); index ++) {
		ExpertWindowList[index]->Show (onoff);
	}

	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Setup_Controls
//
/////////////////////////////////////////////////////////////////////////////
void
DlgConfigPerformanceTabClass::Setup_Controls (void)
{
	//
	//	Configure the main performance slider
	//
	SliderCtrlClass *performance_slider = (SliderCtrlClass *)Get_Dlg_Item (IDC_PERFORMANCE_SLIDER);
	performance_slider->Set_Range (0, MAX_PERFORMANCE_LEVELS - 1);

	//
	//	Configure the individual detail-setting sliders
	//
	((SliderCtrlClass *)Get_Dlg_Item (IDC_GEOMETRY_DETAIL_SLIDER))->Set_Range (0, 2);
	((SliderCtrlClass *)Get_Dlg_Item (IDC_CHAR_SHADOWS_SLIDER))->Set_Range (0, 3);
	((SliderCtrlClass *)Get_Dlg_Item (IDC_TEXTURE_DETAIL_SLIDER))->Set_Range (0, 2);
	((SliderCtrlClass *)Get_Dlg_Item (IDC_SURFACE_DETAIL_SLIDER))->Set_Range (0, 2);
	((SliderCtrlClass *)Get_Dlg_Item (IDC_PARTICLE_DETAIL_SLIDER))->Set_Range (0, 2);

	const WCHAR *PRELIT_MODE_NAMES[] =
	{
		TRANSLATE (IDS_MENU_VERTEX),
		TRANSLATE (IDS_MENU_MP_LIGHTMAPS),
		TRANSLATE (IDS_MENU_MT_LIGHTMAPS)
	};
	
	Set_Dlg_Item_Text (IDC_LIGHTING_MODE, PRELIT_MODE_NAMES [WW3D::Get_Prelit_Mode()]);
	Enable_Dlg_Item (IDC_LIGHTING_MODE,	false);

	const WCHAR *TEXTURE_FILTER_NAMES[] =
	{
		TRANSLATE (IDS_BILINEAR),
		TRANSLATE (IDS_TRILINEAR),
		TRANSLATE (IDS_ANISOTROPIC)
	};

	Set_Dlg_Item_Text (IDC_TEXTURE_FILTER, TEXTURE_FILTER_NAMES [WW3D::Get_Texture_Filter()]);
	Enable_Dlg_Item (IDC_TEXTURE_FILTER, false);

	//
	//	Disable the checkbox if NPatches aren't supported
	//
	if (DX8Wrapper::Get_Current_Caps() && DX8Wrapper::Get_Current_Caps()->Support_NPatches () == false) {
		Check_Dlg_Button (IDC_NPATCH_CHECK, false);
		Enable_Dlg_Item (IDC_NPATCH_CHECK, false);
	}

	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Load_Values
//
/////////////////////////////////////////////////////////////////////////////
void
DlgConfigPerformanceTabClass::Load_Values (void)
{
	SliderCtrlClass *geometry_slider			= (SliderCtrlClass *)Get_Dlg_Item (IDC_GEOMETRY_DETAIL_SLIDER);
	SliderCtrlClass *char_shadows_slider	= (SliderCtrlClass *)Get_Dlg_Item (IDC_CHAR_SHADOWS_SLIDER);
	SliderCtrlClass *texture_slider			= (SliderCtrlClass *)Get_Dlg_Item (IDC_TEXTURE_DETAIL_SLIDER);
	SliderCtrlClass *surface_effect_slider = (SliderCtrlClass *)Get_Dlg_Item (IDC_SURFACE_DETAIL_SLIDER);
	SliderCtrlClass *particle_slider			= (SliderCtrlClass *)Get_Dlg_Item (IDC_PARTICLE_DETAIL_SLIDER);

	//
	//	Attempt to open the registry key
	//
	RegistryClass registry (APPLICATION_SUB_KEY_NAME_SYSTEM_SETTINGS);
	if (registry.Is_Valid ()) {

		//
		//	Read the values from the registry
		//
		int static_shadows	= registry.Get_Int (VALUE_NAME_STATIC_SHADOWS, 1);
		int shadow_mode		= registry.Get_Int (VALUE_NAME_SHADOW_MODE, PhysicsSceneClass::SHADOW_MODE_BLOBS_PLUS);
		int texture_red		= registry.Get_Int (VALUE_NAME_TEXTURE_RES, 0);
		int particle_detail	= registry.Get_Int (VALUE_NAME_PARTICLE_DETAIL, 1);
		int npatches			= registry.Get_Int (VALUE_NAME_NPATCHES, 0);

		//
		//	Get the surface effect mode
		//
		int surface_effect = SurfaceEffectsManager::Get_Mode ();

		//
		//	Get the LOD settings
		//
		int dynamic_lod	= 0;
		int static_lod		= 0;
		PhysicsSceneClass::Get_Instance()->Get_Polygon_Budgets (&static_lod, &dynamic_lod);

		//
		//	Set the slider's positions to reflect the loaded values
		//
		char_shadows_slider->Set_Pos (min (shadow_mode, 3));
		texture_slider->Set_Pos (max (2 - texture_red, 0));
		surface_effect_slider->Set_Pos (surface_effect);
		particle_slider->Set_Pos (particle_detail);

		//
		//	Choose a setting for the geometry slider based on
		// the dynamic and static LOD budgets
		//
		if (dynamic_lod < (MAX_LOD_LOW + 1000)) {
			geometry_slider->Set_Pos (0);
		} else if (dynamic_lod <= MAX_LOD_MED) {
			geometry_slider->Set_Pos (1);
		} else {
			geometry_slider->Set_Pos (2);
		}

		//
		//	Check the checkbox controls (if necessary)
		//
		Check_Dlg_Button (IDC_TERRAIN_SHADOW_CHECK, bool(static_shadows != 0));
		Check_Dlg_Button (IDC_NPATCH_CHECK, bool(npatches != 0));
	}

	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Determine_Performance_Setting
//
/////////////////////////////////////////////////////////////////////////////
void
DlgConfigPerformanceTabClass::Determine_Performance_Setting (void)
{
	float option_levels[MAX_EXPERT_OPTIONS] = { 0 };
	int count_per_option[MAX_EXPERT_OPTIONS] = { 0 };

	//
	//	Get the current values of each setting
	//
	DynamicVectorClass<int> option_values;
	Get_Settings (option_values);

	//
	// Find out which level each option is currently set at
	//
	for (int level = MAX_PERFORMANCE_LEVELS - 1; level >= 0; level --) {
		for (int index = 0; index < MAX_EXPERT_OPTIONS; index ++) {

			//
			//	If the values match, then record the current level for this option
			//
			if (option_values[index] == _PerformanceLevels[level][index].value) {
				option_levels[index] += level;
				count_per_option[index] ++;
			}
		}
	}

	//
	//	Add up all the levels
	//
	float level_sum = 0;
	for (int index = 0; index < MAX_EXPERT_OPTIONS; index ++) {
		level_sum += (option_levels[index] / count_per_option[index]);
	}

	//
	//	Take the average rating
	//
	float level_rating = level_sum / MAX_EXPERT_OPTIONS;

	//
	//	Set the slider's position
	//
	SliderCtrlClass *slider_ctrl	= (SliderCtrlClass *)Get_Dlg_Item (IDC_PERFORMANCE_SLIDER);
	slider_ctrl->Set_Pos (min (int(level_rating + 0.4F), MAX_EXPERT_OPTIONS - 1), false);
	return ;
}


//////////////////////////////////////////////////////////////////////
//
//	On_Command
//
//////////////////////////////////////////////////////////////////////
void
DlgConfigPerformanceTabClass::On_Command (int ctrl_id, int message_id, DWORD param)
{
	switch (ctrl_id)
	{
		case IDC_EXPERT_CHECK:
		{
			bool enabled = Is_Dlg_Button_Checked (IDC_EXPERT_CHECK);
			Display_Expert_Settings (enabled);
			break;
		}
	}

	ChildDialogClass::On_Command (ctrl_id, message_id, param);
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Get_Settings
//
/////////////////////////////////////////////////////////////////////////////
void
DlgConfigPerformanceTabClass::Get_Settings (DynamicVectorClass<int> &settings)
{
	//
	//	Determine what the value of each control is
	//
	for (int index = 0; index < MAX_EXPERT_OPTIONS; index ++) {

		//
		//	Determine what the value of this control is
		//
		int curr_value = 0;
		int ctrl_id = _PerformanceLevels[0][index].ctrl_id;
		switch (ctrl_id) {

			//
			//	Read any of the slider positions
			//
			case IDC_GEOMETRY_DETAIL_SLIDER:
			case IDC_CHAR_SHADOWS_SLIDER:
			case IDC_TEXTURE_DETAIL_SLIDER:
			case IDC_PARTICLE_DETAIL_SLIDER:
			case IDC_SURFACE_DETAIL_SLIDER:
			{
				SliderCtrlClass *slider_ctrl = (SliderCtrlClass *)Get_Dlg_Item (ctrl_id);
				curr_value = slider_ctrl->Get_Pos ();
				break;
			}

			//
			//	Read the values from either checkbox
			//
			case IDC_TERRAIN_SHADOW_CHECK:
			case IDC_NPATCH_CHECK:
				curr_value = Is_Dlg_Button_Checked (ctrl_id);
				break;
		}

		//
		//	Add this value to the list
		//
		settings.Add (curr_value);
	}

	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Update_Expert_Controls
//
/////////////////////////////////////////////////////////////////////////////
void
DlgConfigPerformanceTabClass::Update_Expert_Controls (int level)
{
	for (int index = 0; index < MAX_EXPERT_OPTIONS; index ++) {

		//
		//	Determine which control we are updating
		//
		int ctrl_id = _PerformanceLevels[level][index].ctrl_id;
		switch (ctrl_id) {

			//
			//	Update any of the sliders via the same mechanism
			//
			case IDC_GEOMETRY_DETAIL_SLIDER:
			case IDC_CHAR_SHADOWS_SLIDER:
			case IDC_TEXTURE_DETAIL_SLIDER:
			case IDC_PARTICLE_DETAIL_SLIDER:
			case IDC_SURFACE_DETAIL_SLIDER:
			{
				SliderCtrlClass *slider_ctrl = (SliderCtrlClass *)Get_Dlg_Item (ctrl_id);
				slider_ctrl->Set_Pos (_PerformanceLevels[level][index].value);
				break;
			}

			//
			//	Update any of the check boxes via the same mechanism
			//
			case IDC_TERRAIN_SHADOW_CHECK:
			case IDC_NPATCH_CHECK:
				Check_Dlg_Button (ctrl_id, bool(_PerformanceLevels[level][index].value == 1));
				break;
		}
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_SliderCtrl_Pos_Changed
//
////////////////////////////////////////////////////////////////
void
DlgConfigPerformanceTabClass::On_SliderCtrl_Pos_Changed
(
	SliderCtrlClass *	slider_ctrl,
	int					ctrl_id,
	int					new_pos
)
{
	if (ctrl_id == IDC_PERFORMANCE_SLIDER) {
		Update_Expert_Controls (new_pos);
	}

	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// On_Apply
//
/////////////////////////////////////////////////////////////////////////////
bool
DlgConfigPerformanceTabClass::On_Apply (void)
{
	SliderCtrlClass *geometry_slider			= (SliderCtrlClass *)Get_Dlg_Item (IDC_GEOMETRY_DETAIL_SLIDER);
	SliderCtrlClass *char_shadows_slider	= (SliderCtrlClass *)Get_Dlg_Item (IDC_CHAR_SHADOWS_SLIDER);
	SliderCtrlClass *texture_slider			= (SliderCtrlClass *)Get_Dlg_Item (IDC_TEXTURE_DETAIL_SLIDER);
	SliderCtrlClass *surface_effect_slider = (SliderCtrlClass *)Get_Dlg_Item (IDC_SURFACE_DETAIL_SLIDER);
	SliderCtrlClass *particle_slider			= (SliderCtrlClass *)Get_Dlg_Item (IDC_PARTICLE_DETAIL_SLIDER);
	
	//
	//	Attempt to open the registry key
	//
	RegistryClass registry (APPLICATION_SUB_KEY_NAME_SYSTEM_SETTINGS);
	if (registry.Is_Valid ()) {

		//
		//	Get the current settings from the dialog
		//
		int geometry_detail	= geometry_slider->Get_Pos ();
		int shadow_mode		= char_shadows_slider->Get_Pos ();
		int texture_red		= texture_slider->Get_Pos ();
		int surface_effect	= surface_effect_slider->Get_Pos ();
		int particle_detail	= particle_slider->Get_Pos ();
		int static_shadows	= Is_Dlg_Button_Checked (IDC_TERRAIN_SHADOW_CHECK);
		int npatches			= Is_Dlg_Button_Checked (IDC_NPATCH_CHECK);

		//
		//	Determine a good LOD budget to use
		//
		int lod_budget = 0;
		if (geometry_detail == 0) {
			lod_budget = 0;
		} else if (geometry_detail == 1) {
			lod_budget = MAX_LOD_MED;
		} else if (geometry_detail == 2) {
			lod_budget = MAX_LOD_HIGH;
		}

		//
		//	Store the values in the registry
		//
		registry.Set_Int (VALUE_NAME_DYN_LOD, lod_budget);
		registry.Set_Int (VALUE_NAME_STATIC_LOD, lod_budget);

		registry.Set_Int (VALUE_NAME_DYN_SHADOWS, (shadow_mode != PhysicsSceneClass::SHADOW_MODE_NONE));
		registry.Set_Int (VALUE_NAME_STATIC_SHADOWS, static_shadows);

		registry.Set_Int (VALUE_NAME_SHADOW_MODE,		shadow_mode);
		registry.Set_Int (VALUE_NAME_TEXTURE_RES,		max (2 - texture_red, 0));
		registry.Set_Int (VALUE_NAME_PARTICLE_DETAIL, particle_detail);

		if (DX8Wrapper::Get_Current_Caps() && DX8Wrapper::Get_Current_Caps()->Support_NPatches ()) {
			registry.Set_Int (VALUE_NAME_NPATCHES,	npatches);
		}

		//
		//	Pass the values onto the game
		//
		COMBAT_SCENE->Set_Polygon_Budgets (lod_budget, lod_budget);
		COMBAT_SCENE->Enable_Dynamic_Projectors ((shadow_mode != PhysicsSceneClass::SHADOW_MODE_NONE));
		COMBAT_SCENE->Enable_Static_Projectors ((static_shadows != 0));
		// Note! It is important to invalidate all textures when
		// changing the amount of render targets, as render target
		// creation may have problems if the card is running low on
		// texture memory!
		if (COMBAT_SCENE->Get_Shadow_Mode()!=(PhysicsSceneClass::ShadowEnum)shadow_mode) {
			WW3D::_Invalidate_Textures();
			COMBAT_SCENE->Set_Shadow_Mode ((PhysicsSceneClass::ShadowEnum)shadow_mode);
		}
		WW3D::Set_Texture_Reduction (max (2 - texture_red, 0));
		SurfaceEffectsManager::Set_Mode ((SurfaceEffectsManager::MODE)surface_effect);
	}

	return true;
}