//****************************************************************************
// "Those Funny Funguloids!"
// http://funguloids.sourceforge.net
// Copyright (c) 2006-2007, Mika Halttunen
//
// 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.
//
//***************************************************************************/

#include "game.h"
#include "player.h"
#include "objectsystem.h"
#include "gamecamera.h"
#include "base.h"
#include "effects.h"
#include "bground.h"
#include "scriptsystem.h"
#include "soundsystem.h"
#include "hiscore.h"


// Playfield size (which is really read from the level script..)
Real playfieldWidth = 150.0f;
Real playfieldHeight = 116.0f;

//ConfigFile *GameApplication::mGameConfig = NULL;
CSimpleIniA *GameApplication::mGameConfig = NULL;

// Global game application pointer (I know, I know.. Bad, baad me.. :P)
GameApplication *gameApp;



// Helper function which returns suitable path for the
// config file. It first checks the user's home directory,
// (in Linux only) and if that fails it uses the default directory.
String getConfigLocation(bool reading) {
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
	extern String getFunguloidsDir();

	// Get the path to the config file
	String tmp = getFunguloidsDir() + SETTINGS_FILE;

	// Check if the config file exists there
	if(reading) {
		FILE *ftest = fopen(tmp.c_str(), "rt");
		if(!ftest) {
			// It doesn't exist, try the default
			return SETTINGS_FILE;
		}
		fclose(ftest);
	}

	return tmp;
#endif

	// Return the default
	return SETTINGS_FILE;
}


// Prepare the frame to render
bool OgreAppFrameListener::frameStarted(const FrameEvent &evt) {
	if(mWindow->isClosed() || mQuit)
		return false;

	// Capture the input devices
	mInput->capture();


	// Do the game / menu
	switch(gameApp->getState()) {
		case STATE_MENU:
			doMenu(evt.timeSinceLastFrame); break;
		case STATE_GAME:
			doGame(evt.timeSinceLastFrame); break;
	}

	// Update the sound system
	SoundSystem::getSingleton().update();

	if(gameApp->hasGameEnded())
		gameApp->endGame();
	return true;
}


// Update the special level transition effect
void GameApplication::updateSpecialLevelTransitionEffect(Real alpha) {
	if(alpha > 0) {
		if(!mSpecialLevelTransition->isVisible())
			mSpecialLevelTransition->show();
		MaterialPtr mat = MaterialManager::getSingleton().getByName("SpecialLevelTransition");
		mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setColourOperationEx(LBX_MODULATE, LBS_CURRENT, LBS_MANUAL, ColourValue(alpha,alpha,alpha), ColourValue(alpha,alpha,alpha));
		mat->getTechnique(0)->getPass(1)->getTextureUnitState(1)->setColourOperationEx(LBX_MODULATE, LBS_CURRENT, LBS_MANUAL, ColourValue(alpha,alpha,alpha), ColourValue(alpha,alpha,alpha));
	}
	else
		mSpecialLevelTransition->hide();
}


// Update the ball worm glow effect
void GameApplication::updateBallWormGlowEffect(Real alpha) {
	if(alpha > 0) {
		if(!mBallWormGlowEffect->isVisible())
			mBallWormGlowEffect->show();
		MaterialPtr mat = MaterialManager::getSingleton().getByName("BallWormGlow");
		mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setColourOperationEx(LBX_MODULATE, LBS_CURRENT, LBS_MANUAL, ColourValue(alpha,alpha,alpha), ColourValue(alpha,alpha,alpha));
		mat->getTechnique(0)->getPass(1)->getTextureUnitState(1)->setColourOperationEx(LBX_MODULATE, LBS_CURRENT, LBS_MANUAL, ColourValue(alpha,alpha,alpha), ColourValue(alpha,alpha,alpha));
	}
	else
		mBallWormGlowEffect->hide();
}


// Update the game logic
void OgreAppFrameListener::doGame(Real delta) {
	// Update all the moving objects (enemies, ...)
	if(!mPlayer->isConfirmQuit()) {
		ObjectMapType::iterator i;
		ObjectSystem *ob = ObjectSystem::getSingletonPtr();
		for(i=ob->getFirst(); i != ob->getLast(); ) {
			(*i).second->move(delta);

			// Destroy objects marked for deletion
			if((*i).second->isToBeDeleted())
				ob->destroyObject(i++);
			else
				++i;
		}
	}

	if(mPlayer->isConfirmQuit()) delta = 0;

	// Move the player
	mPlayer->move(delta, mInput);


	// Move the camera
	mGameCamera->followTarget(delta, (mPlayer->isDead() ? 2.0f : 5.5f));
}


// Prepare the scene
void GameApplication::createScene() {
	gameApp = this;
	mGameState = STATE_MENU;
	mEndGame = false;
	mPlayer = NULL;

	// Special level transition overlay
	mSpecialLevelTransition = OverlayManager::getSingleton().getByName("SpecialLevelTransition");
	mBallWormGlowEffect = OverlayManager::getSingleton().getByName("BallWormGlow");

	// Script system
	new ScriptSystem(mSceneMgr, "startup.lua");

	// Set up the shadows
	String shadows = GameApplication::mGameConfig->GetValue("graphics", "shadows", "on");
	if(shadows.compare("off") != 0) {
		mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE);
		mSceneMgr->setShadowColour(ColourValue(0.5f,0.5f,0.5f));
	}

	// Load the sounds
	SoundSystem *ss = SoundSystem::getSingletonPtr();
	ss->loadSound("mushroom1", 400);
	ss->loadSound("mushroom2", 400);
	ss->loadSound("leave_base", 400);
	ss->loadSound("score", 50);
	ss->loadSound("base_hanging", 0, true);
	ss->loadSound("death", 400);
	ss->loadSound("base_enter", 400);
	ss->loadSound("flight1", 0);
	for(int i=1; i<12; i++)
		ss->loadSound("flight" + StringConverter::toString(i+1), 200);
	ss->loadSound("whirler", 200);
	ss->loadSound("change_base", 400);
	ss->loadSound("menumove", 400);
	ss->loadSound("menuselect", 400);
	ss->loadSound("typing", 400);
	ss->loadSound("balls", 50);

	// Create the object system
	new ObjectSystem(mSceneMgr);

	// Create the effect lights pool
	createEffectLights(mSceneMgr);

	// Create the menus and go to the title screen
	mMenu = new Menu(mSceneMgr);
	mMenu->setMenu("TitleScreen");
}


