/*****************************************************************************
 * $Id: game_base_config.cc,v 1.14 2006/08/03 02:17:21 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Configuration, load/state state.
 * @remarks	Saving float values with double precision is necessary.
 *			Even a large unsigned 32-bit value cannot be exactly held
 *			in a normal float without round-off loss.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#define GAME_BASE_CONFIG_CC 1
#include "game_base_common.hh"
#include "game_base_config.hh"
#include "game_joystick.hh"
#include "game_base_craft.hh"
#include "game_chase_plane.hh"
#include "gfx_config_file.hh"
#include "gfx_stream.hh"

string cfgkey_lod				= "lod";		// user-selected LOD
string cfgkey_seed				= "seed";
string cfgkey_window_width		= "window_width";
string cfgkey_window_height		= "window_height";
string cfgkey_collisions		= "collisions";
string cfgkey_response_eye		= "response_eye";
string cfgkey_joystick			= "joystick";
string cfgkey_joystick_axis3	= "joystick_axis3";
string cfgkey_joy_left_min		= "joy_left_min";
string cfgkey_joy_left_max		= "joy_left_max";
string cfgkey_joy_right_min		= "joy_right_min";
string cfgkey_joy_right_max		= "joy_right_max";
string cfgkey_joy_up_min		= "joy_up_min";
string cfgkey_joy_up_max		= "joy_up_max";
string cfgkey_joy_down_min		= "joy_down_min";
string cfgkey_joy_down_max		= "joy_down_max";
string cfgkey_joy_slow_min		= "joy_axis3_slow_min";
string cfgkey_joy_slow_max		= "joy_axis3_slow_max";
string cfgkey_joy_fast_min		= "joy_axis3_fast_min";
string cfgkey_joy_fast_max		= "joy_axis3_fast_max";
string cfgkey_prefix_eye		= "eye_";

/*****************************************************************************
 * Save state to configuration file.
 * @note This is called by a game module's SaveState().
 *****************************************************************************/
bool
Game::SaveStateBase( bool saveView, ConfigFile& config )
{
	// - - - - save non-view state - - - - - - - - - - - - - - - - -

	if ( ! config.Write( cfgkey_lod, gEng.LodUserString() ) )
		return false;
	config.Write( cfgkey_collisions,		(int)IfCollisions() );
	config.Write( cfgkey_joystick,         (int)gJoy.IfEnabled() );
	config.Write( cfgkey_joystick_axis3,	(int)gJoy.GetJoystickAxis3() );
	config.Write( cfgkey_response_eye,		(int)mControlResponseEye );

	// - - - - only save view state follows - - - - - - - - - - - - -

	// Save random seed.
	if ( ! config.Write( cfgkey_seed, (int)gEng.GetSeed() ) )
		return false;

	// Save window geometry.
	config.Write( cfgkey_window_width,  (int)gEng.mView.GetWidth() );
	config.Write( cfgkey_window_height, (int)gEng.mView.GetHeight() );

	ConfigSaveDyna( config, cfgkey_prefix_eye, gEng.mEye, (double)0.0 );

	return true;
}

/*****************************************************************************
 * Load state from configuration file.
 * @note This is called by a game module's SaveState().
 *****************************************************************************/
bool
Game::LoadStateBase( bool loadView, ConfigFile& config )
{
	// Read() will normally fail if the configuration
	// is revised but an older configuration file is read.

	int i;
	//double d;
	string s;

	if ( loadView )
	{
		// Load random seed.
		if ( ! config.Read( cfgkey_seed, i ) )
			return false;
		gEng.SetSeed( i );

		// Load window geometry.
		bool res1 = config.Read( cfgkey_window_width,  mDefaultWindowWidth  );
		bool res2 = config.Read( cfgkey_window_height, mDefaultWindowHeight );
		if ( ! res1 || ! res2 ) return false;

		// Deferred loading: Load eye/player configuration into an interim struct
		// because the Player objects (Game::mPlayer) haven't been created yet.
		ConfigLoadDyna( config, cfgkey_prefix_eye, mEyeConfigState );
	}

	if ( ! mCmdargBase.mCollisions )
	{
		if ( config.Read( cfgkey_collisions, i ) )
			EnableCollisions( i );
	}

	if ( ! gEng.mCmdarg.mLodUser )
	{
		if ( config.Read( cfgkey_lod, s ) )
			gEng.SetLodUser( s );
	}

	if ( config.Read( cfgkey_joystick,	i ) )
		gJoy.Enable( i );

	if ( ! mCmdargBase.mJoyAxis3 )
	{
		if ( config.Read( cfgkey_joystick_axis3, i ) )
			gJoy.SetJoystickAxis3( (eJoyAxis3)i );
	}

	// ----- Load joystick calibration -------
	if ( ! mCmdargBase.mJoyCalibration )
	{
		if ( config.Read( cfgkey_joy_left_min, i ) )		// left min
			gJoy.mCalibration.mLeft.mLo = i;

		if ( config.Read( cfgkey_joy_left_max, i ) )		// left max
			gJoy.mCalibration.mLeft.mHi = i;

		if ( config.Read( cfgkey_joy_right_min, i ) )		// right min
			gJoy.mCalibration.mRight.mLo = i;

		if ( config.Read( cfgkey_joy_right_max, i ) )		// right max
			gJoy.mCalibration.mRight.mHi = i;

		if ( config.Read( cfgkey_joy_up_min, i ) )			// up min
			gJoy.mCalibration.mUp.mLo = i;

		if ( config.Read( cfgkey_joy_up_max, i ) )			// up max
			gJoy.mCalibration.mUp.mHi = i;

		if ( config.Read( cfgkey_joy_down_min, i ) )		// down min
			gJoy.mCalibration.mDown.mLo = i;

		if ( config.Read( cfgkey_joy_down_max, i ) )		// down max
			gJoy.mCalibration.mDown.mHi = i;

		if ( config.Read( cfgkey_joy_slow_min, i ) )		// axis3 slow min
			gJoy.mCalibration.mSlow.mLo = i;

		if ( config.Read( cfgkey_joy_slow_max, i ) )		// axis3 slow max
			gJoy.mCalibration.mSlow.mHi = i;

		if ( config.Read( cfgkey_joy_fast_min, i ) )		// axis3 fast min
			gJoy.mCalibration.mFast.mLo = i;

		if ( config.Read( cfgkey_joy_fast_max, i ) )		// axis3 fast max
			gJoy.mCalibration.mFast.mHi = i;
	}

	if ( config.Read( cfgkey_response_eye, i ) )
		mEyeConfigState.SetControlResponse( i );

	return true;
}

