// This file is part of ThriceBall.
//
// Author: Kai Hillenbrand (AnTeevY)
// © Copyright, 2009 by Kai Hillenbrand, http://www.anteevy.de

// --------------------------------------------------

#include "framelistener.h"

CFrameListener::CFrameListener(RenderWindow* win, SceneManager *sceneMgr, CRenderer *renderer, CPlayer *player)
: CBaseFrameListener(win, true, true, true), m_pSceneMgr(sceneMgr), m_pRenderer(renderer), m_pPlayer(player),
m_pLevel(m_pRenderer->getLevel()), m_pTimeController(NxOgre::TimeController::getSingleton()), 
m_pCamera(sceneMgr->getCamera("MainCamera")), isMMBdown(false), fMouseSens(4.f), m_bIngame(true), m_bContinue(true), 
fWinWidth(1280.f), fWinHeight(1024.f)
{
	// input stuff
	m_pMouse->setEventCallback(this);
	m_pKeyboard->setEventCallback(this);

	// move the mouse cursor away from the center
	// because the direction arrow would go crazy otherwise
	CEGUI::System::getSingleton().injectMouseMove(100, -100);

	std::cout << "ThriceBall Log: Created Framelistener" << std::endl;	// for the log
}

// is called each frame:
bool CFrameListener::frameStarted(const FrameEvent &evt)
{
	// proceed with physics calculation
	m_pTimeController->advance(1.f/60.f); // if vsync, then smooth!

	// enable mouse and keyboard input
	if (m_pMouse)
		m_pMouse->capture();
	if (m_pKeyboard) 
		m_pKeyboard->capture();

	// adjust rotation point scene node so that the ball stays in the center of the screen
	m_pSceneMgr->getSceneNode("RotPointNode")->setPosition(m_pPlayer->getCurBallPos());

	m_pPlayer->update(fWinWidth, fWinHeight);		// update player
	m_pLevel->movePlatforms();						// move the moving platforms
	m_pRenderer->getInterface()->updateHUD();		// refreshes the HUD

	return m_pRenderer->getContRend();
}

// mouse input

CEGUI::MouseButton CFrameListener::convertButton(OIS::MouseButtonID buttonID)
{
	switch (buttonID)
	{
	case OIS::MB_Left:		return CEGUI::LeftButton;
	case OIS::MB_Right:		return CEGUI::RightButton;
	case OIS::MB_Middle:	return CEGUI::MiddleButton;
	default:				return CEGUI::LeftButton;
	}
};

bool CFrameListener::mouseMoved(const OIS::MouseEvent &e)
{
	CEGUI::System::getSingleton().injectMouseMove(e.state.X.rel * fMouseSens, e.state.Y.rel * fMouseSens);
	
	// update window size
	fWinWidth = e.state.width;
	fWinHeight = e.state.height;

	if (isMMBdown) // zoom in/out and rotate the scene node the camera is attached to -> rotate camera
	{
		// keep the camera in boundaries: zoom from 20 to 40 meters away from the ball
		if (((m_pCamera->getParentSceneNode()->getPosition().y < m_pSceneMgr->getSceneNode("RotPointNode")->getPosition().y + 40)
			|| (e.state.Y.rel < 0)) && 
			((m_pCamera->getParentSceneNode()->getPosition().y > m_pSceneMgr->getSceneNode("RotPointNode")->getPosition().y + 20)
			|| (e.state.Y.rel > 0)))
			m_pCamera->getParentSceneNode()->translate(m_pCamera->getDirection() * -e.state.Y.rel * 0.1);

		m_pSceneMgr->getSceneNode("RotPointNode")->rotate( Ogre::Vector3::UNIT_Y, Degree(-e.state.X.rel * 0.5));
	}

	// with mouse wheel scrolling: keep the camera in boundaries: zoom from 20 to 40 meters away from the ball
	if (((m_pCamera->getParentSceneNode()->getPosition().y < m_pSceneMgr->getSceneNode("RotPointNode")->getPosition().y + 40)
		|| (e.state.Z.rel > 0)) && 
		((m_pCamera->getParentSceneNode()->getPosition().y > m_pSceneMgr->getSceneNode("RotPointNode")->getPosition().y + 20)
		|| (e.state.Z.rel < 0)))
		m_pCamera->getParentSceneNode()->translate(m_pCamera->getDirection() * e.state.Z.rel * 0.03);

	return true;
}

