/*  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 "ds.h"
#include "../handlers/utils/quad.h"

using namespace std;

namespace DS
{	
	DemoSystem::DemoSystem(int argc, char** argv, const char* pDataSource, const char* pScript)
	{
		static char* fn = "[DemoSystem::DemoSystem]";
		m_pTextureLoader = 0;
		m_pModelLoader = 0;
		m_argc = argc;
		m_argv = argv;
		m_repeat = 1;
		m_pCacheThread = 0;
		
#ifdef DEVEL
		cout << "DEVEL | DemoSystem Init." << endl;
		cout << "DEVEL | Setting data source to " << pDataSource << endl;
#endif
		
		// Resources
#ifndef NOFILE
		string source = pDataSource;
		if(source.substr(source.size()-4, 4) == ".xpk"
			|| (string)pDataSource == argv[0])
			m_pDataReader = new ReadXPK(pDataSource);
		else
			m_pDataReader = new ReadFile(pDataSource);
#else
		m_pDataReader = new ReadXPK(pDataSource);
#endif
				
		m_pDataReader->Init();
		m_pTextureLoader = new TextureLoader(m_pDataReader);
		m_pModelLoader = new ModelLoader(m_pTextureLoader, m_pDataReader);
		
		// Load scriptfile
		m_pScriptFile = m_pDataReader->GetFile(pScript);
		
		// Display/Sound
		if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) < 0) {
			throw Exception(fn, (string)"Failed to init SDL: " + SDL_GetError());
		}
		
		m_pVideo = new Video();
		m_pMusic = new Music(m_pDataReader);
		m_pMusic->Init(22050);
		
		// Random
		srand(time(0));
		
		// Create loadinfo
		m_pLoadInfo = new LoadInfo(1);
		m_pLoadInfo->SetSize(0, 1);
	}
	
	DemoSystem::~DemoSystem()
	{		
		if(m_pTextureLoader)
			delete m_pTextureLoader;
		if(m_pModelLoader)
			delete m_pModelLoader;

		// Wait for CacheThread to return
		if(m_pCacheThread) {
			int ret; m_bLoadingActive = false;
			SDL_WaitThread(m_pCacheThread, &ret);
		}
		
		SDL_Quit();	
	}

	DemoSystem* DemoSystem::m_pInstance = 0;	
	DemoSystem* DemoSystem::Instance(int argc, char** argv, const char* pDataSource, const char* pScript)
	{
		if(!m_pInstance)
			m_pInstance = new DemoSystem(argc, argv, pDataSource, pScript);
		return m_pInstance;
	}
	
	void DemoSystem::Release()
	{
		if(m_pInstance)
			delete m_pInstance;
	}
	
	void DemoSystem::LoadData()
	{
		for(int i = 0; i < (int)m_scenes.size(); i++)
			m_scenes[i]->LoadData();
	}
	
	int DemoSystem::CacheThread(void* pData)
	{
		DemoSystem* pDemoSystem = (DemoSystem*)pData;
		LoadInfo* pLoadInfo = pDemoSystem->m_pLoadInfo;
		
		unsigned int current = 1;
		unsigned int total = pDemoSystem->m_scenes.size();
		for(int i = 0; i < (int)pDemoSystem->m_scenes.size(); i++) {
			if(pDemoSystem->m_bLoadingActive) {
				pLoadInfo->LockState();
				pDemoSystem->m_scenes[i]->CacheData();
				float &state = pLoadInfo->GetState(0);
				state = ((float)current++/total);
				pLoadInfo->ReleaseState();
			}
		}
		
		return 0;
	}
		
	void DemoSystem::RenderLoadstate()
	{
		glClearColor(0.0, 0.0, 0.0, 0);
		//glEnable(GL_DEPTH_TEST);
		//glDepthFunc(GL_LEQUAL);
		//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, 1.0, 0, 1.0, 1, 100);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		long fadeout = 0;
		
		for(;;)
		{
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glLoadIdentity();
			glPushMatrix();
				float state = m_pLoadInfo->GetTotal();
				if(state == 1)
				{
					static long sfadeout = SDL_GetTicks();
					fadeout = SDL_GetTicks() - sfadeout;
//#ifdef DEVEL
	//				break;
//#endif
					if(fadeout > 1000)
						break;
				}
			
				glTranslatef(-0.5 + state, 0.5, -1);
				glColor3f(1-((float)fadeout/1000), 1-((float)fadeout/1000), 1-((float)fadeout/1000));
				DrawQuad(1, 0.05);
				
				m_pVideo->Update();
			glPopMatrix();
			
			SDL_Delay(10);
		}
	}
	
	void DemoSystem::Run()
	{
		// Parse script
		Parse();
		
		// Set resource pointers
		for(int i = 0; i < (int)m_scenes.size(); i++)
			m_scenes[i]->SetResources(m_pDataReader, m_pTextureLoader, m_pModelLoader);
		
		// Config
		if(m_musicfile[0] != '*')
			m_config.sound = true;
		else
			m_config.sound = false;
#ifdef DEVEL
		m_config.fullscreen = false;
#else
		m_config.fullscreen = true;
#endif
		m_config.loop = false;
		m_config.width = 640;
		m_config.height = 480;
				
		// Launcher GUI
#ifdef USE_GUI
#ifdef DEVEL
		cout << "DEVEL | Running launcher." << endl;
#endif				
		Launcher::Instance()->Run(m_argc, m_argv, m_demotitle.c_str(), "xAngle\nxa-005: Spike", &m_config);
		Launcher::Release();
		
		if(m_config.loop)
			m_repeat = -1;
#endif
#ifdef DEVEL
		cout << "DEVEL | Running demo (" << m_demotitle << ")" << endl;
#endif		
					
		// Open window and wait for screen to adjust if fullscreen
		m_pVideo->Init(m_config.width, m_config.height, m_config.fullscreen, m_demotitle.c_str());
		if(m_config.fullscreen)
			SDL_Delay(1500);
			
		// Load music and cache data
		if(m_config.sound)
			m_pMusic->Load(m_musicfile.c_str());
		

#ifdef DEVEL
		cout << "DEVEL | Loading data to cache."; cout.flush();
#endif

		m_bLoadingActive = true;
		m_pCacheThread = SDL_CreateThread(CacheThread, (void*)this);
		RenderLoadstate();
		for(unsigned int i = 0; i < m_scenes.size(); i++)
			m_scenes[i]->LoadData();
		
		// Setup GL
		glShadeModel(GL_SMOOTH);
		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LEQUAL);
		
		glClearDepth(1);
		glClearColor(0.0, 0.0, 0.0, 0);
		glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
		glViewport(0, 0, m_config.width, m_config.height);
				
		// Run demo
		while(m_repeat == -1 || m_repeat--) {
			for(int i = 0; i < (int)m_scenes.size(); i++)
				m_scenes[i]->Reset();
			PlayScenes();
		}
	}
	
	void DemoSystem::PlayScenes()
	{						
		long StartTime = SDL_GetTicks();
		long ctime = StartTime;
#ifdef DEVEL
		long fstart = StartTime;
		long demostart = StartTime;
		int frames = 0;
#endif
		int current_scene = -1;
		
		if(m_config.sound)
			m_pMusic->Play();
					
		for(;;)
		{			
			// Scene selection
			if(current_scene == -1 || !m_scenes[current_scene]->IsActive(ctime))
			{
				bool bFoundActiveScene = false;
				while(!bFoundActiveScene)
				{
					current_scene++;
					if((int)m_scenes.size() == current_scene)
					{
#ifdef DEVEL
						cout << "\nDEVEL | Oh no! we ran out of scenes to play :(" << endl;
						cout << "DEVEL | Total running time: " << (int)((SDL_GetTicks() - demostart)/1000) << " seconds." << endl;
#endif
						break;
					}
					else if(m_scenes[current_scene]->GetActive())
							bFoundActiveScene = true;
				}
				
				if(!bFoundActiveScene)
					break;
				
				StartTime = SDL_GetTicks();					
#ifdef DEVEL
				cout << endl;
				cout << "DEVEL | Scene " << m_scenes[current_scene]->GetID() << ": ";
				cout.flush();
#endif
			}
				
			ctime = SDL_GetTicks() - StartTime;
			
#ifdef DEVEL
			// FPS
			frames++;
			if(SDL_GetTicks() - fstart > 1000)
			{
				cout << frames << " "; cout.flush();
				frames = 0;
				fstart = SDL_GetTicks();
			}
#endif
			// Draw
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glLoadIdentity();
			m_scenes[current_scene]->Draw(ctime);
			
			// Update video and check events
			m_pVideo->Update();
			
			SDL_Event e;
			SDL_PollEvent(&e);
			if(e.type == SDL_QUIT)
			{
#ifdef DEVEL
				cout << "\nDEVEL | Detected SDL_QUIT, shutting down." << endl;
#endif
				m_repeat = 0;
				break;
			}
			else if(e.type == SDL_KEYDOWN)
			{
				SDLKey ActiveKey = e.key.keysym.sym;
				if(ActiveKey == SDLK_ESCAPE || ActiveKey == SDLK_q) {
#ifdef DEVEL
					cout << "\nDEVEL | Detected ESC or Q, shutting down." << endl;
#endif
					m_repeat = 0;
					break;
				}
#ifdef DEVEL
				else if(ActiveKey == SDLK_SPACE) {
					static long last = SDL_GetTicks();
					static bool first = true;
					if((SDL_GetTicks() - last) > 100 || first) {
						last = SDL_GetTicks();
						first = false;
						cout << "\nDEVEL | Current scene time: " << SDL_GetTicks() - StartTime << endl;
					}
				}
#endif
			}
		}
		
		if(m_config.sound)
			m_pMusic->Stop();
	}
}
