
/*
	file : main.cpp

	Dragon Wings (c) 2005 Philipp Roßberger, Sebastian Roll
		www.einszunull.net
		www.sebastianroll.de

	This software is provided 'as-is', without any express or implied
	warranty.  In no event will the authors be held liable for any damages
	arising from the use of this software.

	Permission is granted to anyone to use this software for any purpose,
	including commercial applications, and to alter it and redistribute it
	freely, subject to the following restrictions:

	1. The origin of this software must not be misrepresented; you must not
		claim that you wrote the original software. If you use this software
		in a product, an acknowledgment in the product documentation would be
		appreciated but is not required.
	2. Altered source versions must be plainly marked as such, and must not be
		misrepresented as being the original software.
	3. This notice may not be removed or altered from any source distribution.

	Used software libraries from other people (thank you very much):

	The Irrlicht Engine:
		Copyright (c) 2002-2005 Nikolaus Gebhardt
		www.irrlicht.sourceforge.net
  
	The Character Animation Library (cal3D):
		Copyright (c) 2001, 2002 Bruno 'Beosil' Heidelberger
		www.cal3d.sourceforge.net

	The FMOD audio library:
		Copyright (c) Firelight Technologies Pty, Ltd. 1999-2004
		www.fmod.org

	Article - Using Cal3D with Irrlicht:
		Copyright (c) Thomas Suter 2004
		www.thomas.webtracker.ch/jahia/Jahia/cache/off/pid/672
*/


// link libraries
#pragma comment(lib, "../../../irrlicht-0.12.0/lib/Win32-VisualStudio/Irrlicht.lib")
#pragma comment(lib, "../../../cal3d-0.10.0/bin/release/cal3d.lib")
#pragma comment(lib, "../../../fmodapi374win/api/lib/fmodvc.lib")

// standard system includes
#include <stdlib.h>
#include <iostream>
#include "conio.h"
#include "winsock2.h"
#include "irrlicht.h"
#include "CCal3DSceneNode.h"
#include "fmod.h"

// use namespaces in every included file from now on
using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

// use defines in every included file from now on
#define v3 vector3df

// ------------------------------------- config -----------
#define LEVELPATH					"levels/level.xml"
#define MEDIAPATH_NUMBERS			"media/numbers/"			// append e.g. "0.png"
#define MEDIAPATH					"media/"
#define MEDIAFILE_END_IMAGE			"endImage.jpg"
// --------------------------------- end config -----------

// simple state-machine controlls game
#define STATE_GAME_START				2
#define STATE_LEVEL_END					4
#define STATE_PREPARENEXT_LEVEL			8
#define STATE_PLAY						16
#define STATE_DEATH						32
#define STATE_DEMO						64
#define STATE_DEMO_INIT					128

// global variables
extern IrrlichtDevice* device	= 0;
extern IVideoDriver* driver		= 0;
extern ISceneManager* smgr		= 0;
extern IGUIEnvironment* guienv	= 0;
extern ICameraSceneNode* cam	= 0;
extern ILightSceneNode* light	= 0;
extern IGUIStaticText* dbg		= 0;
extern IGUIInOutFader* fader	= 0;

extern ISceneNode* skyBox			= 0;
extern ISceneNode* arrow			= 0;
extern ILightSceneNode* spotLight	= 0;
extern ISceneNode* billB			= 0;

extern SCREEN_X = 800;
extern SCREEN_Y = 600;

extern bool paused = false;				// key P
extern bool renderOfflineAnim = false;	// key A
extern bool haveTracker = false;		// key T
extern bool fullScreen = false;			// key F
extern bool isDebug = false;			// key D
extern bool doDemoExit = false;			// any key, if in demo-mode
extern bool doAppExit = false;			// key ESC
extern bool doMusic = true;				// key M

// Object can be asteroid or ring or ..
struct Obj
{
	stringc name;
	IMesh* mesh;
	ISceneNode* node;
	v3 rot;
	IParticleSystemSceneNode* pssn;
};
// all objects are stored in this list
extern list<Obj*> objects;
list<Obj*> objects;

// the incoming UDP tracking data from an rdt400 tracking server
// you can play this game without mouse. You can use your body!
struct trackedBody
{
	unsigned long id; // id number
	float quality; // quality
	float loc[3]; // location
	float ang[3]; // orientation angles (eta, theta, phi)
	float rot[9]; // rotation matrix
};

// all player data is stored here
struct SPlayer
{
	CCal3DSceneNode* node;
	v3 velocity;
	v3 acceleration;
	trackedBody calibratedLeftWing;
	trackedBody calibratedRightWing;
} player;

