/*  DS
 *  Copyright (C) Anders Asplund and Joakim Kolsjö 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 "mdemo.h"
#include "dirlist.h"
#include "mvideo.h"
#include "mmusic.h"
#include "mtexture.h"
#include "mmodel.h"
#include "mtimer.h"
#include "mscene.h"
#include "launcher.h"

using namespace std;

namespace DS
{
	DirectoryList* DirectoryList::m_pInstance = 0;
	MModel* MModel::m_pInstance = 0;
	MScene* MScene::m_pInstance = 0;
	MMusic* MMusic::m_pInstance = 0;
	MTexture* MTexture::m_pInstance = 0;
	MTimer* MTimer::m_pInstance = 0;
	MVideo* MVideo::m_pInstance = 0;
	Launcher* Launcher::m_pInstance = 0;
	MDemo* MDemo::m_pInstance = 0;
	
	MDemo::MDemo()
	{
	}
	
	MDemo::~MDemo()
	{		
		delete m_loadstates.pValues;
		delete MScene::GetPointer();
		delete MMusic::GetPointer();
		delete MVideo::GetPointer();
		delete MModel::GetPointer();
		delete MTexture::GetPointer();
		delete MTimer::GetPointer();		
		delete DirectoryList::GetPointer();
#ifdef USE_GUI
		delete Launcher::GetPointer();
#endif
		SDL_Quit();
		exit(0);	
	}
	
	MDemo* MDemo::Instance()
	{
		if(!m_pInstance)
			m_pInstance = new MDemo();
		return m_pInstance;
	}

	void MDemo::Init(int width, int height, int bpp, bool fullscreen,
			const char* pWindowTitle, const char* pInfoText, int samplerate,
			bool sound, const char* pDataDirectory, bool loop, int loopstart)
	{
		m_settings.width = width;		
		m_settings.height = height;
		m_settings.bpp = bpp;
		m_settings.fullscreen = fullscreen;
		m_settings.sound = sound;
		m_settings.title = pWindowTitle;
		m_settings.info = pInfoText;
		m_settings.samplerate = samplerate;
		m_settings.loop = loop;
		m_loopstart = loopstart;	
		
		// Setup random
		srand(0);
			
		// Instance components
		DirectoryList::Instance(pDataDirectory);
		MMusic::Instance(pDataDirectory);
		MVideo::Instance();
		MModel::Instance(pDataDirectory);
		MTexture::Instance(pDataDirectory);
		MTimer::Instance();
		MScene::Instance();
#ifdef USE_GUI
		Launcher::Instance();
#endif
		m_pLoaderScene = 0;
		
		// Get pointers
		m_pMScene = MScene::GetPointer();
		m_pMMusic = MMusic::GetPointer();
		m_pMTexture = MTexture::GetPointer();
		m_pMVideo = MVideo::GetPointer();
		m_pMModel = MModel::GetPointer();
		
		// Create loadstate info
		m_loadstates.nValues = 3;
		m_loadstates.pValues = new float[m_loadstates.nValues];
		m_loadstates.descriptions.push_back("Textures");
		m_loadstates.descriptions.push_back("Models");
		m_loadstates.descriptions.push_back("Scenes");
		for(int i = 0; i < m_loadstates.nValues; i++)
			m_loadstates.pValues[i] = 0;	

	}
	
	int LoadThread(void* pData)
	{
		MDemo* pMDemo = (MDemo*)pData;
		
		// Wait for display to be done before starting load (just for visual effect)
		for(;;)
		{
			if(pMDemo->m_startLoading)
				break;
			if(!pMDemo->m_loadThreadActive)
				return 0;
			
			MTimer::GetPointer()->Delay(100);
		}
		
		// Load data
		if(pMDemo->m_loadThreadActive) {
			if(MTexture::GetPointer()->Load(pMDemo->m_loadstates.pValues[0],
					pMDemo->m_loadThreadActive))
			{
				pMDemo->m_displayThreadActive = false;
				return 0;
			}
		}
				
		if(pMDemo->m_loadThreadActive)		
		{
			if(MModel::GetPointer()->Load(pMDemo->m_loadstates.pValues[1],
					pMDemo->m_loadThreadActive))
			{
				pMDemo->m_displayThreadActive = false;
				return 0;
			}
		}
				
		if(pMDemo->m_loadThreadActive)
		{
			if(MScene::GetPointer()->Load(pMDemo->m_loadstates.pValues[2],
					pMDemo->m_loadThreadActive))
			{
				pMDemo->m_displayThreadActive = false;
				return 0;
			}
		}
		return 0;
	}
	
	int DisplayThread(void* pData)
	{
		MDemo* pMDemo = (MDemo*)pData;
		
		// Init SDL
		if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) < 0) {
			cerr << "Unable to init SDL: " << SDL_GetError() << endl;
			exit(1);
		}	
		
		// Open window
		Settings* pSet = &pMDemo->m_settings;
		MVideo::GetPointer()->Init(pSet->width, pSet->height, pSet->bpp,
						pSet->fullscreen, pSet->title.c_str());
		
		// Load data for loader scene
		Scene* pLoaderScene = MScene::GetPointer()->GetScene(0);
		if(!pLoaderScene) {
			cout << "[DisplayThread] There isn't any loader scene!" << endl;
			return 1;
		}
		pLoaderScene->LoadScene();
		
		// If fullscreen, wait for screen to adjust
		if(pSet->fullscreen)
			MTimer::GetPointer()->Delay(1500);
		
		
		pMDemo->m_startLoading = true;
		MScene::GetPointer()->Run(&pMDemo->m_loadstates,
			pMDemo->m_displayThreadActive, pSet->loop, pMDemo->m_loopstart);
		return 0;
	}
	
	void MDemo::Run(int argc, char **argv)
	{
#ifdef USE_GUI
		// Open launcher
		LauncherConfig config = { m_settings.fullscreen, m_settings.sound, m_settings.loop, m_settings.width, m_settings.height };
		Launcher::GetPointer()->Run(argc, argv, m_settings.title.c_str(),
									m_settings.info.c_str(), &config);
		m_settings.fullscreen = config.Fullscreen;
		m_settings.sound = config.Sound;
		m_settings.loop = config.Loop;
		m_settings.width = config.width;
		m_settings.height = config.height;
#endif

		m_pMMusic->Init(m_settings.samplerate, 128, m_settings.sound);
		m_pMMusic->Load();
									
		m_displayThreadActive = true;
		m_loadThreadActive = true;
		m_startLoading = false;

		m_pLoadThread = SDL_CreateThread(LoadThread, (void*)this);
		m_pDisplayThread = SDL_CreateThread(DisplayThread, (void*)this);
		
		int ret;
		SDL_WaitThread(m_pDisplayThread, &ret);
		m_loadThreadActive = false;
		SDL_WaitThread(m_pLoadThread, &ret);
		delete m_pInstance;
	}
	
	void MDemo::AddScene(Scene* pScene)
	{
		m_pMScene->AddScene(pScene);
	}
}