bool CFrameListener::mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonID id)
{
	CEGUI::System::getSingleton().injectMouseButtonDown(convertButton(id));

	if (id == OIS::MB_Left)		// left mouse button is now hold down
	{
		m_pPlayer->setCharging(true);	// start charging for a shot
	}
	if (id == OIS::MB_Right)	// right mouse button is now hold down
	{
		m_pPlayer->setBraking(true);	// start braking the ball

		if (m_pPlayer->isCharging()) // if LMB is already pressed (for charging power for a shot)
			m_pPlayer->cancelShot();	// stop the shot
	}
	if (id == OIS::MB_Middle)	// right mouse button is now hold down
		isMMBdown = true;
		
	return true;
}

bool CFrameListener::mouseReleased(const OIS::MouseEvent &e, OIS::MouseButtonID id)
{
	CEGUI::System::getSingleton().injectMouseButtonUp(convertButton(id));

	// mouse button is no longer hold down
	if (id == OIS::MB_Left)		// left mouse button is now hold down
	{
		if (m_pPlayer->isCharging())	// if shot was not calceled before
		{
			m_pPlayer->applyMove();		// move the currently selected ball with appropiate force in the chosen direction
		}
	}
	if (id == OIS::MB_Right)	// right mouse button is now hold down
		m_pPlayer->setBraking(false);	// no longer braking the ball
		
	if (id == OIS::MB_Middle)	// right mouse button is now hold down
		isMMBdown = false;
	
	return true;
}

// key input

bool CFrameListener::keyPressed(const OIS::KeyEvent &e)
{
	switch (e.key)
	{
	case OIS::KC_ESCAPE:	// ESCAPE KEY: exit program
		m_pRenderer->setContRend(false);
		
		// send quit info to standard output
		std::cout << m_pPlayer->getQuitMsg().c_str() << std::endl;
		break;

	case OIS::KC_DELETE:	// DELETE KEY: restart level
		m_pPlayer->restart();	// first, reset the player...
		m_pLevel->restart();	// ...and then the level
		break;

	case OIS::KC_SPACE:		// SPACE KEY: toggle time pause/play
		if (m_pRenderer->getDebugMode())
		{
			if (m_pTimeController->isPaused())
				m_pTimeController->resume();
			else
				m_pTimeController->pause();
		}
		break;

	case OIS::KC_TAB:		// TAB KEY: switch to next ball
		m_pPlayer->switchBall();
		break;

	case OIS::KC_F12:		// F12 KEY: save screenshot
		m_pWindow->writeContentsToFile("Screen_" + m_pRenderer->getLevelName() + "_" + StringConverter::toString(int(Ogre::Math::RangeRandom(10000,99999))) + ".png");
		break;

	case OIS::KC_LSHIFT:	// LEFT SHIFT KEY: (debug mode) jump to next checkpoint
		if (m_pRenderer->getDebugMode())
		{
			m_pLevel->nextCheckpoint();
		}
		break;

	case OIS::KC_E:			// E KEY: (debug mode) reset the energy to 100%
		if (m_pRenderer->getDebugMode())
		{
			m_pPlayer->refillEnergy();
		}
		break;

	case OIS::KC_D:			// D KEY: (debug mode) toggle debug overlay
		if (m_pRenderer->getDebugMode())
		{
			if (m_pDebugOverlay->isVisible())
				m_pDebugOverlay->hide();
			else
				m_pDebugOverlay->show();
		}
		break;

	default: break;
	}

	return true;
}

bool CFrameListener::keyReleased(const OIS::KeyEvent &e)
{
	return true;
}

bool CFrameListener::windowClosing(RenderWindow *rw)
{
	// send quit info to standard output
	std::cout << m_pPlayer->getQuitMsg().c_str() << std::endl;
	return true;
}