// oh no, even more globals
extern ITimer* timer		= 0;
extern u32 levelTime		= 0;
extern u32 oldTime			= 0;
extern u32 now				= 0;
extern v3 nextRingPosition;
v3 nextRingPosition;
extern int numLeftRings		= 0;
extern float direction		= 0;
extern float ydirection		= 0;
extern int gameState		= STATE_GAME_START;

// some gui images
extern ITexture*  numbers[10]		= {0,0,0,0,0,0,0,0,0,0};	// font 0..9
extern IGUIImage* time[3]			= {0,0,0};	// time as 123 (123 seconds)
extern IGUIImage* ringCntDownImg	= 0;
extern IGUIImage* ringsLeftImg		= 0;
extern IGUIImage* logo				= 0;

// include project files
#include "tools.h"
#include "udp.h"
#include "sound.h"
#include "controller.h"
#include "level.h"
#include "event.h"


int main()
{
	// ask user for driver
	E_DRIVER_TYPE driverType;
	printf("DRAGON WINGS alpha1 (c) 2005 Philipp Rossberger, Sebastian Roll\n");
	printf("\n");
	printf("Music by Clemens Becker (www.beatsuite.de)\n");
	printf("Using software libraries:\n");
	printf(" Irrlicht Engine: (c) 2002-2005 Nikolaus Gebhardt\n");
	printf(" Character Animation Library: (c) 2001-2002 Bruno 'Beosil' Heidelberger\n");
	printf(" FMOD audio library: (c) 1999-2004 Firelight Technologies Pty, Ltd.\n");
	printf(" Article 'Cal3D with Irrlicht': (c) 2004 Thomas Suter\n");
	printf(" Independent JPEG Group's JPEG software: (c) 1991-1998 Thomas G. Lane\n");
	printf(" ZLib Data Compression Library: (c) 1995-2004 Jean-loup Gailly, Mark Adler\n");
	printf("\n");
	printf("Press 'D' in game for additional information!\n");
	printf("\n");
	/*
	printf("[R] : reset level\n");
	printf("[P] : pause/unpause game\n");
	printf("[A] : offline animation on/off\n");
	printf("[T] : switch tracking/mouse input\n");
	printf("[C] : calibrate tracking\n");
	printf("[F] : toggle fullscreen\n");
	printf("[D] : debug on/off\n");
	printf("[ESC] : exit\n\n");
	*/
	printf("Please select driver:\n (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.2  <-- best\n");
	char inp = getch();
	switch(inp)
	{
		case 'a': driverType = EDT_DIRECTX9;	break;
		case 'b': driverType = EDT_DIRECTX8;	break;
		case 'c': driverType = EDT_OPENGL;   break;
		default: return 1;
	}

	dimension2d<s32> screenRes;
	printf("\nPlease select resolution:\n (a) 1600 x 1200 (Fullscreen)\n (b) 1280 x 1024 (Fullscreen)\n (c) 800 x 600  (Fullscreen) <-- best\n");
	printf(" (d) 1600 x 1200\n (e) 1280 x 1024\n (f) 800 x 600\n");
	inp = getch();
	switch(inp)
	{
		case 'a': screenRes.Width = 1600; screenRes.Height = 1200; fullScreen = true; break;
		case 'b': screenRes.Width = 1280; screenRes.Height = 1024; fullScreen = true; break;
		case 'c': screenRes.Width = 800; screenRes.Height = 600; fullScreen = true; break;
		case 'd': screenRes.Width = 1600; screenRes.Height = 1200; fullScreen = false; break;
		case 'e': screenRes.Width = 1280; screenRes.Height = 1024; fullScreen = false; break;
		case 'f': screenRes.Width = 800; screenRes.Height = 600; fullScreen = false; break;
		default: return 1;
	}
	SCREEN_X = screenRes.Width;
	SCREEN_Y = screenRes.Height;

	int fps = 0;
	int lastFPS = 1;
	stringw str;
	EventReceiver receiver;			
	bool oldFullScreenMode = fullScreen;

	device = createDevice(driverType , screenRes, 32, fullScreen, false, false, &receiver);
	driver = device->getVideoDriver();
	smgr = device->getSceneManager();
	guienv = device->getGUIEnvironment();
	fader = guienv->addInOutFader();

	if (haveTracker)
		udpInit("dtrack-pc138", 5000);
	soundInit();
	soundPlayMusic();
	soundWindAdjust(0);
	fader->fadeOut(200);	// visible -> black

	while(device->run())
	{
		switch (gameState)
		{
			case STATE_GAME_START:
			{
				if (fader->isReady())
					fader->fadeOut(1);	// visible -> black
					reset();
				gameState = STATE_PREPARENEXT_LEVEL;
				break;
			}

			case STATE_PREPARENEXT_LEVEL:
			{
				soundWindAdjust(0);
				
				loadNextLevel();
				cam->setFarValue(CAMERA_FAR_VALUE);
				if (haveTracker)
				{
					udpReceive();
					calibrate();
				}
				// display amount of rings left
				if (isInRange(numLeftRings, 0 , 9))
				{
					ringCntDownImg->setImage(numbers[numLeftRings]);
				}
				fader->fadeIn(2000);	// black -> visible
				gameState = STATE_PLAY;
				break;
			}

			case STATE_PLAY:
			{
				if (haveTracker)
					udpReceive();
				update();
				break;
			}

			case STATE_LEVEL_END:
			{
				fader->fadeOut(1);	// visible -> black
				demoInit();
				gameState = STATE_DEMO_INIT;
				break;
			}

			case STATE_DEMO_INIT:
			{
				demo();
				if (fader->isReady())
				{
					fader->fadeIn(2000);
					gameState = STATE_DEMO;
				}
				break;
			}
			case STATE_DEMO:
			{
				demo();
				if (doDemoExit)
				{
					demoClose();
					doDemoExit = false;
					fader->fadeOut(2000);	// visible -> black
					gameState = STATE_DEATH;
				}
				break;
			}

			case STATE_DEATH:
			{
				if (fader->isReady())
				{
					gameState = STATE_GAME_START;
				}
				break;
			}
		}
		if (isDebug)
		{
			addDbg("----------------------------");
			addDbg("[R] : reset level");
			addDbg("[P] : pause/unpause game");
			addDbg("[M] : music on/off");
			addDbg("[A] : offline animation on/off");
			addDbg("[T] : switch tracking/mouse input");
			addDbg("[C] : calibrate tracking");
			addDbg("[F] : toggle fullscreen");
			addDbg("[D] : debug on/off");
			addDbg("[ESC] : exit");
			addDbg("----------------------------");
			addDbg("");
			if (haveTracker)
			{
				addDbg("body pos left", udpGetLeftWing()->loc);
				addDbg("body angle left", udpGetLeftWing()->ang);
				addDbg("body pos right", udpGetRightWing()->loc);
				addDbg("body angle right", udpGetRightWing()->ang);
			}
			addDbg("");
			addDbg("camPos", cam->getPosition());
			addDbg("");
			if (player.node)
			{
				addDbg("player pos", player.node->getPosition());
				addDbg("player rotation", player.node->getRotation());
				addDbg("player acceleration", player.acceleration);
				addDbg("player velocity", player.velocity);
				addDbg("player x direction", direction);
				addDbg("player y direction", ydirection);

				addDbg("");
				CalQuaternion q;
				q = player.node->getBoneRotation(1);
				addDbg("bone right wing qaternion", q);
				q = player.node->getBoneRotation(2);
				addDbg("bone left wing qaternion", q);
			}
			addDbg("");

			addDbg("drawn triangles", (int) driver->getPrimitiveCountDrawn());
			addDbg(str);
			guienv->getRootGUIElement()->bringToFront(dbg);
		}

		driver->beginScene(true, true, SColor(255,100,101,140));
		smgr->drawAll();
		guienv->drawAll();

		if (gameState >= STATE_PLAY)
			driver->endScene();		// swap backbuffer with frontbuffer
		
		if (isDebug)
			clrDbg();

		while(paused)
		{
			_sleep(10);
			if (!device->run()) break;
		}

		fps = driver->getFPS();
		if (lastFPS != fps)
		{
			str = L"Dragon Wings [FPS:";
			str += fps;
			str += "]";
			device->setWindowCaption(str.c_str());
			lastFPS = fps;
		}

		if (oldFullScreenMode != fullScreen)
		{
			cout << endl << "main() changing fullScreen mode" << endl << endl;
			oldFullScreenMode = fullScreen;
			device->drop();
			device = 0;	driver = 0;	smgr = 0; guienv = 0; cam = 0; light = 0; dbg = 0; skyBox = 0; arrow = 0; ringCntDownImg = 0; spotLight = 0; ringsLeftImg = 0; billB = 0; logo = 0;
			_sleep(100); // give os time
			device = createDevice(driverType, screenRes, 32, fullScreen, false, false, &receiver);
			driver = device->getVideoDriver();
			smgr = device->getSceneManager();
			guienv = device->getGUIEnvironment();
			_sleep(100); // give os time
			
			list<Obj*>::Iterator it = objects.begin();
			while(it != objects.end())
			{
				delete (*it);
				it++;
			}
			player.node = 0;
			for (int i=0; i<3; i++)
				time[i] = 0;
			objects.clear();
			gameState = STATE_GAME_START;
		}

		if (doAppExit)
			break;	// exit while loop
	}

	udpClose();
	soundClose();
	device->drop();
	return 0;
}