// Start a new game
void GameApplication::startNewGame() {
	clearLevel();

	if(mPlayer) {
		delete mPlayer;
		mPlayer = NULL;
	}


	mGameCamera->resetRotation();
	mEndGame = false;
	mGameState = STATE_GAME;
	OverlayManager::getSingleton().getByName("DisplayOverlay")->show();

	// Create the player
	mPlayer = new Player("Player", mSceneMgr, "Drop.mesh", Vector3(0,0,0), mGameCamera, false);
	mPlayer->setCastShadows(false);
	mGameCamera->setTarget(mPlayer->getSceneNode());
	ScriptSystem::getSingleton().setPlayer(mPlayer);
	mFrameListener->setPlayer(mPlayer);

	ScriptSystem::getSingleton().executeString("NewGame();");

	// Load the first level
	loadLevel("level1.lua");
}


// End the current game
void GameApplication::endGame() {
	OverlayManager::getSingleton().getByName("DisplayOverlay")->hide();
	mGameState = STATE_MENU;
	mEndGame = false;

	Overlay *scroller = OverlayManager::getSingleton().getByName("MenuScroller");
	scroller->setScroll(0,0);

	// Check if the player qualifies for the high scores
	if(mPlayer->isDead()) {
		hiscoreList.load(getHiscoreLocation(true));
		int place = hiscoreList.addName("zzz", mPlayer->getScore());
		if(place != -1) {
			mMenu->setMenu("HighScores");
			mMenu->enterName(place);
		}
		else
			mMenu->setMenu("MainMenu");
	}
	else
		mMenu->setMenu("MainMenu");
}


// Load a level
void GameApplication::loadLevel(const String &level) {
	// Load the level script
	ScriptSystem::getSingleton().executeScript(level.c_str());

	// Get the level dimensions
	playfieldWidth = ScriptSystem::getSingleton().getScriptNumber("playfieldWidth");
	playfieldHeight = ScriptSystem::getSingleton().getScriptNumber("playfieldHeight");


	// Create the background
	ScriptSystem::getSingleton().executeString("BackgroundCreate();");
	createBackground(mSceneMgr);


	// Create the lights
	ScriptSystem::getSingleton().executeString("LightsCreate();");


	// Level borders
/*	ManualObject *manual = mSceneMgr->createManualObject("viivat");
	manual->begin("BaseWhiteNoLighting", RenderOperation::OT_LINE_STRIP);
	manual->position(-playfieldWidth, -playfieldHeight, 0);
	manual->position(-playfieldWidth,  playfieldHeight, 0);
	manual->position( playfieldWidth,  playfieldHeight, 0);
	manual->position( playfieldWidth, -playfieldHeight, 0);
	manual->index(0);
	manual->index(1);
	manual->index(2);
	manual->index(3);
	manual->index(0);
	manual->end();
	mSceneMgr->getRootSceneNode()->createChildSceneNode("LevelBorders")->attachObject(manual);
*/

	// Create the objects
	ScriptSystem::getSingleton().executeString("LevelCreate();");

	// Position the player to the home base
	if(mPlayer) {
		Vector3 pos;
		MovingObject *base = ObjectSystem::getSingleton().findObject(ScriptSystem::getSingleton().getScriptString("playerHomeBase"));
		pos = base->getPosition();
		pos.z = 0;
		mPlayer->moveTo(pos);
		mPlayer->setLastBase(static_cast<Base*>(base));
	}


	// Set the bloom
	MaterialPtr mat = MaterialManager::getSingleton().getByName("Ogre/Compositor/BloomBlend");
	Real effect;
	String st = mGameConfig->GetValue("graphics", "bloom_amount", "1.0");
	effect = StringConverter::parseReal(st);
	if(level != "special_level.lua")
		effect *= 0.5f;
	mat->setSelfIllumination(effect, effect, effect);

	mRoot->clearEventTimes();
}


// Clear the level
void GameApplication::clearLevel() {
	try {
		destroyBackground(mSceneMgr);
		ScriptSystem::getSingleton().destroyScriptLights();
		ObjectSystem::getSingleton().destroyObjects();
	}
	catch(...) {
		// Ignore..
	}
}


// Set the end game
void GameApplication::setEndGame() {
	mEndGame = true;
	updateBallWormGlowEffect(0);
	updateSpecialLevelTransitionEffect(0);
}


void GameApplication::createCamera() {
	mCamera = mSceneMgr->createCamera("MainCam");
	mCamera->setPosition(Vector3(0,0,115));
	mCamera->lookAt(Vector3(0,0,0));
	mCamera->setNearClipDistance(10);

	mGameCamera = new GameCamera(mCamera);
}


GameApplication::~GameApplication() {
	clearLevel();
	if(mPlayer)
		delete mPlayer;

	if(mMenu)
		delete mMenu;

	if(mGameCamera)
		delete mGameCamera;

	if(mGameConfig)
		delete mGameConfig;
}


// Game title
const String GameApplication::getTitle() const {
	return "Those Funny Funguloids!";
}

