/*  DS
 *  Copyright (C) Joakim Kolsjö and Anders Asplund 2005
 *	This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.

 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.

 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 
 */
 
#include "mscene.h"
#include "mmusic.h"
#include "mdemo.h"

using namespace std;

namespace DS
{	
	MScene::MScene()
	{
		m_pMVideo = DS::MVideo::GetPointer();
		m_pMTimer = DS::MTimer::GetPointer();
		
		m_move = 1.0;	
		m_Frames = 0;
#ifdef DEBUG
		m_SkipScene = false;
		m_FpsCount = 0;
		m_TotalFps = 0;
#endif
	}
	
	MScene::~MScene()
	{
		if(m_Scene.size() > 0)
		{			
			std::map <int, Scene*>::iterator iter = m_Scene.begin();
			while(iter != m_Scene.end())
			{
				delete iter->second;
				++iter;
			}
		}	
	}
	
	void MScene::Instance()
	{
		if(!m_pInstance)
			m_pInstance = new MScene();
	}
	
	MScene* MScene::GetPointer()
	{
		return m_pInstance;
	}
	
	void MScene::AddScene(Scene* pScene)
	{
		m_Scene[m_Scene.size()] = pScene;
	}
	
	Scene* MScene::GetScene(int number)
	{
		if(number < (int)m_Scene.size() && number >= 0)
			return m_Scene[number];
		else {
			cout << "[MScene::GetScene] There is no scene " << number << endl;
			return 0;
		}
	}
	
	int MScene::Run(LoadStates* pLoadStates, bool &bThreadActive, bool loop, int loopstart)
	{	
		int CurrentScene = 0, RetCode = 0;		
		bool bRunNextScene = false;
		
		if(!m_Scene.size()) {
			cout << "[MScene::Run] No scenes to run?!" << endl;
			return 1;
		}		

		m_pMTimer->Start();
		m_FpsUpdate = m_Frametime = m_SceneStart = m_Demotime = m_pMTimer->Gettime();
		m_Scene[0]->EnterScene();		

#ifdef DEBUG
		cout << "DEBUG | Scene 0 (" << m_Scene[0]->GetIdentity() << ")." << endl;
#endif
		bool bCheckLoading = true;
		
		// Run scenes
		for(;;)
		{
			// Check if we need to check state of loading
			if(bCheckLoading)
			{
				float state = 0;
				for(int i = 0; i < pLoadStates->nValues; i++) {
					state += pLoadStates->pValues[i];
				}
				state /= pLoadStates->nValues;
				
				// If loading is complete, make GL textures
				if(state == 1)
				{
					MTexture::GetPointer()->MakeGLTextures();
					MModel::GetPointer()->LoadGLTextures();
					bCheckLoading = false;
				}
			}
			
			// Check if we have to exit (data probably failed to load)
			if(!bThreadActive)
				return 0;
			
			// Run the current scene
			if(!CurrentScene) // Loader scene
				RetCode = m_Scene[CurrentScene]->DrawFrame((m_pMTimer->Gettime() - m_SceneStart), m_move, pLoadStates);
			else {
				RetCode = m_Scene[CurrentScene]->DrawFrame((m_pMTimer->Gettime() - m_SceneStart), m_move);
			}
								
			// Check if we should run the next scene simultaniously
			if((RetCode == RUN_NEXT_SCENE) && (bRunNextScene == false)) {
				m_NextScenetime = m_pMTimer->Gettime();
				bRunNextScene = true;
			}
			
			if((bRunNextScene == true )
				&& (RetCode == RUN_NEXT_SCENE)
				&& (CurrentScene+1 <= (int)m_Scene.size()))
			{
				m_Scene[CurrentScene+1]->DrawFrame((m_pMTimer->Gettime() - m_NextScenetime), m_move);
			}
						
			// Update events (keyboard, mouse, window)
			int ret;
			if(CurrentScene)
				ret = Update(true);
			else
				ret = Update(false);
			
			if(ret)
				return 0;
			
#ifdef DEBUG
			// For skipping scenes (tab key)
			if(m_SkipScene) {
				RetCode = true;
				m_SkipScene = false;
			}
#endif
			
			// The current scene has ended
			if(RetCode == END_SCENE)
			{	
				m_Scene[CurrentScene]->ExitScene();
#ifdef DEBUG
				if(CurrentScene)
					cout << "Average fps: " << (float)(((float)(m_TotalFps))/((float)(m_FpsCount))) << endl;
				
				m_TotalFps = 0;
				m_FpsCount = 0;
#endif
				// Check if the current scene is the last
				if(++CurrentScene+1 > (int)m_Scene.size())
				{
					// Check if looping is activated
					if(loop)
					{
						if(loopstart < 1 || loopstart >= (int)m_Scene.size())
						{
							cout << "Loopstart isn't a valid scene number!? (" << loopstart << ")." << endl;
							return 1;
						}
						
						CurrentScene = loopstart;
						if(bRunNextScene)
							m_SceneStart = m_pMTimer->Gettime() - m_NextScenetime;
						else
							m_SceneStart = m_pMTimer->Gettime();
						
						bRunNextScene = false;
						
						m_Scene[CurrentScene]->EnterScene();
	#ifdef DEBUG
						cout << "DEBUG | Loop is set, going back to requested loopstart." << endl;
						cout << "DEBUG | Scene " << CurrentScene
							<< " (" << m_Scene[CurrentScene]->GetIdentity() << "): ";
						cout.flush();
	#endif
						
					}
					else {
#ifdef DEBUG
					cout << "DEBUG | Oh no! we ran out of scenes to play :(" << endl;
					cout << "DEBUG | Total running time: " << (int)((m_pMTimer->Gettime() - m_Demotime)/1000) << " seconds." << endl;
#endif
						return 0;
					}
				}
				else
				{
					if(bRunNextScene)
						m_SceneStart = m_pMTimer->Gettime() - m_NextScenetime;
					else
						m_SceneStart = m_pMTimer->Gettime();
					
					bRunNextScene = false;
					
					m_Scene[CurrentScene]->EnterScene();
#ifdef DEBUG
					cout << "DEBUG | Scene " << CurrentScene
						<< " (" << m_Scene[CurrentScene]->GetIdentity() << "): ";
					cout.flush();
#endif
				}
			}
		}
	}
	
