/****************************/
/*			MAIN.C			*/
/* (c)2004 Pangea Software  */
/* By Brian Greenstone      */
/****************************/


/****************************/
/*    PROTOTYPES            */
/****************************/

static void InitApplication(void);
static void GetGameNib(void);
static MOGroupObject *CreateAnObject(short bg3dGroup, short bg3dModel, float scale, OGLPoint3D *coord);
static void MoveObjects(void);


/**************************/
/*    EXTERNS             */
/**************************/

extern	CGDirectDisplayID			gCGDisplayID;
extern	int							gFrameCounter, gMouseDeltaX;
extern	GLuint						gVertexArrayRangeObjects[];
extern	float						gFramesPerSecond, gOneOverFramesPerSecond;
extern	float						gGammaBrightness;
extern	long						gMouseWheelDelta;
extern	PrefsType					gGamePrefs;
extern	InputNeedType				gControlNeeds[];
extern	MetaObjectPtr				gBG3DGroupList[NUM_MODEL_GROUPS][MAX_OBJECTS_IN_GROUP];
extern	OGLPoint3D					gCamera_Coord, gCamera_LookAt;
extern	OGLVector3D					gCamera_UpVec;
extern	Byte						gStereoPass;

/**************************/
/*    CONSTANTS           */
/**************************/

typedef struct
{
	OGLPoint3D		points[8];
	OGLColorRGBA	colors[8];
	GLint			quads[6*4];

}CubeDataType;

/****************************/
/*    VARIABLES             */
/****************************/

Boolean				gQuitApplication = false;

CFBundleRef 		gBundle = nil;
IBNibRef 			gNibs = nil;

Boolean				gPantherOrBetter = false;

short				gPrefsFolderVRefNum;
long				gPrefsFolderDirID;


MOGroupObject		*gRaptorObject;




/******************** INIT APPLICATION ************************/
//
// Called from main() to setup all the initial application stuff.
// 

static void InitApplication(void)
{
	NumVersion	vers;
	Boolean		oldOS = false;
	OSErr		iErr;
	long		createdDirID;

			/* DO SOME RESOURCE INITS */
			
	SetDefaultDirectory();						// set app's default directory
	GetGameNib();								// get ref to our .nib file
	GetMyApplicationResourcesFolder();			// get FSSpec and FSRef to our Resources folder
	
	
			/* FIND USER'S PREFERENCES FOLDER */
			
	iErr = FindFolder(kOnSystemDisk,kPreferencesFolderType,kDontCreateFolder,		// locate the folder
					&gPrefsFolderVRefNum,&gPrefsFolderDirID);
	if (iErr != noErr)
		DoAlert("\pCannot locate the Preferences folder.");

	DirCreate(gPrefsFolderVRefNum,gPrefsFolderDirID,"\pShutterDemo",&createdDirID);		// make "ShutterDemo" folder in there

	
		/* DETERMINE IF RUNNING ON AT LEAST OS 10.3 (Panther) */
		//
		// We need to know this to work around bugs in the HID Manager
		//

	Gestalt(gestaltSystemVersion,(long *)&vers);
				
	if (vers.stage >= 0x10)													// see if at least OS 10
	{
		if ((vers.stage == 0x10) && (vers.nonRelRev < 0x20))				// must be at least OS 10.2 !!!
			oldOS = true; 
		else
		{
			if ((vers.stage == 0x10) && (vers.nonRelRev >= 0x30))			// see if 10.3 or higher
				gPantherOrBetter = true;
			else
				gPantherOrBetter = false;
		}
	}
	else
		oldOS = true; 
	
	if (oldOS)
		DoFatalAlert("\pThis game requires at least MacOS 10.2.6."); 
	
	
	
			/* GET THE LIST OF HID DEVICES & ELEMENTS */
			
	InitMyHIDManagerStuff();					// must do before loading prefs

			/* INIT & LOAD PREFERENCES */
	
	InitDefaultPrefs();							// init to defaults
	LoadOurPrefs();								// then try to load the saved settings
	
	
	DoInputConfigDialog();						// bring up the config dialog


		
			/*******************/
			/* DO VIDEO CONFIG */
			/*******************/
			
	DoScreenModeDialog();						// ask user for mode
	
	SwitchDisplayMode(gGamePrefs.screenWidth, gGamePrefs.screenHeight, gGamePrefs.depth);	
	
	CalcDisplayVRAM();							// calc amount of VRAM on this display
	
	InitGammaValues();							// init gamma values
	

			/********************/
			/* GET OPENGL GOING */
			/********************/
				
	OGL_CreateDrawContext();					// create the draw context
	OGL_InitFont();								// init an AGL font to use
	InitBG3DManager();							// init the BG3D containers
	
	
	
		/* INSTALL OUR EVENT HANDLER CALLBACK */
			
	InitMyEventHandler();

	
			/* INIT AUDIO SYSTEM */
			
	InitAudio();
	
	

}


