/*****************************************************************************
 * $Id: mod_base_init.cc,v 1.1 2006/11/24 21:56:57 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Initialization.
 * @verbatim
 *
 * Module startup:
 * ---------------
 * 
 * The startup of a module is separated into the distinct steps
 * of construction, initialization, and starting.
 *
 * 1. Module constructors should be a NOP.
 *
 *    Why?  Because the global Module object (gModule) is a static.
 *    Program load, when statics are initialized, is too early.
 *
 * 2. main() calls Module::Init() then Engine::Init().
 *
 *    int
 *    main( int argc, char** argv )
 *    {
 *        gModule.Init( argc, argv );
 *        return gEng.Init( argc, argv, PROGRAM_NAME_STR );
 *    }
 *
 *    Why this order?  Why not start the engine first?
 *    Because the engine broadcasts events -- the Module must have listeners ready.
 *    But since the engine is ready yet, Module::Init() should just do
 *    basic initialization without starting the module.
 *
 * 3. During its initialization, the engine will call World::Make().
 *
 *    World is a global object managed by the module
 *    But the engine doesn't know who makes it.
 *
 * 4. The engine will broadcast an "engine ready" event.
 *
 * 5. The base Module class has a listener for "engine ready": Listener_Module_EngineReady().
 *    It starts the Module by calling the virtual Module::Start()
 *    which is overridden by a Module.
 *
 * 6. Module::Start() will execute.  Since it was called by a listener
 *    for an event from the engine, when Start() returns, the engine code will resume.
 *
 * 7. The engine transfers execution to the underlying graphics system (gfxsys).
 *
 * Initialization is done.
 *
 * @endverbatim
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#define MOD_BASE_INIT_CC 1
#include "mod_base_common.hh"
#include "mod_base_joystick.hh"

/*****************************************************************************
 * Module ctor/dtor
 *****************************************************************************/
Module::Module( void )
:   mCraftDlist( Dlist<Craft>::KEEP ),  // let ~Quadrant() delete Objects
    mDynaModelTargetDlist( Dlist<DynaModel>::KEEP ),
    mTracerDlist( Dlist<Tracer>::KEEP )
{
    // NOP
}
Module::~Module()
{
    // NOP
}

/*****************************************************************************
 * Initialize.
 * @return 0 if successful (UNIX exit convention).
 *****************************************************************************/
int
Module::Init( int argc, char** argv, const string& appname )
{
					///////////////////////////
					// ENGINE IS NOT READY!! //
					///////////////////////////

	GFX_INIT( appname );

	//mCollisions	= true;			// wrong, use EnableCollisions() which informs engine
	EnableCollisions( true );
	mDemoMode				= false;
	mTexturing				= true;
	mControlResponseEye		= CONTROL_RESPONSE_PERCENT_DEFAULT;  // percent
	mControlResponseChase	= CONTROL_RESPONSE_PERCENT_DEFAULT;
	Getcwd( mCwd );					// this early value should be used as glutInit() calls chdir()
	mPlayer					= NULL;
	mPlayer1Craft			= NULL;
	mPlayer2Craft			= NULL;
	mPlayer1				= NULL;
	mPlayer2				= NULL;
	mDefaultWindowWidth		= MAIN_WINDOW_WIDTH;
	mDefaultWindowHeight	= MAIN_WINDOW_HEIGHT;

	StartStopListeners( true );

	return 0;
}

/*****************************************************************************
 * Process command-line args.
 *****************************************************************************/