	int MScene::Load(float &state, bool &active)
	{
		if(m_Scene.size() == 1)
		{
			cout << "[MScene::Load] There isn't any scene after loader!" << endl;
			return 1;
		}
		
		// Load scenes (all but the loader, scene 0)
		for(int i = 1; i < (int)m_Scene.size(); i++) {
			state = ((float)i/(m_Scene.size()-1));
			
			if(!active)
				return 0;
			
			// Load scene
			if(m_Scene[i]->LoadScene())
				return 1;
		}
		return 0;
	}
	
	int MScene::Update(bool frames)
	{	
#ifdef DEBUG
		if((m_pMTimer->Gettime() - m_FpsUpdate) >= 1000) {
			m_TotalFps += m_Frames;
			++m_FpsCount;
			if(frames)
				cout << m_Frames << " ";
			cout.flush();
			m_Frames = 0;
			m_FpsUpdate = m_pMTimer->Gettime();
		}
		m_Frames++;
#endif
		// Update video, check for input, ...
		m_pMVideo->Update();
		SDL_PollEvent(&m_SDLEvent);
		if(m_SDLEvent.type == SDL_QUIT)
			return 2;		
		else if(m_SDLEvent.type == SDL_KEYDOWN)
		{
			SDLKey ActiveKey = m_SDLEvent.key.keysym.sym;
			if(ActiveKey == SDLK_ESCAPE || ActiveKey == SDLK_q)
			{
#ifdef DEBUG
				cout << endl;
#endif
				return 2;
			}
#ifdef DEBUG	
			else if(ActiveKey == SDLK_SPACE) {
				static long last = m_pMTimer->Gettime();
				static bool first = true;
				if((m_pMTimer->Gettime() - last) > 100 || first) {
					last = m_pMTimer->Gettime();
					first = false;
					cout << "DEBUG | Current scene time: " << m_pMTimer->Gettime() - m_SceneStart << endl;
				}
			}

			else if(ActiveKey == SDLK_TAB)
				m_SkipScene = true;
#endif	
		}
		
#ifdef MOUSEPOS
		if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(1))
		{
			int x, y;
			SDL_GetMouseState(&x, &y);
			static long last = m_pMTimer->Gettime();
			static bool first = true;
			if((m_pMTimer->Gettime() - last) > 200 || first) {
				last = m_pMTimer->Gettime();
				first = false;
				cout << x << ", " << y << ", "; cout.flush();
			}
		}
#endif
		
		// Calcuate move based upon being 1.0 when it's 25 fps
		m_move = (float)(m_pMTimer->Gettime() - m_Frametime)/(1000/25);
		m_Frametime = m_pMTimer->Gettime();
		
		return 0;
	}
}