/******************** GET GAME NIB *********************/
//
// Our dialog resources are all stored in the Game.nib file.
// This code creates a reference to that .nib file so that we can
// easily get to the resources whenever we need to.
//

static void GetGameNib(void)
{
OSStatus	iErr;
			
	gBundle = CFBundleGetMainBundle();
	if (gBundle == nil)
		DoFatalAlert("\pCannot locate Main Bundle!");
		
	iErr = CreateNibReferenceWithCFBundle(gBundle, CFSTR("Game"), &gNibs);			// note:  we don't need the ".nib" extension in the filename
	if (iErr != noErr)
		DoFatalAlert("\pCreateNibReferenceWithCFBundle failed!");
}

#pragma mark -


/****************** CREATE SOME OBJECTS **********************/

static void CreateSomeObjects(void)
{
static OGLPoint3D raptorCoord	= {0,0,0};


			/* MAKE RAPTOR OBJECT */
			
	gRaptorObject = CreateAnObject(MODEL_GROUP_DINOSAURS,				
									Dino_GroupModel_Raptor,
									.05,
									&raptorCoord);

}


/******************* CREATE AN OBJECT *******************/
//
// We create an MetaObject which represents and object that we want to draw in our scene.
//
// First we create a Group object to hold everything.  Then we add a Matrix object to the group
// and then we add a reference to the BG3D data to the group.  The BG3D data is another
// group object which contains all of the vertex and triangle data for the meshes making up
// the object.
//
// So, we're building an object that looks something like this:
//
//		BASE GROUP OBJECT
//					|
//				MATRIX OBJECT
//					|
// 				 BG3D GROUP
//						|
//						Mesh Data
//						Mesh Data
//						Mesh Data
//

static MOGroupObject *CreateAnObject(short bg3dGroup, short bg3dModel, float scale, OGLPoint3D *coord)
{
MOGroupObject	*baseGroupObj;
MOMatrixObject	*matrixObj;
OGLMatrix4x4	matrix;

				/* CREATE THE BASE GROUP OBJECT */
				
	baseGroupObj = MO_CreateNewObjectOfType(MO_TYPE_GROUP, 0, nil);


			/* CREATE A MATRIX OBJECT & ADD TO GROUP */

	OGLMatrix4x4_SetScale(&matrix, scale, scale, scale);		// initialize a transform matrix
	matrix.value[M03] = coord->x;								// set translation in the matrix
	matrix.value[M13] = coord->y;
	matrix.value[M23] = coord->z;

	matrixObj = MO_CreateNewObjectOfType(MO_TYPE_MATRIX,		// create matrix object from the matrix
										0,
										&matrix);
										
	MO_AppendToGroup(baseGroupObj, matrixObj);					// add matrix object to group


			/* APPEND THE BG3D MODEL DATA TO THE GROUP */
			
	MO_AppendToGroup(baseGroupObj, gBG3DGroupList[bg3dGroup][bg3dModel]);

	return(baseGroupObj);
}


/********************* MOVE OBJECTS **********************/

static void MoveObjects(void)
{
OGLMatrix4x4	rotMatrix;
MOMatrixObject	*matrixObj;

				/* SPIN RAPTOR */
				
	matrixObj = (MOMatrixObject *)gRaptorObject->objectData.groupContents[0];		// matrix object is 1st object in the base group
	
	OGLMatrix4x4_SetRotate_Y(&rotMatrix, .5 * gOneOverFramesPerSecond);

	OGLMatrix4x4_Multiply(&rotMatrix, &matrixObj->matrix, &matrixObj->matrix);

}


#pragma mark -

/***************** DRAW SCENE GEOMETRY *******************/
//
// Called from DrawOpenGLScene() when it's time to submit our
// geometry data to OpenGL.
//

