#include "Ogre.h"
#include "OgreConfigFile.h"
#include "GameFrameListener.h"
#include "GameApplication.h"

using namespace Ogre;

namespace DinastyOnline {
	namespace Graphics {

		GameApplication::GameApplication() {
			mFrameListener = 0;
			mRoot = 0;
			world = NULL;
		}

		GameApplication::~GameApplication()	{
			if(mGUISystem) {
				delete mGUISystem;
				mGUISystem = 0;
			}
			if(mGUIRenderer) {
				delete mGUIRenderer;
				mGUIRenderer = 0;
			}
			if (mFrameListener) {
				delete mFrameListener;
			}
			if (mRoot) {
				delete mRoot;
			}
		}

		/// Start
		void GameApplication::go(void)
		{
			if (!setup()) {
				return;
			}

			mRoot->startRendering();

			// clean up
			destroyScene();
		}

		/// Builds base (camera, viewports, resources, framelistener...)
		bool GameApplication::setup(void)
		{
			mRoot = new Root();

			setupResources();

			if (!configure()) {
				return false;
			}

			chooseSceneManager();
			createCamera();
			createViewports();

			// Set default mipmap level (NB some APIs ignore this)
			TextureManager::getSingleton().setDefaultNumMipmaps(5);

			// Create any resource listeners (for loading screens)
			createResourceListener();

			// Load resources
			loadResources();

			// Create main Menu
			createMenu();

			createFrameListener();

			return true;
		}

		/// Configures the application :: Output system, full-screen, resolution...
		bool GameApplication::configure(void) {

			// Try to load "ogre.cfg" for configuration options
			if (mRoot->restoreConfig())
			{
				// Let the system create a default rendering window by passing 'true'
				mWindow = mRoot->initialise(true, "Dinasty Online :: Demo");
			} else {
				// "ogre.cfg" does not exists

				// Ask the configuration questions
				if(mRoot->showConfigDialog()) {
					// Let the system create a default rendering window by passing 'true'
					mWindow = mRoot->initialise(true);
				} else {
					return false;
				}
			}

			return true;
		}

		/// Type of SceneManager
		void GameApplication::chooseSceneManager(void) {
			// Use the terrain scene manager.
			mSceneMgr = mRoot->getSceneManager(ST_EXTERIOR_CLOSE);
		}

		void GameApplication::createCamera(void) {
			// Create the camera
			mCamera = mSceneMgr->createCamera("PlayerCam");
			// Do not show things nearest thatn 5 px
			mCamera->setNearClipDistance(5);
		}

		void GameApplication::createFrameListener(void)	{
			mFrameListener= new GameFrameListener(mWindow, mCamera, mSceneMgr, mGUIRenderer, mWalkList);
			mFrameListener->showDebugOverlay(true);
			mRoot->addFrameListener(mFrameListener);
		}

		/// Main menu
		void GameApplication::createMenu(void) {

			// CEGUI setup
			mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow, Ogre::RENDER_QUEUE_OVERLAY, true, 3000, ST_EXTERIOR_CLOSE);
			mGUISystem = new CEGUI::System(mGUIRenderer);

			// CEGUI Mouse setup
			CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"WindowsLook.scheme");
			mGUISystem->setDefaultMouseCursor((CEGUI::utf8*)"WindowsLook", (CEGUI::utf8*)"MouseArrow");
			CEGUI::MouseCursor::getSingleton().setImage("WindowsLook", "MouseArrow");
			mGUISystem->setDefaultFont((CEGUI::utf8*)"Tahoma-12");

			// Create "CEGUI.log"
			CEGUI::Logger::getSingleton().setLoggingLevel(CEGUI::Informative);

			// Load sheet with defined windows
			CEGUI::Window* sheet = CEGUI::WindowManager::getSingleton().loadWindowLayout(
					(CEGUI::utf8*)"gameMainMenu.layout");
			mGUISystem->setGUISheet(sheet);