/*****************************************************************************
 * Save/load Dyna objects.
 *****************************************************************************/
void
ConfigSaveDyna( ConfigFile& config, const string& prefix, const Dyna& dyna, double chaseDist )
{
	MatrixExt xm; ConfigCopyMatrix( xm, dyna );	// ConfigCopyDynaMatrix() is a friend of Dyna class
	config.Write( prefix+"Xx", (double)xm[Xx] );
	config.Write( prefix+"Xy", (double)xm[Xy] );
	config.Write( prefix+"Xz", (double)xm[Xz] );
	config.Write( prefix+"Yx", (double)xm[Yx] );
	config.Write( prefix+"Yy", (double)xm[Yy] );
	config.Write( prefix+"Yz", (double)xm[Yz] );
	config.Write( prefix+"Zx", (double)xm[Zx] );
	config.Write( prefix+"Zy", (double)xm[Zy] );
	config.Write( prefix+"Zz", (double)xm[Zz] );
	config.Write( prefix+"Ox", (double)xm[Ox] );
	config.Write( prefix+"Oy", (double)xm[Oy] );
	config.Write( prefix+"Oz", (double)xm[Oz] );
	config.Write( prefix+"chaseDist", (double)chaseDist );
}
void
ConfigLoadDyna( ConfigFile& config, const string& prefix, DynaConfigState& dynaConfigState )
{
	double d;
	static MatrixExt xm; // static to suppress compiler warning
	double chasePlaneDistance;
	config.Read( prefix+"Xx", d ); xm[Xx] = d;
	config.Read( prefix+"Xy", d ); xm[Xy] = d;
	config.Read( prefix+"Xz", d ); xm[Xz] = d;
	config.Read( prefix+"Yx", d ); xm[Yx] = d;
	config.Read( prefix+"Yy", d ); xm[Yy] = d;
	config.Read( prefix+"Yz", d ); xm[Yz] = d;
	config.Read( prefix+"Zx", d ); xm[Zx] = d;
	config.Read( prefix+"Zy", d ); xm[Zy] = d;
	config.Read( prefix+"Zz", d ); xm[Zz] = d;
	config.Read( prefix+"Ox", d ); xm[Ox] = d;
	config.Read( prefix+"Oy", d ); xm[Oy] = d;
	config.Read( prefix+"Oz", d ); xm[Oz] = d;
	config.Read( prefix+"chaseDist", chasePlaneDistance );
	dynaConfigState.SetXm( xm );
	dynaConfigState.SetChasePlaneDistance( chasePlaneDistance );
}
// ConfigCopyDynaMatrix() is a friend of Dyna class.
void
ConfigCopyMatrix( MatrixExt& dest, const Dyna& dyna )
{
	CopyMatrix( dest, dyna.mXm );
}

/*****************************************************************************
 * Wrapper for ConfigFile object.
 *****************************************************************************/
static Ptr<ConfigFile> sConfig;
ConfigFile&
Game::GetConfigFileBase( const string& appname )
{
	if ( sConfig == NULL )
	{
		// If -config <fname> wasn't passed, mConfigFilename will be empty.
		sConfig = new ConfigFile( appname, mConfigFilename );
	}
	return *sConfig;
}
void
Game::CloseConfigFile( void )
{
    // Really flush it, there's no Close() method.
    if ( sConfig != NULL ) sConfig->Flush();
}

/*****************************************************************************
 * DynaConfigState.
 *****************************************************************************/
DynaConfigState::DynaConfigState( void )
{
	mXmValid = mChasePlaneDistanceValid = mControlResponseValid = false;
	IdentityMatrix( mXm );
	mChasePlaneDistance = 0.0;
	mControlResponse = 100;
}
void
DynaConfigState::SetXm( const MatrixExt& xm )
{
	mXmValid = true;
	CopyMatrix( mXm, xm );
}
void
DynaConfigState::SetChasePlaneDistance( double chasePlaneDistance )
{
	mChasePlaneDistanceValid = true;
	mChasePlaneDistance = chasePlaneDistance;
}
void
DynaConfigState::SetControlResponse( Percent controlResponse )
{
	mControlResponseValid = true;
	mControlResponse = controlResponse;
}
void
DynaConfigState::LoadDyna( Dyna& dyna )
{
	if ( mXmValid ) dyna.Reload( mXm );

	if ( mChasePlaneDistanceValid ) dyna.mChasePlane->SetDistance( mChasePlaneDistance );

	if ( mControlResponseValid ) gGame.SetControlResponse( dyna, mControlResponse );
}