void DrawSceneGeometry(void)
{
			/* DRAW A FRAME COUNTER & FPS IN THE UPPER-LEFT CORNER */
			
	OGL_DrawString("\pframe #:", 20, 20);
	OGL_DrawInt(gFrameCounter, 70, 20);

	OGL_DrawString("\pfps:", 20, 40);
	OGL_DrawInt(gFramesPerSecond, 70, 40);

		
			/* DRAW MESSAGE IN OPPOSITE CORNER */

	OGL_DrawString("\pUse Arrow keys to move camera", 500, 400);			
	OGL_DrawString("\pType Command-Q to Quit", 500, 440);
		
	

			/* SET STATE */
	
	OGL_SetColor4f(1,1,1,1);	
	OGL_DisableTexture2D();
	OGL_DisableBlend();
	
	
			/****************/
			/* DRAW OBJECTS */
			/****************/
			//
			// To draw an object all we do is pass the Group Object containing the matrix and model to MO_DrawObject().
			// However, we first need to make sure our Vertex Array Range is correctly set for maximum performance
			// so we call glBindVertexArrayAPPLE().  
			//

	glBindVertexArrayAPPLE(gVertexArrayRangeObjects[MODEL_GROUP_DINOSAURS]);		// bind to the correct vertex array range memory for the GLOBAL models
	
			/* DRAW RAPTOR */
			
	MO_DrawObject(gRaptorObject);

}





/********************** MOVE CAMERA ***********************/

static void MoveCamera(void)
{
static	float		rot			= 0;

		/**********************************************/
		/* CHECK HID DEVICE VALUES FOR MOTION CONTROL */
		/**********************************************/
			
			/* SEE IF A FORWARD BUTTON WAS PRESSED */
			
	if (gControlNeeds[kNeed_Forward_Button].value != 0)
	{
		gCamera_Coord.z -= gOneOverFramesPerSecond * 20.0f;	
	}
	
			/* SEE IF BACKWARD BUTTON WAS PRESSED */
	else
	if (gControlNeeds[kNeed_Backward_Button].value != 0)
	{
		gCamera_Coord.z += gOneOverFramesPerSecond * 20.0f;	
	}
	
			/* SEE IF TURN LEFT BUTTON WAS PRESSED */

	if (gControlNeeds[kNeed_TurnLeft_Button].value != 0)
	{
		rot += gOneOverFramesPerSecond * 1.5f;	
	}
			/* SEE IF TURN RIGHT BUTTON WAS PRESSED */
	else
	if (gControlNeeds[kNeed_TurnRight_Button].value != 0)
	{
		rot -= gOneOverFramesPerSecond * 1.5f;	
	}
	
	
			/* PLAY SOUND EFFECT IF NEW FIRE KEY PRESSED */
			
	if (gControlNeeds[kNeed_Fire].newButtonPress) 
	{
		OAL_PlaySound3D(0, nil, nil, 1.0, 1.0, AL_FALSE);
	}
	
	
			/* UPDATE LOOK-AT PT */
	
	gCamera_LookAt.x = gCamera_Coord.x - sin(rot);
	gCamera_LookAt.z = gCamera_Coord.z - cos(rot);

}


#pragma mark -


/********************* MY MAIN LOOP **********************/

static pascal void MyMainLoop (EventLoopTimerRef  theTimer, void* userData)
{
static float	timer = 0;

	#pragma unused (theTimer, userData)

			/* EXIT EVENT LOOP IF QUIT FLAG SET */
			
	if (gQuitApplication)
		QuitApplicationEventLoop();

				/*************************/
				/* DO 1 PASS OF THE LOOP */
				/*************************/
				
	CalcFramesPerSecond();					// calc FPS

			/* MOVE & UPDATE STUFF */
			
	PollAllHIDInputNeeds();					// update input values
	MoveCamera();							// move camera
	MoveObjects();
	
			/* DRAW SCENE */
				
	OGL_DrawScene();
	
	
			/* UPDATE SYSTEM ACTIVITY SO THE COMPUTER WON'T GO TO SLEEP */
			
	timer -= gOneOverFramesPerSecond;
	if (timer < 0.0f)						// see if time to update system activity
	{
		UpdateSystemActivity(UsrActivity);	// ping the system
		timer = 30;							// reset timer for 30 seconds
	}
}	


/************************ MAIN **************************/

int main(void)
{
			/* INITIALIZE THINGS */

	InitApplication();
			
			
			/* PLAY A SONG & AMBIENT EFFECT */
			
//	PlaySong("\pSong.m4a", true);
//	OAL_PlaySound3D(2, nil, nil, 1.0, 1.0, AL_TRUE);
	
	
			/* LOAD MODELS FROM BG3D FILE */
			
	ImportBG3D("\pModels.bg3d", MODEL_GROUP_DINOSAURS);		// load dinosaur models
			
	
			/* CREATE SOME OBJECTS TO DRAW */
			
	CreateSomeObjects();
	
	
			/* DO THE MAIN LOOP */

	SetMyMainLoopEventTimer(MyMainLoop);		// install the main loop timer
	
	RunApplicationEventLoop();					// process all the timer events

	RemoveMyMainLoopEventTimer();				// we're all done, so cleanup
	
	
	CleanQuit();
}










