//|||||||||||||||||||||||||||||||||||||||||||||||

#include "AppStateManager.hpp"

#include <OgreWindowEventUtilities.h>

//|||||||||||||||||||||||||||||||||||||||||||||||

AppStateManager::AppStateManager()
{
	m_bShutdown = false;	
}

//|||||||||||||||||||||||||||||||||||||||||||||||

AppStateManager::~AppStateManager()
{
	state_info si;

    while(!m_ActiveStateStack.empty())
	{
		m_ActiveStateStack.back()->exit();
		m_ActiveStateStack.pop_back();
	}

	while(!m_States.empty())
	{
		si = m_States.back();
        si.state->destroy();
        m_States.pop_back();
	}
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void AppStateManager::manageAppState(Ogre::String stateName, AppState* state)
{
	try
	{
		state_info new_state_info;
		new_state_info.name = stateName;
		new_state_info.state = state;
		m_States.push_back(new_state_info);
	}
	catch(std::exception& e)
	{
		delete state;
		throw Ogre::Exception(Ogre::Exception::ERR_INTERNAL_ERROR, "Error while trying to manage a new AppState\n" + Ogre::String(e.what()), "AppStateManager.cpp (39)");
	}
}

//|||||||||||||||||||||||||||||||||||||||||||||||

AppState* AppStateManager::findByName(Ogre::String stateName)
{
	std::vector<state_info>::iterator itr;

	for(itr=m_States.begin();itr!=m_States.end();itr++)
	{
		if(itr->name==stateName)
			return itr->state;
	}

	return 0;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void AppStateManager::start(AppState* state)
{	
	changeAppState(state);	

	const int updateIntervalUs = 16666; // 16.666 ms
	unsigned long timeSinceLastFrameMs = 1;
	unsigned long timeSinceLastFrameUs = 1;
	unsigned long startTimeUs = 0;	

	while(!m_bShutdown)
	{
		if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isClosed())m_bShutdown = true;

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		Ogre::WindowEventUtilities::messagePump();
#endif
		if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isActive())
		{
			if (OgreFramework::getSingletonPtr()->isFixedFrameRate())
			{
				// Mingyu: fixed framerate at 60 Hz				
				timeSinceLastFrameUs = OgreFramework::getSingletonPtr()->m_pTimer->getMicroseconds() - startTimeUs;
			
				if (timeSinceLastFrameUs > updateIntervalUs)
				{					
					startTimeUs = OgreFramework::getSingletonPtr()->m_pTimer->getMicroseconds();
					timeSinceLastFrameMs = timeSinceLastFrameUs / 1000;

					OgreFramework::getSingletonPtr()->m_pKeyboard->capture();
					OgreFramework::getSingletonPtr()->m_pMouse->capture();					

					m_ActiveStateStack.back()->update(timeSinceLastFrameMs);

					OgreFramework::getSingletonPtr()->updateOgre(timeSinceLastFrameMs);
					OgreFramework::getSingletonPtr()->m_pRoot->renderOneFrame();
				}				
				// Mingyu:
				// Don't use Sleep here. Even 1ms, Sleep(1), is too long (no usleep on windows)				
			}
			else
			{
				// original version			
				startTimeUs = OgreFramework::getSingletonPtr()->m_pTimer->getMicrosecondsCPU();

				OgreFramework::getSingletonPtr()->m_pKeyboard->capture();
				OgreFramework::getSingletonPtr()->m_pMouse->capture();
				
				m_ActiveStateStack.back()->update(timeSinceLastFrameMs);

				OgreFramework::getSingletonPtr()->updateOgre(timeSinceLastFrameMs);
				OgreFramework::getSingletonPtr()->m_pRoot->renderOneFrame();

 				timeSinceLastFrameUs = OgreFramework::getSingletonPtr()->m_pTimer->getMicrosecondsCPU() - startTimeUs;
				timeSinceLastFrameMs = timeSinceLastFrameUs / 1000;
			}
		}
		else // m_pRenderWnd->isActive()
		{
			Sleep(1000);
		}
	}

	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Main loop quit");
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void AppStateManager::changeAppState(AppState* state)
{
	if(!m_ActiveStateStack.empty())
	{
		m_ActiveStateStack.back()->exit();
		m_ActiveStateStack.pop_back();
	}

	m_ActiveStateStack.push_back(state);
	init(state);
	m_ActiveStateStack.back()->enter();
}

//|||||||||||||||||||||||||||||||||||||||||||||||

bool AppStateManager::pushAppState(AppState* state)
{
	if(!m_ActiveStateStack.empty())
	{
		if(!m_ActiveStateStack.back()->pause())
			return false;
	}

	m_ActiveStateStack.push_back(state);
	init(state);
	m_ActiveStateStack.back()->enter();

	return true;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void AppStateManager::popAppState(void)
{
	if(!m_ActiveStateStack.empty())
	{
		m_ActiveStateStack.back()->exit();
		m_ActiveStateStack.pop_back();
	}

	if(!m_ActiveStateStack.empty())
	{
		init(m_ActiveStateStack.back());
		m_ActiveStateStack.back()->resume();
	}
    else
		shutdown();
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void AppStateManager::pauseAppState(AppState* state)
{
	if(!m_ActiveStateStack.empty())
	{
		m_ActiveStateStack.back()->pause();
	}

	if(m_ActiveStateStack.size() > 2)
	{
		init(m_ActiveStateStack.back());
		m_ActiveStateStack.back()->resume();
	}
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void AppStateManager::shutdown()
{
	m_bShutdown = true;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void AppStateManager::init(AppState* state)
{
    OgreFramework::getSingletonPtr()->m_pKeyboard->setEventCallback(state);
	OgreFramework::getSingletonPtr()->m_pMouse->setEventCallback(state);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->setListener(state);

	OgreFramework::getSingletonPtr()->m_pRenderWnd->resetStatistics();
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void AppStateManager::popAllAndPushAppState(AppState* state)
{
    while(!m_ActiveStateStack.empty())
    {
        m_ActiveStateStack.back()->exit();
        m_ActiveStateStack.pop_back();
    }

    pushAppState(state);
}

//|||||||||||||||||||||||||||||||||||||||||||||||
// Mingyu:
bool AppStateManager::pushAppStateFrom(AppState* stateTo, AppState* stateFrom)
{
	if(!m_ActiveStateStack.empty())
	{
		if(!m_ActiveStateStack.back()->pause())
			return false;
	}

	m_ActiveStateStack.push_back(stateTo);
	init(stateTo);
	stateTo->setAppStateFrom(stateFrom);
	m_ActiveStateStack.back()->enter();

	return true;
}
//|||||||||||||||||||||||||||||||||||||||||||||||

Ogre::String AppStateManager::getStateName(AppState* state)
{
	std::vector<state_info>::iterator itr;

	for(itr=m_States.begin();itr!=m_States.end();itr++)
	{
		if (itr->state == state)
			return itr->name;		
	}
	return "unknown state";
}

//|||||||||||||||||||||||||||||||||||||||||||||||