			// Connect handlers with menu elements
			setupEventHandlers();

			// Background: Create a skydome
			mSceneMgr->setSkyDome(true, "GameMaterial/CloudySky", 5, 8);
		}

		/// Set Handlers for Menu elements
		void GameApplication::setupEventHandlers(void) {
			CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();
			wmgr.getWindow((CEGUI::utf8*)"GameGui/LoginWindow/QuitButton")
				->subscribeEvent(
					CEGUI::PushButton::EventClicked,
					CEGUI::Event::Subscriber(&GameApplication::handleQuit, this));
			wmgr.getWindow((CEGUI::utf8*)"GameGui/LoginWindow/LoginButton")
				->subscribeEvent(
					CEGUI::PushButton::EventClicked,
					CEGUI::Event::Subscriber(&GameApplication::handleLogin, this));
			wmgr.getWindow((CEGUI::utf8*)"GameGui/SelectCharacter/StartButton")
				->subscribeEvent(
					CEGUI::PushButton::EventClicked,
					CEGUI::Event::Subscriber(&GameApplication::handleStart, this));
			wmgr.getWindow((CEGUI::utf8*)"GameGui/SelectCharacter/QuitButton")
				->subscribeEvent(
					CEGUI::PushButton::EventClicked,
					CEGUI::Event::Subscriber(&GameApplication::handleQuit, this));
		}

		/// Hide login window and show character selection window
		bool GameApplication::handleLogin(const CEGUI::EventArgs& e) {
			CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();
			wmgr.getWindow((CEGUI::utf8*)"GameGui/LoginWindow")->hide();

			// Add Characters to ComboBox
			CEGUI::Combobox* objectComboBox = (CEGUI::Combobox*)CEGUI::WindowManager::getSingleton().getWindow("GameGui/SelectCharacter/CharacterListbox");
			CEGUI::colour black = CEGUI::colour(0,0,0);

			CEGUI::ListboxTextItem* item = new CEGUI::ListboxTextItem((CEGUI::utf8*)"BlackSmith", 0);
			item->setTextColours(black);
			objectComboBox->addItem(item);
			item = new CEGUI::ListboxTextItem((CEGUI::utf8*)"Preacher", 1);
			item->setTextColours(black);
			objectComboBox->addItem(item);
			item = new CEGUI::ListboxTextItem((CEGUI::utf8*)"Thief", 2);
			item->setTextColours(black);
			objectComboBox->addItem(item);

			// Get login and password
			CEGUI::Editbox *loginBox = (CEGUI::Editbox *)wmgr.getWindow((CEGUI::utf8*)"GameGui/LoginWindow/Loginbox");
			CEGUI::String login = loginBox->getText();
			this->login = login.c_str();
			
			CEGUI::Editbox *passBox = (CEGUI::Editbox *)wmgr.getWindow((CEGUI::utf8*)"GameGui/LoginWindow/Passwordbox");
			CEGUI::String pass = passBox->getText();
			this->password = pass.c_str();
			
			// Create connection
			Address cmdAddress("127.0.0.1:5555");
			this->world = new GameWorldClient(cmdAddress, this->login, this->password);
			TNLLogEnable(LogGhostConnection, true);
			
			wmgr.getWindow((CEGUI::utf8*)"GameGui/SelectCharacter")->show();

			return true;
		}

		// Hide character selection window and create game Scene
		bool GameApplication::handleStart(const CEGUI::EventArgs& e) {
			CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();
			wmgr.getWindow((CEGUI::utf8*)"GameGui/SelectCharacter")->hide();

			// Turn off SkyDome of Main menu
			mSceneMgr->setSkyDome(false, "GameMaterial/CloudySky");

			createScene();

			// Initialize and make framelistener listen for key/mouse interaction for gaming
			mFrameListener->startGame(this->world);
			return true;
		}

		bool GameApplication::handleQuit(const CEGUI::EventArgs& e) {
			static_cast<GameFrameListener*>(mFrameListener)->requestShutdown();
			return true;
		}

		void GameApplication::createScene(void){
			SceneNode *node;
			Entity *ent;

			// Set ambient light
			mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

			// White Background
			ColourValue fadeColour(0.9, 0.9, 1);
			mWindow->getViewport(0)->setBackgroundColour(fadeColour);

			// No fog, problems with robot model
			//mSceneMgr->setFog( FOG_LINEAR, fadeColour, 0, 600, 900);

			// Sky plane (if Fog enabled)
			//Plane plane;
			//plane.d = 100;
			//plane.normal = Vector3::NEGATIVE_UNIT_Y;

			// Curved Plane 1500x1500 (50 tiles)
			//mSceneMgr->setSkyPlane( true, plane, "GameMaterial/CloudySky", 1500, 50, true, 0.5, 150, 150 );

			// Sky dome if fog disabled
			mSceneMgr->setSkyDome(true, "GameMaterial/CloudySky", 5, 8);

			// Load world geometry
			mSceneMgr->setWorldGeometry( "terrain.cfg" );

			// Create 2 anidated scene nodes for the camera. We want to be able to
			// move in the camera's direction.

			// Create the Camera scene node (we can yaw and move this node)
			node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "CamNode1", Vector3( 7500, 800, 7500 ) );
			// Create the pitch node (we can pitch this node)
			node = node->createChildSceneNode( "PitchNode1" );
			node->pitch(Degree(-55));
			node->attachObject( mCamera );
			// Set camera relative look point
			mCamera->setPosition( 0, 0, 0 );

			// Our player
			ent = mSceneMgr->createEntity( "Player", "robot.mesh" );
			node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "PlayerNode" );
			node->attachObject( ent );
			node->setPosition( Vector3(7300, 0, 7000) );

			// Create a second node/pitch node associated to our player's node
			// Here we will put the camera to follow our player
        	node = node->createChildSceneNode( "CamNode2", Vector3( -300, 200, 0 ) );
        	node->yaw(Degree(270));
        	node = node->createChildSceneNode( "PitchNode2" );
        	node->pitch(Degree(-15));

			/*Entity *ent2 = mSceneMgr->createEntity( "Building", "sphere.mesh" );
			SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "BuildingNode" );
			node2->attachObject( ent2 );
			node2->setPosition( Vector3(7500, 100, 7200) );*/
								
			/*
			ent = mSceneMgr->createEntity( "Test", "athene.mesh" );
			node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "TestNode" );
			node->attachObject( ent );
			node->setPosition( Vector3(7700, 100, 7500) );*/

		};

		void GameApplication::destroyScene(void){}

		void GameApplication::createViewports(void) {
			// Create one viewport, entire window
			Viewport* vp = mWindow->addViewport(mCamera);
			vp->setBackgroundColour(ColourValue(0,0,0));

			// Alter the camera aspect ratio to match the viewport
			mCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
		}

		/// Load resources from file
		void GameApplication::setupResources(void) {
			// Load resource paths from config file
			ConfigFile cf;
			cf.load("resources.cfg");

			// Go through all sections & settings in the file
			ConfigFile::SectionIterator seci = cf.getSectionIterator();

			String secName, typeName, archName;
			while (seci.hasMoreElements())
			{
				secName = seci.peekNextKey();
				ConfigFile::SettingsMultiMap *settings = seci.getNext();
				ConfigFile::SettingsMultiMap::iterator i;
				for (i = settings->begin(); i != settings->end(); ++i)
				{
					typeName = i->first;
					archName = i->second;
					ResourceGroupManager::getSingleton().addResourceLocation(
						archName, typeName, secName);
				}
			}
		}

		/// Used for creating resource listeners (e.g. for loading screens)
		void GameApplication::createResourceListener(void) { }

		/// Resource Group loading and/or initialise
		void GameApplication::loadResources(void) {
			// Initialise, parse scripts etc
			ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
		}

	}
}



