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


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

static void DrawSpinningCube(void);
static void MoveCube(void);
static void InitApplication(void);
static void GetGameNib(void);
static void InitializeGeometry(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[];

/**************************/
/*    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;


		/* GEOMETRY DATA FOR CUBE */

CubeDataType	gCubeMesh =
{
			/* POINTS */
				
	-1, -1, 1,					// left/bottom/front
	-1,-1,-1,					// left/bottom/rear
	1,-1,-1,					// right/bottom/rear
	1,-1,1,						// right/bottom/front
	-1, 1, 1,					// left/top/front
	-1,1,-1,					// left/top/rear
	1,1,-1,						// right/top/rear
	1,1,1,						// right/top/front	

			/* COLORS */
	0,0,0,1,					
	1,0,0,1,					
	0,1,0,1,				
	0,0,1,1,				
	1,1,1,1,				
	1,1,0,1,			
	0,1,1,1,				
	1,0,1,1,			

			/* QUADS */
			
	0,1,2,3,					// bottom face
	7,6,5,4,					// top face
	4,5,1,0,					// left face
	7,3,2,6,					// right face
	7,4,0,3,					// front face
	5,6,2,1						// rear face
};


float	gCubeRotX = 0, gCubeRotY = 0;


/******************** 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,"\pMyGame",&createdDirID);		// make "MyGame" 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
	
	if (gGamePrefs.playInWindow)				// play in window or full screen?
		CreateGameWindow();		
	else	
		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
	InitializeGeometry();						// allocate the VAR memory for the geometry mesh data
	
	
	
	
		/* 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 -

/***************** INITIALIZE GEOMETRY ********************/

static void InitializeGeometry(void)
{
			/* ASSIGN CUBE'S DATA TO VAR #0 */
			
	OGL_AssignVertexArrayRangeMemory(sizeof(CubeDataType), &gCubeMesh, 0);
}


/***************** 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);
	
	OGL_DrawString("\pX-Axis:", 20, 60);
	OGL_DrawInt(gControlNeeds[kNeed_XAxis].value, 70, 60);
	
	OGL_DrawString("\pY-Axis:", 20, 80);
	OGL_DrawInt(gControlNeeds[kNeed_YAxis].value, 70, 80);
	

			
			/* 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_DisableLighting();
	OGL_DisableTexture2D();
	OGL_DisableBlend();
	
			/* DRAW CUBE */
			
	DrawSpinningCube();
}



/***************** DRAW SPINNING CUBE *******************/

static void DrawSpinningCube(void)
{
		
			/* MAKE THE CUBE'S VAR MEMORY ACTIVE */
				
	glBindVertexArrayAPPLE(gVertexArrayRangeObjects[0]);		// bind to VAR #0
	
	
			/* SCALE AND ROTATE */
			
	glScalef(3,3,3);
	glRotatef(gCubeRotX,1,0,0);
	glRotatef(gCubeRotY,0,1,0);



			/* INIT THE VERTEX ARRAYS */
						
	glEnableClientState(GL_VERTEX_ARRAY);			// point to points
	glVertexPointer(3, GL_FLOAT, 0, gCubeMesh.points);	

	glColorPointer(4, GL_FLOAT, 0, gCubeMesh.colors);
	glEnableClientState(GL_COLOR_ARRAY);			// point to vertex colors

	glDisableClientState(GL_TEXTURE_COORD_ARRAY);	// no uv's
	glDisableClientState (GL_NORMAL_ARRAY);			// no normals

		
			/* SUBMIT THE VERTEX ARRAYS */

	glDrawElements(GL_QUADS, 4*6, GL_UNSIGNED_INT, gCubeMesh.quads);
		
}


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

static void MoveCamera(void)
{
static	OGLPoint3D	cameraCoord = {0,0,10};
static	OGLPoint3D	lookAtPt	= {0,0,0};
static	OGLVector3D	upVec		= {0,1,0};
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)
	{
		cameraCoord.z -= gOneOverFramesPerSecond * 20.0f;	
	}
	
			/* SEE IF BACKWARD BUTTON WAS PRESSED */
	else
	if (gControlNeeds[kNeed_Backward_Button].value != 0)
	{
		cameraCoord.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);
	}
	
	
			/* SET CAMERA */
	
	lookAtPt.x = cameraCoord.x - sin(rot);
	lookAtPt.z = cameraCoord.z - cos(rot);

	OGL_SetCamera(&cameraCoord, &lookAtPt, &upVec);

}


/******************** MOVE CUBE ************************/
//
// Called to rotate the cube.
//

static void MoveCube(void)
{
	gCubeRotX += 300.0f * gOneOverFramesPerSecond;		// spin 300 degrees per second on x-axis
	gCubeRotY += 100.0f * gOneOverFramesPerSecond;		// spin 100 degrees per second on y-axis
}


#pragma mark -


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

static pascal void MyMainLoop (EventLoopTimerRef  theTimer, void* userData)
{
	#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
	GammaFadeInOneFrame(2.0);				// do 2-second fade-in
	MoveCube();								// move cube
	MoveCamera();							// move camera
	
			/* DRAW SCENE */
				
	OGL_DrawScene();
				
}	


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

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

	InitApplication();
			
			
			
				/* DIM DISPLAY */
		
	if (!gGamePrefs.playInWindow)							// only gamma fade if full-screen
	{
		gGammaBrightness = 0;
		SetGammaFade(0);
	}
	
	
			/* PLAY A SONG & AMBIENT EFFECT */
			
//	PlaySong("\pSong.m4a", true);
	OAL_PlaySound3D(2, nil, nil, 1.0, 1.0, AL_TRUE);
	
	
			/* DO THE MAIN LOOP */

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

	RemoveMyMainLoopEventTimer();				// we're all done, so cleanup
	
			
	
		/* DO A 1.0 SECOND FADE OUT */
			
	if (!gGamePrefs.playInWindow)					// don't do gamma fades if playing in window
		GammaFadeOut(1.0);
	
	
	CleanQuit();
}