void
Module::Cmdarg( int argc, char** argv )
{
#define JOY_ARG()				{ gJoy.Enable( true ); mCmdargBase.mJoy = true; }
#define JOY_ARG_AXIS3(AXIS3)	{ JOY_ARG(); mCmdargBase.mJoyAxis3 = true; gJoy.SetJoystickAxis3( AXIS3 ); }
#define JOY_ARG_CALIBRATION()	{ JOY_ARG(); mCmdargBase.mJoyCalibration = true; }

	mCmdargBase.mArgc  = argc;
	mCmdargBase.mArgv  = argv;
	mCmdargBase.mArgv0 = argv[0];

	// Parse args.
	for ( int i = 1; i < argc; ++i )
	{
		// -j (enable joystick)?
		if ( STREQ( argv[i], "-j" ) )
		{
			JOY_ARG();
			CmdargDone( i );
		}
		// Print joystick values (to help user to calibrate)?
		else if ( STREQ( argv[i], "-jp" ) )
		{
			JOY_ARG();
			gJoy.EnablePrintJoystick( true );
			CmdargDone( i );
		}
		// Joystick third axis (wheel) controls throttle or rudder?
		else if ( STREQ( argv[i], "-j3=throttle" ) )
		{
			JOY_ARG_AXIS3( eJoyAxis3_THROTTLE );
			CmdargDone( i );
		}
		else if ( STREQ( argv[i], "-j3=rudder" ) )
		{
			JOY_ARG_AXIS3( eJoyAxis3_RUDDER );
			CmdargDone( i );
		}
		else if ( STREQ( argv[i], "-j3=none" ) )
		{
			JOY_ARG_AXIS3( eJoyAxis3_DISABLED );
			CmdargDone( i );
		}
		// Joystick response?
		else if ( STREQ( argv[i], "-jr" ) )
		{
			JOY_ARG_CALIBRATION();
			float r0 = Argf(i+1,argc,argv);
			float r1 = Argf(i+2,argc,argv);
			float r2 = Argf(i+3,argc,argv);
			// Ensure args are good as SetJoystickResponse() doesn't.
			gJoy.SetJoystickResponse( r0 ? r0 : 1.0,
                                      r1 ? r1 : 1.0,
                                      r2 ? r2 : 1.0 );
			mCmdargBase.mJoyResponse = true;
			CmdargDone( i, 4 );
			i += 3;					// note the order: CmdargDone() before incrementing i
		}
		// Joystick calibration?
		// -jmin left right up down slow fast
		// -jmax left right up down slow fast
		else if ( STREQ( argv[i], "-jmin" ) )
		{
			JOY_ARG_CALIBRATION();
			// joy.CalibrateMin/Max() can tolerate bad args.
			gJoy.CalibrateMin( Argi(i+1,argc,argv),   // left
                               Argi(i+2,argc,argv),   // right
                               Argi(i+3,argc,argv),   // up
                               Argi(i+4,argc,argv),   // down
                               Argi(i+5,argc,argv),   // slow
                               Argi(i+6,argc,argv) ); // fast
			CmdargDone( i, 7 );
			i += 6;
		}
		else if ( STREQ( argv[i], "-jmax" ) )
		{
			JOY_ARG_CALIBRATION();
			// joy.CalibrateMin/Max() can tolerate bad args.
			gJoy.CalibrateMax( Argi(i+1,argc,argv),   // left
                               Argi(i+2,argc,argv),   // right
                               Argi(i+3,argc,argv),   // up
                               Argi(i+4,argc,argv),   // down
                               Argi(i+5,argc,argv),   // slow
                               Argi(i+6,argc,argv) ); // fast
			CmdargDone( i, 7 );
			i += 6;
		}
		// Set joystick deadzone/center: -jdz n1 n2 n3
		else if ( STREQ(argv[i],"-jdz") )
		{
			JOY_ARG_CALIBRATION();
			static int joystickDeadzone[3];
			joystickDeadzone[0] = Argi(i+1,argc,argv);
			joystickDeadzone[1] = Argi(i+2,argc,argv);
			joystickDeadzone[2] = Argi(i+3,argc,argv);
			// gJoy.CalibrateDeadzone() can tolerate bad args.
			gJoy.CalibrateDeadzone( joystickDeadzone );
			CmdargDone( i, 4 );
			i += 3;
		}
		// Set joystick directions: -jd +-1 +-1 +-1
		else if ( STREQ(argv[i],"-jd") )
		{
			JOY_ARG_CALIBRATION();
			static int joystickDirection[3];
			joystickDirection[0] = Argi(i+1,argc,argv);
			joystickDirection[1] = Argi(i+2,argc,argv);
			joystickDirection[2] = Argi(i+3,argc,argv);
			// gJoy.CalibrateDirection() can tolerate bad args.
			gJoy.CalibrateDirection( joystickDirection );
			CmdargDone( i, 4 );
			i += 3;
		}
		// Set joystick autofire.
		else if ( STREQ(argv[i],"-jautofire") )
		{
			JOY_ARG();
			gJoy.EnableJoystickAutofire( true );
			CmdargDone( i );
		}
		// Collisions.
		else if ( STREQ( argv[i], "-collisions=on" ) || STREQ( argv[i], "-collision=on" ) )
		{
			mCmdargBase.mCollisions = true;
			EnableCollisions( true );
			CmdargDone( i );
		}
		else if (    STREQ( argv[i], "-collisions=off" ) || STREQ( argv[i], "-collision=off" )
                  || STREQ( argv[i], "-no-collisions" )  || STREQ( argv[i], "-no-collision" ) )
		{
			mCmdargBase.mCollisions = true;
			EnableCollisions( false );
			CmdargDone( i );
		}
		// Texturing.
		else if ( STREQ( argv[i], "-textures=off" ) || STREQ( argv[i], "-no-textures" ) )
		{
			SetTexturing( false );
			CmdargDone( i );
		}
		else if ( STREQ( argv[i], "-textures=on" ) || STREQ( argv[i], "-textures" ) )
		{
			SetTexturing( true );
			CmdargDone( i );
		}
		// Configuration filename?
		else if ( STREQ( argv[i], "-config" ) )
		{
			mConfigFilename = Argstr(i+1,argc,argv);

			// Was filename omitted and possibly an arg is where it would be?
			if ( mConfigFilename == "" || mConfigFilename == "-" )
			{
				mConfigFilename = "";
				*gStreamError << "ERROR: no filename passed after -config" << endl;
				continue;
			}
			CmdargDone( i, 2 );
			i += 1;					// note the order: CmdargDone() before incrementing i
		}
	}

	// ---------------------- cmd-args done ------------------------------------

	if ( gJoy.IfEnabled() && gJoy.IfPrintJoystick() )
	{
		float r[3]; gJoy.GetJoystickResponse( &r[0], &r[1], &r[2] );
		*gStreamOut << "joystick calibration:" << endl
              << "min: "
			  << "left="  << setw(5) << setfill('0') << ABS(gJoy.mCalibration.mLeft.mLo)  << ' '
			  << "right=" << setw(5) << setfill('0') << ABS(gJoy.mCalibration.mRight.mLo) << ' '
			  << "up="    << setw(5) << setfill('0') << ABS(gJoy.mCalibration.mUp.mLo)    << ' '
			  << "down="  << setw(5) << setfill('0') << ABS(gJoy.mCalibration.mDown.mLo)  << ' '
			  << "slow="  << setw(5) << setfill('0') << ABS(gJoy.mCalibration.mSlow.mLo)  << ' '
			  << "fast="  << setw(5) << setfill('0') << ABS(gJoy.mCalibration.mFast.mLo)  << endl
              << "max: "
			  << "left="  << setw(5) << setfill('0') << ABS(gJoy.mCalibration.mLeft.mHi)  << ' '
			  << "right=" << setw(5) << setfill('0') << ABS(gJoy.mCalibration.mRight.mHi) << ' '
			  << "up="    << setw(5) << setfill('0') << ABS(gJoy.mCalibration.mUp.mHi)    << ' '
			  << "down="  << setw(5) << setfill('0') << ABS(gJoy.mCalibration.mDown.mHi)  << ' '
			  << "slow="  << setw(5) << setfill('0') << ABS(gJoy.mCalibration.mSlow.mHi)  << ' '
			  << "fast="  << setw(5) << setfill('0') << ABS(gJoy.mCalibration.mFast.mHi)  << endl;
		int pr = (*gStreamOut).precision(3);
		*gStreamOut << "joystick response = " << r[0] << r[1] << r[2] << endl;
		(*gStreamOut).precision(pr);
	}
	if ( mCmdargBase.mJoy && ! gJoy.IfEnabled() )
		{ *gStreamError << "(joystick unavailable or unsupported)" << endl; }
}
