//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Manages the set of application configuration options.
//
//=============================================================================//

#include "stdafx.h"
#include "Options.h"
#include "hammer.h"
#include "MainFrm.h"
#include "mapdoc.h"
#include "KeyValues.h"
#include "ConfigManager.h"
#include "GlobalFunctions.h"
#include "CustomMessages.h"
#include "OptionProperties.h"
#include <process.h>

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


const char GAMECFG_SIG[] = "Game Configurations File\r\n\x1a";
const float GAMECFG_VERSION = 1.4f;

static const char *pszGeneral = "General";
static const char *pszView2D = "2D Views";
static const char *pszView3D = "3D Views";
static const char *g_szColors = "Custom2DColors";

const int iThisVersion = 2;

// So File | Open will be in the right directory.
char *g_pMapDir = NULL;


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
COptionsConfigs::COptionsConfigs(void)
{
	nConfigs = 0;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Output : 
//-----------------------------------------------------------------------------
COptionsConfigs::~COptionsConfigs(void)
{
	for (int i = 0; i < nConfigs; i++)
	{
		CGameConfig *pConfig = Configs[i];
		if (!pConfig)
			continue;

		delete pConfig;
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Output : 
//-----------------------------------------------------------------------------
CGameConfig *COptionsConfigs::AddConfig(void)
{
	CGameConfig *pConfig = new CGameConfig;
	Configs.SetAtGrow(nConfigs++, pConfig);

	return pConfig;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : dwID - 
//			piIndex - 
// Output : 
//-----------------------------------------------------------------------------
CGameConfig *COptionsConfigs::FindConfig(DWORD dwID, int *piIndex)
{
	for(int i = 0; i < nConfigs; i++)
	{
		if(Configs[i]->dwID == dwID)
		{
			if(piIndex)
				piIndex[0] = i;
			return Configs[i];
		}
	}

	return NULL;
}


//-----------------------------------------------------------------------------
// Purpose: Looks for a game configuration with a given mod directory.
//-----------------------------------------------------------------------------
CGameConfig *COptionsConfigs::FindConfigForGame(const char *szGame)
{
	for (int i = 0; i < nConfigs; i++)
	{
		char *pszGameDir = Configs[i]->m_szModDir;
		if ( Q_stricmp( pszGameDir, szGame ) == 0 )
			return Configs[i];
	}

	return NULL;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns the number of game configurations successfully imported.
//-----------------------------------------------------------------------------
int COptionsConfigs::ImportOldGameConfigs(const char *pszFileName)
{
	int nConfigsRead = 0;

	char szRootDir[MAX_PATH];
	char szFullPath[MAX_PATH];
	APP()->GetDirectory(DIR_PROGRAM, szRootDir);
	Q_MakeAbsolutePath( szFullPath, MAX_PATH, pszFileName, szRootDir );
	std::fstream file( szFullPath, std::ios::in | std::ios::binary );
	if (file.is_open())
	{
		// Read sig.
		char szSig[sizeof(GAMECFG_SIG)];
		file.read(szSig, sizeof szSig);
		if (!memcmp(szSig, GAMECFG_SIG, sizeof szSig))
		{
			// Read version.
			float fThisVersion;
			file.read((char *)&fThisVersion, sizeof(fThisVersion));
			if ((fThisVersion >= 1.0) && (fThisVersion <= GAMECFG_VERSION))
			{
				// Read number of configs.
				int nTotalConfigs;
				file.read((char *)&nTotalConfigs, sizeof(nTotalConfigs));

				// Load each config.
				for (int i = 0; i < nTotalConfigs; i++)
				{
					CGameConfig *pConfig = AddConfig();
					pConfig->Import(file, fThisVersion);
					nConfigsRead++;

					if (!g_pMapDir)
					{
						g_pMapDir = (char *)pConfig->szMapDir;
					}
				}
			}
		}

		file.close();
	}

	return(nConfigsRead);
}

// Our call to "new" will be hosed without this header
#include "tier0/memdbgoff.h"

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool COptionsConfigs::ResetGameConfigs( bool bOverwrite )
{
	CGameConfigManager mgr;
	int nNumLoaded = 0;

	mgr.SetBaseDirectory( m_strConfigDir );

	if ( bOverwrite )
	{
		// Reset the configurations on the disk
		mgr.ResetConfigs();
		
		// Load the newly changed game configs
		nNumLoaded = LoadGameConfigs();
	}
	else
	{
		// Simply get the keyvalue block from the manager and parse that
		KeyValues *pDefaultConfigs = new KeyValues( "Defaults" );

		if ( mgr.GetDefaultGameBlock( pDefaultConfigs ) == false )
			return false;
		
		// Load from the blocks
		nNumLoaded = LoadGameConfigsBlock( pDefaultConfigs ); 

		// Clean up
		pDefaultConfigs->deleteThis();
	}

	return ( nNumLoaded > 0 );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pBlock - 
// Output : int
//-----------------------------------------------------------------------------
int COptionsConfigs::LoadGameConfigsBlock( KeyValues *pBlock )
{
	if ( pBlock == NULL )
		return 0;

	int nConfigsRead = 0;

	for ( KeyValues *pKey = pBlock->GetFirstTrueSubKey(); pKey; pKey = pKey->GetNextTrueSubKey() )
	{
		CGameConfig *pConfig = AddConfig();
		if ( pConfig != NULL )
		{	
			if ( pConfig->Load( pKey ) )
			{
				nConfigsRead++;
			}
		}
	}

	return nConfigsRead;
}

//-----------------------------------------------------------------------------
// Purpose: Loads all the game configs from disk.
// Output : Returns the number of game configurations successfully loaded.
//-----------------------------------------------------------------------------
int COptionsConfigs::LoadGameConfigs()
{
	//
	// Read game configs - this is from an external file so we can distribute it
	// with the editor as a set of defaults.
	//
	// Older versions of the editor used a binary file. Try that first.
	//
	int nConfigsRead = ImportOldGameConfigs("GameCfg.wc");
	if (nConfigsRead > 0)
	{
		// This will cause a double conversion, from .wc to .ini to .txt, but oh well...
		char szRootDir[MAX_PATH];
		char szFullPath[MAX_PATH];
		APP()->GetDirectory(DIR_PROGRAM, szRootDir);
		Q_MakeAbsolutePath( szFullPath, MAX_PATH, "GameCfg.wc", szRootDir );
		remove( szFullPath );
		char szSaveName[MAX_PATH];
		strcpy(szSaveName, m_strConfigDir);
		Q_AppendSlash(szSaveName, sizeof(szSaveName));
		Q_strcat(szSaveName, "GameCfg.ini", sizeof(szSaveName));
		SaveGameConfigs();
		return(nConfigsRead);
	}

	CGameConfigManager mgr;

	if ( !mgr.LoadConfigs( m_strConfigDir ) )
		return 0;

	KeyValues *pGame = mgr.GetGameBlock();
	if (!pGame)
		return 0;

	// Install the message handler for error messages.
	GDSetMessageFunc(Msg);

	// Load from the blocks
	nConfigsRead = LoadGameConfigsBlock( pGame );

	return nConfigsRead;
}


//-----------------------------------------------------------------------------
// Purpose: Saves all the cgame configurations to disk.
// Input  : *pszFileName - 
//-----------------------------------------------------------------------------
void COptionsConfigs::SaveGameConfigs()
{
	// Only do this if we've got configs to save!
	if ( GetGameConfigCount() == 0 )
		return;

	CGameConfigManager mgr;

	if ( mgr.LoadConfigs( m_strConfigDir ) == false )
		return;

	// Get the global configuration data
	KeyValues *pGame = mgr.GetGameBlock();
	
	// For each Hammer known configuation, update the values in the global configs
	for ( int i = 0; i < nConfigs; i++ )
	{
		KeyValues *pConfig = pGame->FindKey(Configs.GetAt(i)->szName);
		
		// Add the configuration if it wasn't found
		if ( pConfig == NULL )
		{
			pConfig = pGame->CreateNewKey();
			if ( pConfig == NULL )
			{
				// FIXME: fatal error
				return;
			}
		}

		// Update the changes
		Configs.GetAt(i)->Save(pConfig);
	}

	// For each global configuration, remove any configs Hammer has deleted
	bool bFoundConfig;
	KeyValues *pConfig = pGame->GetFirstTrueSubKey();
	while ( pConfig != NULL )
	{
		// Search through all the configs Hammer knows of for a matching name
		bFoundConfig = false;
		for ( int i = 0; i < nConfigs; i++ )
		{
			if ( !Q_stricmp( pConfig->GetName(), Configs.GetAt(i)->szName ) )
			{
				bFoundConfig = true;
				break;
			}
		}

		// Move along to the next config
		if ( bFoundConfig )
		{
			pConfig = pConfig->GetNextTrueSubKey();
			continue;
		}

		// Delete the configuration block if we didn't find it
		KeyValues *pNextConfig = pConfig->GetNextTrueSubKey();
		pGame->RemoveSubKey( pConfig );
		pConfig->deleteThis();
		pConfig = pNextConfig;
	}
	
	// Save the resulting changes
	mgr.SaveConfigs( m_strConfigDir );
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
COptions::COptions(void)
{
}


//-----------------------------------------------------------------------------
// Purpose: Looks for the Valve Hammer Editor registry settings and returns whether
//			they were found.
//-----------------------------------------------------------------------------
static bool HammerSettingsFound(void)
{
	bool bFound = false;
	HKEY hkeySoftware;
	if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_READ | KEY_WRITE, &hkeySoftware) == ERROR_SUCCESS)
	{
		HKEY hkeyValve;
		if (RegOpenKeyEx(hkeySoftware, "Valve", 0, KEY_READ | KEY_WRITE, &hkeyValve) == ERROR_SUCCESS)
		{
			HKEY hkeyHammer;
			if (RegOpenKeyEx(hkeyValve, "Hammer", 0, KEY_READ | KEY_WRITE, &hkeyHammer) == ERROR_SUCCESS)
			{
				HKEY hkeyConfigured;
				if (RegOpenKeyEx(hkeyHammer, "Configured", 0, KEY_READ | KEY_WRITE, &hkeyConfigured) == ERROR_SUCCESS)
				{
					bFound = true;
					RegCloseKey(hkeyConfigured);
				}
				RegCloseKey(hkeyHammer);
			}
			RegCloseKey(hkeyValve);
		}
		RegCloseKey(hkeySoftware);
	}

	return bFound;
}


//-----------------------------------------------------------------------------
// Purpose: Looks for the Valve Hammer Editor registry settings and returns whether
//			they were found.
//-----------------------------------------------------------------------------
static bool ValveHammerEditorSettingsFound(void)
{
	bool bFound = false;
	HKEY hkeySoftware;
	if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_READ | KEY_WRITE, &hkeySoftware) == ERROR_SUCCESS)
	{
		HKEY hkeyValve;
		if (RegOpenKeyEx(hkeySoftware, "Valve", 0, KEY_READ | KEY_WRITE, &hkeyValve) == ERROR_SUCCESS)
		{
			HKEY hkeyHammer;
			if (RegOpenKeyEx(hkeyValve, "Valve Hammer Editor", 0, KEY_READ | KEY_WRITE, &hkeyHammer) == ERROR_SUCCESS)
			{
				HKEY hkeyConfigured;
				if (RegOpenKeyEx(hkeyHammer, "Configured", 0, KEY_READ | KEY_WRITE, &hkeyConfigured) == ERROR_SUCCESS)
				{
					bFound = true;
					RegCloseKey(hkeyConfigured);
				}
				RegCloseKey(hkeyHammer);
			}
			RegCloseKey(hkeyValve);
		}
		RegCloseKey(hkeySoftware);
	}

	return bFound;
}


//-----------------------------------------------------------------------------
// Purpose: Looks for the Worldcraft registry settings and returns whether they
//			were found.
//-----------------------------------------------------------------------------
static bool WorldcraftSettingsFound(void)
{
	bool bFound = false;
	HKEY hkeySoftware;
	if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_READ | KEY_WRITE, &hkeySoftware) == ERROR_SUCCESS)
	{
		HKEY hkeyValve;
		if (RegOpenKeyEx(hkeySoftware, "Valve", 0, KEY_READ | KEY_WRITE, &hkeyValve) == ERROR_SUCCESS)
		{
			HKEY hkeyWorldcraft;
			if (RegOpenKeyEx(hkeyValve, "Worldcraft", 0, KEY_READ | KEY_WRITE, &hkeyWorldcraft) == ERROR_SUCCESS)
			{
				bFound = true;
				RegCloseKey(hkeyWorldcraft);
			}
			RegCloseKey(hkeyValve);
		}
		RegCloseKey(hkeySoftware);
	}

	return bFound;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool COptions::Init(void)
{
	//
	// If the we have no registry settings and the "Valve Hammer Editor" registry tree exists,
	// import settings from there. If that isn't found, try "Worldcraft".
	//
	bool bWCSettingsFound = false;
	bool bVHESettingsFound = false;

	if (!HammerSettingsFound())
	{
		bVHESettingsFound = ValveHammerEditorSettingsFound();
		if (!bVHESettingsFound)
		{
			bWCSettingsFound = WorldcraftSettingsFound();
		}
	}

	if (bVHESettingsFound)
	{
		APP()->BeginImportVHESettings();
	}
	else if (bWCSettingsFound)
	{
		APP()->BeginImportWCSettings();
	}

	SetDefaults();
	
	if (!Read())
	{
		return false;
	}

	if (bVHESettingsFound || bWCSettingsFound)
	{
		APP()->EndImportSettings();
	}

	//
	// Notify appropriate windows of new settings.
	// dvs: is all this necessary?
	//
	CMainFrame *pMainWnd = GetMainWnd();
	if (pMainWnd != NULL)
	{
		pMainWnd->SetBrightness(textures.fBrightness);

		pMainWnd->UpdateAllDocViews( MAPVIEW_OPTIONS_CHANGED );
		
		// FIXME: can't do this before the filesystem is initialized
		//pMainWnd->GlobalNotify(WM_GAME_CHANGED);
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: Enables or disables texture locking.
// Input  : b - TRUE to enable texture locking, FALSE to disable.
// Output : Returns the previous value of the texture locking flag.
//-----------------------------------------------------------------------------
BOOL COptions::SetLockingTextures(BOOL b)
{
	BOOL bOld = general.bLockingTextures;
	general.bLockingTextures = b;
	return(bOld);
}


//-----------------------------------------------------------------------------
// Purpose: Returns TRUE if texture locking is enabled, FALSE if not.
//-----------------------------------------------------------------------------
BOOL COptions::IsLockingTextures(void)
{
	return(general.bLockingTextures);
}

BOOL COptions::SetScaleLockingTextures(BOOL b)
{
	BOOL bOld = general.bScaleLockingTextures;
	general.bScaleLockingTextures = b;
	return(bOld);
}

BOOL COptions::IsScaleLockingTextures(void)
{
	return general.bScaleLockingTextures;
}

//-----------------------------------------------------------------------------
// Purpose: Returns whether new faces should be world aligned or face aligned.
//-----------------------------------------------------------------------------
TextureAlignment_t COptions::GetTextureAlignment(void)
{
	return(general.eTextureAlignment);
}


//-----------------------------------------------------------------------------
// Purpose: Sets whether new faces should be world aligned or face aligned.
// Input  : eTextureAlignment - TEXTURE_ALIGN_WORLD or TEXTURE_ALIGN_FACE.
// Output : Returns the old setting for texture alignment.
//-----------------------------------------------------------------------------
TextureAlignment_t COptions::SetTextureAlignment(TextureAlignment_t eTextureAlignment)
{
	TextureAlignment_t eOld = general.eTextureAlignment;
	general.eTextureAlignment = eTextureAlignment;
	return(eOld);
}


//-----------------------------------------------------------------------------
// Purpose: Returns whether helpers should be hidden or shown.
//-----------------------------------------------------------------------------
bool COptions::GetShowHelpers(void)
{
	return (general.bShowHelpers == TRUE);
}


//-----------------------------------------------------------------------------
// Purpose: Sets whether helpers should be hidden or shown.
//-----------------------------------------------------------------------------
void COptions::SetShowHelpers(bool bShow)
{
	general.bShowHelpers = bShow ? TRUE : FALSE;
}


//-----------------------------------------------------------------------------
// Purpose: Loads the application configuration settings.
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
bool COptions::Read(void)
{
	if (!APP()->GetProfileInt("Configured", "Configured", 0))
	{
		return false;
	}

	DWORD dwTime = APP()->GetProfileInt("Configured", "Installed", time(NULL));
	CTimeSpan ts(time(NULL) - dwTime);
	uDaysSinceInstalled = ts.GetDays();

	int i, iSize;
	CString str;

	// read texture info - it's stored in the general section from
	//  an old version, but this doesn't matter much.
	iSize = APP()->GetProfileInt(pszGeneral, "TextureFileCount", 0);
	if(iSize)
	{
		// make sure default is removed
		textures.nTextureFiles = 0;
		textures.TextureFiles.RemoveAll();
		// read texture file names
		for(i = 0; i < iSize; i++)
		{
			str.Format("TextureFile%d", i);
			str = APP()->GetProfileString(pszGeneral, str);
			if(GetFileAttributes(str) == 0xffffffff)
			{
				// can't find
				continue;
			}
			textures.TextureFiles.Add(str);
			textures.nTextureFiles++;
		}
	}
	else
	{
		// SetDefaults() added 'textures.wad' to the list
	}
	textures.fBrightness = float(APP()->GetProfileInt(pszGeneral, "Brightness", 10)) / 10.0;

	// load general info
	general.nMaxCameras = APP()->GetProfileInt(pszGeneral, "Max Cameras", 100);
	general.iUndoLevels = APP()->GetProfileInt(pszGeneral, "Undo Levels", 50);
	general.bLockingTextures = APP()->GetProfileInt(pszGeneral, "Locking Textures", TRUE);
	general.bScaleLockingTextures = APP()->GetProfileInt(pszGeneral, "Scale Locking Textures", FALSE);
	general.eTextureAlignment = (TextureAlignment_t)APP()->GetProfileInt(pszGeneral, "Texture Alignment", TEXTURE_ALIGN_WORLD);
	general.bLoadwinpos = APP()->GetProfileInt(pszGeneral, "Load Default Positions", TRUE);
	general.bIndependentwin = APP()->GetProfileInt(pszGeneral, "Independent Windows", FALSE);
	general.bEnablePerforceIntegration = APP()->GetProfileInt( pszGeneral, "Perforce Integration", TRUE );
	general.bGroupWhileIgnore = APP()->GetProfileInt(pszGeneral, "GroupWhileIgnore", FALSE);
	general.bStretchArches = APP()->GetProfileInt(pszGeneral, "StretchArches", TRUE);
	general.bShowHelpers = APP()->GetProfileInt(pszGeneral, "Show Helpers", TRUE);
	general.bCheckVisibleMapErrors = APP()->GetProfileInt(pszGeneral, "Visible Map Errors", FALSE);
	general.iTimeBetweenSaves = APP()->GetProfileInt(pszGeneral, "Time Between Saves", 15);
	general.iMaxAutosaveSpace = APP()->GetProfileInt(pszGeneral, "Max Autosave Space", 100);
	general.iMaxAutosavesPerMap = APP()->GetProfileInt(pszGeneral, "Max Saves Per Map", 5);
	general.bEnableAutosave = APP()->GetProfileInt(pszGeneral, "Autosaves Enabled", 1);
	general.bClosedCorrectly = APP()->GetProfileInt(pszGeneral, "Closed Correctly", TRUE);
	general.bShowHiddenTargetsAsBroken = APP()->GetProfileInt(pszGeneral, "Show Hidden Targets As Broken", TRUE);	
	general.bRadiusCulling = APP()->GetProfileInt(pszGeneral, "Use Radius Culling", FALSE);

	char szDefaultAutosavePath[MAX_PATH];
	strcpy( szDefaultAutosavePath, APP()->GetProfileString(pszGeneral, "Directory", "C:"));
	strcat( szDefaultAutosavePath, "\\HammerAutosave\\" );
	strcpy( general.szAutosaveDir, APP()->GetProfileString("General", "Autosave Dir", szDefaultAutosavePath));
	if ( Q_strlen( general.szAutosaveDir ) == 0 )
	{
		strcpy( general.szAutosaveDir, szDefaultAutosavePath );
	}
	APP()->SetDirectory( DIR_AUTOSAVE, general.szAutosaveDir );	

	// read view2d
	view2d.bCrosshairs = APP()->GetProfileInt(pszView2D, "Crosshairs", FALSE);
	view2d.bGroupCarve = APP()->GetProfileInt(pszView2D, "GroupCarve", TRUE);
	view2d.bScrollbars = APP()->GetProfileInt(pszView2D, "Scrollbars", TRUE);
	view2d.bRotateConstrain = APP()->GetProfileInt(pszView2D, "RotateConstrain", FALSE);
	view2d.bDrawVertices = APP()->GetProfileInt(pszView2D, "Draw Vertices", TRUE);
	view2d.bDrawModels = APP()->GetProfileInt(pszView2D, "Draw Models", TRUE);
	view2d.bWhiteOnBlack = APP()->GetProfileInt(pszView2D, "WhiteOnBlack", TRUE);
	view2d.bGridHigh1024 = APP()->GetProfileInt(pszView2D, "GridHigh1024", TRUE);
	view2d.bGridHigh10 = APP()->GetProfileInt(pszView2D, "GridHigh10", TRUE);
	view2d.bHideSmallGrid = APP()->GetProfileInt(pszView2D, "HideSmallGrid", TRUE);
	view2d.bNudge = APP()->GetProfileInt(pszView2D, "Nudge", FALSE);
	view2d.bOrientPrimitives = APP()->GetProfileInt(pszView2D, "OrientPrimitives", FALSE);
	view2d.bAutoSelect = APP()->GetProfileInt(pszView2D, "AutoSelect", FALSE);
	view2d.bSelectbyhandles = APP()->GetProfileInt(pszView2D, "SelectByHandles", FALSE);
	view2d.iGridIntensity = APP()->GetProfileInt(pszView2D, "GridIntensity", 30);
	view2d.iDefaultGrid = APP()->GetProfileInt(pszView2D, "Default Grid", 64);
	view2d.iGridHighSpec = APP()->GetProfileInt(pszView2D, "GridHighSpec", 8);
	view2d.bKeepclonegroup = APP()->GetProfileInt(pszView2D, "Keepclonegroup", TRUE);
	view2d.bGridHigh64 = APP()->GetProfileInt(pszView2D, "Gridhigh64", TRUE);
	view2d.bGridDots = APP()->GetProfileInt(pszView2D, "GridDots", FALSE);
	view2d.bCenteroncamera = APP()->GetProfileInt(pszView2D, "Centeroncamera", FALSE);
	view2d.bUsegroupcolors = APP()->GetProfileInt(pszView2D, "Usegroupcolors", TRUE);

	// read view3d
	view3d.bHardware = APP()->GetProfileInt(pszView3D, "Hardware", FALSE);
	view3d.bReverseY = APP()->GetProfileInt(pszView3D, "Reverse Y", TRUE);
	view3d.iBackPlane = APP()->GetProfileInt(pszView3D, "BackPlane", 5000);
	view3d.bUseMouseLook = APP()->GetProfileInt(pszView3D, "UseMouseLook", TRUE);
	view3d.nModelDistance = APP()->GetProfileInt(pszView3D, "ModelDistance", 400);
	view3d.nDetailDistance = APP()->GetProfileInt(pszView3D, "DetailDistance", 1200);
	view3d.bAnimateModels = APP()->GetProfileInt(pszView3D, "AnimateModels", FALSE);
	view3d.nForwardSpeedMax = APP()->GetProfileInt(pszView3D, "ForwardSpeedMax", 1000);
	view3d.nTimeToMaxSpeed = APP()->GetProfileInt(pszView3D, "TimeToMaxSpeed", 500);
	view3d.bFilterTextures = APP()->GetProfileInt(pszView3D, "FilterTextures", TRUE);
	view3d.bReverseSelection = APP()->GetProfileInt(pszView3D, "ReverseSelection", FALSE);
	view3d.fFOV = 90;
	view3d.iViewInstancesMode = APP()->GetProfileInt(pszView3D, "ViewInstancesMode", 1);
		
	ReadColorSettings();

	//
	// If we can't load any game configurations, pop up the options screen.
	//
	if (configs.LoadGameConfigs() == 0)
	{
		if (!RunConfigurationDialog())
			return false;
	}

	//
	// By default use the first config.
	//
	if (configs.nConfigs > 0)
	{
		g_pGameConfig = configs.Configs.GetAt(0);
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool COptions::RunConfigurationDialog()
{
	CString strText;
	strText.LoadString(IDS_NO_CONFIGS_AVAILABLE);
	if (MessageBox(NULL, strText, "First Time Setup", MB_ICONQUESTION | MB_YESNO) == IDYES)
	{
		APP()->OpenURL(ID_HELP_FIRST_TIME_SETUP, GetMainWnd()->GetSafeHwnd());
	}

	COptionProperties dlg("Configure Hammer");

	do
	{
		if (dlg.DoModal() != IDOK)
		{
			return false;
		}

		if (configs.nConfigs == 0)
		{
			MessageBox(NULL, "You must create at least one game configuration before using Hammer.", "First Time Setup", MB_ICONEXCLAMATION | MB_OK);
		}

	} while (configs.nConfigs == 0);

	Options.Write( TRUE, TRUE );
	return true;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void COptions::ReadColorSettings(void)
{ 
	colors.bUseCustom = (APP()->GetProfileInt(g_szColors, "UseCustom", 0) != 0);
	if (colors.bUseCustom)
	{
		colors.clrAxis = APP()->GetProfileColor(g_szColors, "Grid0", 0 , 100, 100);
		colors.bScaleAxisColor = (APP()->GetProfileInt(g_szColors, "ScaleGrid0", 0) != 0);
		colors.clrGrid = APP()->GetProfileColor(g_szColors, "Grid", 50 , 50, 50);
		colors.bScaleGridColor = (APP()->GetProfileInt(g_szColors, "ScaleGrid", 1) != 0);
		colors.clrGrid10 = APP()->GetProfileColor(g_szColors, "Grid10", 40 , 40, 40);
		colors.bScaleGrid10Color = (APP()->GetProfileInt(g_szColors, "ScaleGrid10", 1) != 0);
		colors.clrGrid1024 = APP()->GetProfileColor(g_szColors, "Grid1024", 40 , 40, 40);
		colors.bScaleGrid1024Color = (APP()->GetProfileInt(g_szColors, "ScaleGrid1024", 1) != 0);
		colors.clrGridDot = APP()->GetProfileColor(g_szColors, "GridDot", 128, 128, 128);
		colors.bScaleGridDotColor = (APP()->GetProfileInt(g_szColors, "ScaleGridDot", 1) != 0);

		colors.clrBrush = APP()->GetProfileColor(g_szColors, "LineColor", 0, 0, 0);
		colors.clrEntity = APP()->GetProfileColor(g_szColors, "Entity", 220, 30, 220);
		colors.clrVertex = APP()->GetProfileColor(g_szColors, "Vertex", 0, 0, 0);
		colors.clrBackground = APP()->GetProfileColor(g_szColors, "Background", 0, 0, 0);
		colors.clrToolHandle = APP()->GetProfileColor(g_szColors, "HandleColor", 0, 0, 0);
		colors.clrToolBlock = APP()->GetProfileColor(g_szColors, "BoxColor", 0, 0, 0);
		colors.clrToolSelection = APP()->GetProfileColor(g_szColors, "ToolSelect", 0, 0, 0);
		colors.clrToolMorph = APP()->GetProfileColor(g_szColors, "Morph", 255, 0, 0);
		colors.clrToolPath = APP()->GetProfileColor(g_szColors, "Path", 255, 0, 0);
		colors.clrSelection = APP()->GetProfileColor(g_szColors, "Selection", 220, 0, 0);
		colors.clrToolDrag = APP()->GetProfileColor(g_szColors, "ToolDrag", 255, 255, 0);
	}
	else
	{
		if (Options.view2d.bWhiteOnBlack)
		{
			// BLACK BACKGROUND
			colors.clrBackground = RGB(0, 0, 0);
			colors.clrGrid = RGB(255, 255, 255);
			colors.clrGridDot = RGB(255, 255, 255);
			colors.clrGrid1024 = RGB(100, 50, 5);
			colors.clrGrid10 = RGB(255, 255, 255);
			colors.clrAxis = RGB(0, 100, 100);
			colors.clrBrush = RGB(255, 255, 255);
			colors.clrVertex = RGB(255, 255, 255);

			colors.clrToolHandle = RGB(255, 255, 255);
			colors.clrToolBlock = RGB(255, 255, 255);
			colors.clrToolDrag = RGB(255, 255, 0);
		}
		else
		{
			// WHITE BACKGROUND
			colors.clrBackground = RGB(255, 255, 255);
			colors.clrGrid = RGB(50, 50, 50);
			colors.clrGridDot = RGB(40, 40, 40);
			colors.clrGrid1024 = RGB(200, 100, 10);
			colors.clrGrid10 = RGB(40, 40, 40);
			colors.clrAxis = RGB(0, 100, 100);
			colors.clrBrush = RGB(0, 0, 0);
			colors.clrVertex = RGB(0, 0, 0);

			colors.clrToolHandle = RGB(0, 0, 0);
			colors.clrToolBlock = RGB(0, 0, 0);
			colors.clrToolDrag = RGB(0, 0, 255);
		}

		colors.bScaleAxisColor = false;
		colors.bScaleGridColor = true;
		colors.bScaleGrid10Color = true;
		colors.bScaleGrid1024Color = false;
		colors.bScaleGridDotColor = true;

		colors.clrToolSelection = RGB(255, 255, 0);
		colors.clrSelection = RGB(255, 0, 0);
		colors.clrToolMorph = RGB(255, 0, 0);
		colors.clrToolPath = RGB(255, 0, 0);
		colors.clrEntity = RGB(220, 30, 220);
		colors.clrModelCollisionWireframe = RGB( 255, 255, 0 );
		colors.clrModelCollisionWireframeDisabled = RGB( 220, 30, 220 );
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : fOverwrite - 
//-----------------------------------------------------------------------------
void COptions::Write( BOOL fOverwrite, BOOL fSaveConfigs )
{
	APP()->WriteProfileInt("Configured", "Configured", iThisVersion);

	int i, iSize;
	CString str;

	// write texture info - remember, it's stored in general
	iSize = textures.nTextureFiles;
	APP()->WriteProfileInt(pszGeneral, "TextureFileCount", iSize);
	for(i = 0; i < iSize; i++)
	{
		str.Format("TextureFile%d", i);
		APP()->WriteProfileString(pszGeneral, str, textures.TextureFiles[i]);
	}
	APP()->WriteProfileInt(pszGeneral, "Brightness", int(textures.fBrightness * 10));

	// write general
	APP()->WriteProfileInt(pszGeneral, "Max Cameras", general.nMaxCameras);
	APP()->WriteProfileInt(pszGeneral, "Undo Levels", general.iUndoLevels);
	APP()->WriteProfileInt(pszGeneral, "Locking Textures", general.bLockingTextures);
	APP()->WriteProfileInt(pszGeneral, "Scale Locking Textures", general.bScaleLockingTextures);
	APP()->WriteProfileInt(pszGeneral, "Texture Alignment", general.eTextureAlignment);
	APP()->WriteProfileInt(pszGeneral, "Independent Windows", general.bIndependentwin);
	APP()->WriteProfileInt( pszGeneral, "Perforce Integration", general.bEnablePerforceIntegration );
	APP()->WriteProfileInt(pszGeneral, "Load Default Positions", general.bLoadwinpos);
	APP()->WriteProfileInt(pszGeneral, "GroupWhileIgnore", general.bGroupWhileIgnore);
	APP()->WriteProfileInt(pszGeneral, "StretchArches", general.bStretchArches);
	APP()->WriteProfileInt(pszGeneral, "Show Helpers", general.bShowHelpers);
	APP()->WriteProfileInt(pszGeneral, "Visible Map Errors", general.bCheckVisibleMapErrors);
	APP()->WriteProfileInt(pszGeneral, "Time Between Saves", general.iTimeBetweenSaves);
	APP()->WriteProfileInt(pszGeneral, "Max Autosave Space", general.iMaxAutosaveSpace);
	APP()->WriteProfileInt(pszGeneral, "Max Saves Per Map", general.iMaxAutosavesPerMap);
	APP()->WriteProfileInt(pszGeneral, "Autosaves Enabled", general.bEnableAutosave);
	APP()->WriteProfileInt(pszGeneral, "Closed Correctly", general.bClosedCorrectly);
	APP()->WriteProfileString(pszGeneral, "Autosave Dir", general.szAutosaveDir);
	APP()->SetDirectory( DIR_AUTOSAVE, general.szAutosaveDir );
	APP()->WriteProfileInt(pszGeneral, "Show Hidden Targets As Broken", general.bShowHiddenTargetsAsBroken);
	APP()->WriteProfileInt(pszGeneral, "Use Radius Culling", general.bRadiusCulling);


	
	// write view2d
	APP()->WriteProfileInt(pszView2D, "Crosshairs", view2d.bCrosshairs);
	APP()->WriteProfileInt(pszView2D, "GroupCarve", view2d.bGroupCarve);
	APP()->WriteProfileInt(pszView2D, "Scrollbars", view2d.bScrollbars);
	APP()->WriteProfileInt(pszView2D, "RotateConstrain", view2d.bRotateConstrain);
	APP()->WriteProfileInt(pszView2D, "Draw Vertices", view2d.bDrawVertices);
	APP()->WriteProfileInt(pszView2D, "Draw Models", view2d.bDrawModels);
	APP()->WriteProfileInt(pszView2D, "Default Grid", view2d.iDefaultGrid);
	APP()->WriteProfileInt(pszView2D, "WhiteOnBlack", view2d.bWhiteOnBlack);
	APP()->WriteProfileInt(pszView2D, "GridHigh1024", view2d.bGridHigh1024);
	APP()->WriteProfileInt(pszView2D, "GridHigh10", view2d.bGridHigh10);
	APP()->WriteProfileInt(pszView2D, "GridIntensity", view2d.iGridIntensity);
	APP()->WriteProfileInt(pszView2D, "HideSmallGrid", view2d.bHideSmallGrid);
	APP()->WriteProfileInt(pszView2D, "Nudge", view2d.bNudge);
	APP()->WriteProfileInt(pszView2D, "OrientPrimitives", view2d.bOrientPrimitives);
	APP()->WriteProfileInt(pszView2D, "AutoSelect", view2d.bAutoSelect);
	APP()->WriteProfileInt(pszView2D, "SelectByHandles", view2d.bSelectbyhandles);
	APP()->WriteProfileInt(pszView2D, "GridHighSpec", view2d.iGridHighSpec);
	APP()->WriteProfileInt(pszView2D, "KeepCloneGroup", view2d.bKeepclonegroup);
	APP()->WriteProfileInt(pszView2D, "Gridhigh64", view2d.bGridHigh64);
	APP()->WriteProfileInt(pszView2D, "GridDots", view2d.bGridDots);
	APP()->WriteProfileInt(pszView2D, "Centeroncamera", view2d.bCenteroncamera);
	APP()->WriteProfileInt(pszView2D, "Usegroupcolors", view2d.bUsegroupcolors);

	// write view3d
	APP()->WriteProfileInt(pszView3D, "Hardware", view3d.bHardware);
	APP()->WriteProfileInt(pszView3D, "Reverse Y", view3d.bReverseY);
	APP()->WriteProfileInt(pszView3D, "BackPlane", view3d.iBackPlane);
	APP()->WriteProfileInt(pszView3D, "UseMouseLook", view3d.bUseMouseLook);
	APP()->WriteProfileInt(pszView3D, "ModelDistance", view3d.nModelDistance);
	APP()->WriteProfileInt(pszView3D, "DetailDistance", view3d.nDetailDistance);
	APP()->WriteProfileInt(pszView3D, "AnimateModels", view3d.bAnimateModels);
	APP()->WriteProfileInt(pszView3D, "ForwardSpeedMax", view3d.nForwardSpeedMax);
	APP()->WriteProfileInt(pszView3D, "TimeToMaxSpeed", view3d.nTimeToMaxSpeed);
	APP()->WriteProfileInt(pszView3D, "FilterTextures", view3d.bFilterTextures);
	APP()->WriteProfileInt(pszView3D, "ReverseSelection", view3d.bReverseSelection);
	APP()->WriteProfileInt(pszView3D, "ViewInstancesMode", view3d.iViewInstancesMode);

	//
	// We don't write custom color settings because there is no GUI for them yet.
	//

	// Write out the game configurations
	if ( fSaveConfigs )
	{
		configs.SaveGameConfigs();
	}
}


void COptions::SetClosedCorrectly(BOOL bClosed)
{
	APP()->WriteProfileInt( pszGeneral, "Closed Correctly", bClosed );	
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void COptions::SetDefaults(void)
{
	BOOL bWrite = FALSE;

	if (APP()->GetProfileInt("Configured", "Configured", 0) != iThisVersion)
	{
		bWrite = TRUE;
	}

	if (APP()->GetProfileInt("Configured", "Installed", 42151) == 42151)
	{
		APP()->WriteProfileInt("Configured", "Installed", time(NULL));
	}

	uDaysSinceInstalled = 0;

	// textures
	textures.nTextureFiles = 1;
	textures.TextureFiles.Add("textures.wad");
	textures.fBrightness = 1.0;

	// general
	general.bIndependentwin = FALSE;
	general.bEnablePerforceIntegration = TRUE;
	general.bLoadwinpos = TRUE;
	general.iUndoLevels = 50;
	general.nMaxCameras = 100;
	general.bGroupWhileIgnore = FALSE;
	general.bStretchArches = TRUE;
	general.bLockingTextures = TRUE;
	general.bScaleLockingTextures = FALSE;
	general.bShowHelpers = TRUE;
	general.iTimeBetweenSaves = 15;
	general.iMaxAutosaveSpace = 100;
	general.iMaxAutosavesPerMap = 5;
	general.bEnableAutosave = TRUE;
	general.bClosedCorrectly = TRUE;
	general.bShowCollisionModels = FALSE;
	general.bShowDetailObjects = TRUE;
	general.bShowNoDrawBrushes = TRUE;
	
	// view2d
	view2d.bCrosshairs = FALSE;
	view2d.bGroupCarve = TRUE;
	view2d.bScrollbars = TRUE;
	view2d.bRotateConstrain = FALSE;
	view2d.bDrawVertices = TRUE;
	view2d.bDrawModels = TRUE;
	view2d.iDefaultGrid = 64;
	view2d.bWhiteOnBlack = TRUE;
	view2d.bGridHigh1024 = TRUE;
	view2d.bGridHigh10 = TRUE;
	view2d.iGridIntensity = 30;
	view2d.bHideSmallGrid = TRUE;
	view2d.bNudge = FALSE;
	view2d.bOrientPrimitives = FALSE;
	view2d.bAutoSelect = FALSE;
	view2d.bSelectbyhandles = FALSE;
	view2d.iGridHighSpec = 8;
	view2d.bKeepclonegroup = TRUE;
	view2d.bGridHigh64 = FALSE;
	view2d.bGridDots = FALSE;
	view2d.bCenteroncamera = FALSE;
	view2d.bUsegroupcolors = TRUE;

	// view3d
	view3d.bUseMouseLook = TRUE;
	view3d.bHardware = FALSE;
	view3d.bReverseY = FALSE;
	view3d.iBackPlane = 5000;
	view3d.nModelDistance = 400;
	view3d.nDetailDistance = 1200;
	view3d.bAnimateModels = FALSE;
	view3d.nForwardSpeedMax = 1000;
	view3d.nTimeToMaxSpeed = 500;
	view3d.bFilterTextures = TRUE;
	view3d.bReverseSelection = FALSE;
	view3d.bPreviewModelFade = false;
	view3d.iViewInstancesMode = 1;

	if ( bWrite )
	{
		Write( FALSE, FALSE );
	}
}


//-----------------------------------------------------------------------------
// Purpose: This is called by the user interface itself when changes are made. 
//			tells the COptions object to notify the parts of the interface.
// Input  : dwOptionsChanged - Flags indicating which options changed.
//-----------------------------------------------------------------------------
void COptions::PerformChanges(DWORD dwOptionsChanged)
{
	CMainFrame *pMainWnd = GetMainWnd();

	if (dwOptionsChanged & secTextures)
	{
		if (pMainWnd != NULL)
		{
			pMainWnd->SetBrightness(textures.fBrightness);
		}
	}

	if (dwOptionsChanged & secView2D)
	{
		ReadColorSettings();

		if (pMainWnd != NULL)
		{
			pMainWnd->UpdateAllDocViews( MAPVIEW_UPDATE_ONLY_2D | MAPVIEW_OPTIONS_CHANGED | MAPVIEW_RENDER_NOW );
		}
	}

	if (dwOptionsChanged & secView3D)
	{
		if (pMainWnd != NULL)
		{
			pMainWnd->UpdateAllDocViews(MAPVIEW_UPDATE_ONLY_3D | MAPVIEW_OPTIONS_CHANGED | MAPVIEW_RENDER_NOW );
		}
	}

	if (dwOptionsChanged & secConfigs)
	{
		if (pMainWnd != NULL)
		{
			pMainWnd->GlobalNotify(WM_GAME_CHANGED);
		}
	}
}