/*********************************************************************
*	SystemConfig
*	SOURCE FILE
*	Autor:	Michal Jirouš
*	Datum: 7.7.2008
*	Soubor: systemconf.cpp
*	Popis: Modul spravujici zakladni informace o systemu jako jsou
* 			rozliseni, bitova hloubka, jazyk. Nacita config ze souboru
* 			a kontroluje dostupnost nastavenych vlastnosti
**********************************************************************/

#include "sys_config.h"
#include "sys_console.h"
#include <fstream>
#include <iostream>
#include <SDL/SDL_opengl.h>
#include "graphics.h"

using namespace systemConsole;
using namespace std;
//Nastaveni zakladnich informaci
systemconf::SystemInfo::SystemInfo()
{
	m_iResolutionWidth = systemconf::DEFAULT_RESOLUTION_WIDTH;
	m_iResolutionHeight = systemconf::DEFAULT_RESOLUTION_HEIGHT;
	m_iBpp = systemconf::DEFAULT_BPP;
	m_sLanguage = systemconf::DEFAULT_LANGUAGE;
	m_iSkipIntro = systemconf::DEFAULT_SKIPINTRO;
	m_iSystemState = SYSTEM_INIT;
	m_pSystemSurface = NULL;
	m_iWindowedMode = systemconf::DEFAULT_WINDOWEDMODE;
	m_iDeveloper_mode = systemconf::DEFAULT_DEVELOPER;
	m_sMenuMap = DEFAULT_MENUMAP;
}

systemconf::SystemInfo g_SystemInfo;
map<int,int> g_VideoModes;

//Ziska dostupne video mody a ulozi je do mapy
bool determineVideoModes()
{
	g_VideoModes.clear();
	
	SDL_Rect **modes;
	modes = SDL_ListModes(NULL, systemconf::REQUIRED_FLAGS);

	// Jsou vubec nejake mody dostupne?
	if(modes == (SDL_Rect **)0)
	{
		console << "No video modes available" << endline;
		return false;
	}

	// Jsou nejaka omezeni na rozliseni?
	if(modes == (SDL_Rect **)-1)
	{
		console << "All video modes available" << endline;
	}
	else
	{	
		//zjisteni dostupnych modu
		console << "Available video modes:" << endline;
		for(int i = 0; modes[i]; i++)
		{
			console << modes[i]->w << "x" << modes[i]->h << endline;	//vypis
			if( modes[i]->w >= systemconf::MINIMUM_WIDTH )
				g_VideoModes.insert( make_pair( modes[i]->w, modes[i]->h) );	//ulozeni
		}
	}
	return true;
}

void systemconf::setGameDirectory( std::string gameDir )
{
	g_SystemInfo.m_sGameDirectory = gameDir;
}

//Nacteni nastaveni z konfiguracniho souboru
bool systemconf::loadConfig(  const char* filename )
{
	IFSTREAM_OPEN( fin, filename, ios::in );
	
	if( fin.is_open() )
	{
		string parameter;
		string data;
		while( 1 )
		{
			fin >> parameter;
			if( fin.eof() )
				break;
			
			fin >> data;	//nacteni znaku "="
			fin >> data;
			
			setParameter( parameter, data );
		}
		
		fin.close();
	}
	else
		return false;
	return init();
}

//inicializace a kontrola aktualni konfigurace ( v pripade inicializace
//se kontroluji pocatecni parametry )
bool systemconf::init()
{
	if(SDL_Init(SUBSYSTEMS) == -1)// Inicializace SDL
	{
		console << "Unable to initialize SDL: " <<	SDL_GetError() << endline;
		return false;
	}

	if( !determineVideoModes() || !checkAndCorrectParameters() )
			return false;
	return true;
}



bool systemconf::initVideo()
{
	//nastaveni OpenGL
	console << "Setting OpenGL parameters." << endline;
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, SYSTEM_GL_DOUBLEBUFFER);

	SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, SYSTEM_GL_BUFFER_SIZE);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, SYSTEM_GL_DEPTH_SIZE);
	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, SYSTEM_GL_STENCIL_SIZE);

	SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE, SYSTEM_GL_ACCUM_RED_SIZE);
	SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE, SYSTEM_GL_ACCUM_GREEN_SIZE);
	SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE, SYSTEM_GL_ACCUM_BLUE_SIZE);
	SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE, SYSTEM_GL_ACCUM_ALPHA_SIZE);


	int iWindowFlags = BASIC_FLAGS;
	if( g_SystemInfo.m_iWindowedMode == 0 )
		iWindowFlags |= SDL_FULLSCREEN;

	console << "Creating video surface." << endline;
	g_SystemInfo.m_pSystemSurface = SDL_SetVideoMode(
		g_SystemInfo.m_iResolutionWidth,
		g_SystemInfo.m_iResolutionHeight,
		g_SystemInfo.m_iBpp,
		iWindowFlags );

	if( g_SystemInfo.m_pSystemSurface == NULL)
	{
		console << "Unable to set " << g_SystemInfo.m_iResolutionWidth << "x" << 
					g_SystemInfo.m_iResolutionHeight << "video: " << SDL_GetError() << "." << endline;
		return false;
	}
	
	if( !initOpenGLSettings() )
		return false;

	SDL_WM_SetCaption(systemconf::SYSTEM_WINDOW_TITLE.c_str(),NULL);// Titulek okna

	return true;
}

bool systemconf::initOpenGLSettings()
{
	glClearColor(0, 0, 0, 0);       // barva pozadi
	glClearDepth(1);
    glPolygonMode(GL_FRONT, GL_FILL);           // nastaveni rezimu vykresleni polygonalniho modelu
	glShadeModel(GL_SMOOTH);                        // nastaveni stinovaciho rezimu

	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);// nastaveni rezimu textury
	glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);// vylepseni zobrazovani textur
	glHint(GL_FOG_HINT, GL_DONT_CARE);
	
	glDepthFunc(GL_LEQUAL);
	SDL_WarpMouse( g_SystemInfo.m_iResolutionWidth >> 1, g_SystemInfo.m_iResolutionHeight >> 1 );

	glDrawBuffer(GL_BACK);
	
	
	glEnable(GL_DEPTH_TEST);
	
	glViewport(0, 0, g_SystemInfo.m_iResolutionWidth, g_SystemInfo.m_iResolutionHeight );

	graphics_init();

	return true;
}


//nastavi parametr konfigurace
void systemconf::setParameter( string &parameter, string &data)
{
	if( parameter == COMMAND_RESOLUTION_WIDTH )
		g_SystemInfo.m_iResolutionWidth = atoi( data.c_str() );
	else if( parameter == COMMAND_RESOLUTION_HEIGHT )
		g_SystemInfo.m_iResolutionHeight = atoi( data.c_str() );
	else if( parameter == COMMAND_BPP )
		g_SystemInfo.m_iBpp = atoi( data.c_str() );
	else if( parameter == COMMAND_LANGUAGE )
		g_SystemInfo.m_sLanguage = data;
	else if( parameter == COMMAND_SKIPINTRO )
		g_SystemInfo.m_iSkipIntro = atoi( data.c_str() );
	else if( parameter == COMMANDS_WINDOWEDMODE )
		g_SystemInfo.m_iWindowedMode = atoi( data.c_str() );
	else if( parameter == COMMANDS_DEVELOPER )
		g_SystemInfo.m_iDeveloper_mode = atoi( data.c_str() );
	else if( parameter == COMMANDS_MENUMAP )
		g_SystemInfo.m_sMenuMap =  data;

}

//vraci aktualni systemovou konfiguraci
systemconf::SystemInfo &systemconf::getSystemInfo()
{
	return g_SystemInfo;
}

//ulozi konfiguraci do souboru
void systemconf::saveConfig( const char* filename )
{
	ofstream fout(filename, ios::out | ios::trunc);
	if( !fout.is_open() )
		return;
	
	fout << COMMAND_RESOLUTION_WIDTH << " = " << g_SystemInfo.m_iResolutionWidth << endl;
	fout << COMMAND_RESOLUTION_HEIGHT << " = " << g_SystemInfo.m_iResolutionHeight << endl;
	fout << COMMAND_BPP << " = " << g_SystemInfo.m_iBpp << endl;
	fout << COMMAND_LANGUAGE << " = " << g_SystemInfo.m_sLanguage << endl;
	fout << COMMAND_SKIPINTRO << " = " << g_SystemInfo.m_iSkipIntro << endl;
	fout << COMMANDS_WINDOWEDMODE << " = " << g_SystemInfo.m_iWindowedMode << endl;
	fout << COMMANDS_DEVELOPER << " = " << g_SystemInfo.m_iDeveloper_mode << endl;
	fout << COMMANDS_MENUMAP << " = " << g_SystemInfo.m_sMenuMap << endl;
	fout.close();
}

//otestuje nastaveni a pripadne ho opravi
bool systemconf::checkAndCorrectParameters()
{
	int bpp = SDL_VideoModeOK( g_SystemInfo.m_iResolutionWidth, g_SystemInfo.m_iResolutionHeight, g_SystemInfo.m_iBpp, REQUIRED_FLAGS);

	if( bpp == 0 )	//error
	{
		console << "Error while checking Video mode -> correcting BPP..." << endline;
		bpp = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
		console << "BPP set to " << bpp << endline;	
		g_SystemInfo.m_iBpp = bpp;	//nova hodnota BPP
		
		bpp = SDL_VideoModeOK( g_SystemInfo.m_iResolutionWidth, g_SystemInfo.m_iResolutionHeight, g_SystemInfo.m_iBpp, REQUIRED_FLAGS);
		
		if( bpp == 0 )	//error
		{
			console << "Error while checking Video mode -> correcting resolution..." << endline;
			
			bpp = SDL_GetVideoInfo()->vfmt->BitsPerPixel;

			// Zkusi ziskat dostupna rozliseni podle flagu
			SDL_Rect **modes;
			modes = SDL_ListModes(NULL, REQUIRED_FLAGS);
			
			if(modes == (SDL_Rect **)0)// Zadne dostupne mody
			{
				console << "No video mode available -> Quiting" << endline;
				return false;
			}
			else if(modes == (SDL_Rect **)-1)// Zadna omezeni
			{
				console << "Setting video mode 640x480" << endline;
				g_SystemInfo.m_iResolutionWidth = 640;
				g_SystemInfo.m_iResolutionHeight = 480;
			}
			else// Pouzije maximalni dostupne rozmery
			{
				int i = 0;
				while( modes[i] && modes[i++]->w >= MINIMUM_WIDTH );
				
				if( i == 0 )
				{
					console << "No video mode available -> Quiting" << endline;
					return false;
				}
				g_SystemInfo.m_iResolutionWidth = modes[i-1]->w;
				g_SystemInfo.m_iResolutionHeight = modes[i-1]->h;
				
				console << "Setting minimum video mode " << modes[i-1]->w << "x" << modes[i-1]->h << endline;
				
			}

			bpp = SDL_VideoModeOK( g_SystemInfo.m_iResolutionWidth, g_SystemInfo.m_iResolutionHeight, g_SystemInfo.m_iBpp, REQUIRED_FLAGS);
			if( bpp == 0 )
			{
				console << "Could not correct video mode -> quitting" << endline;
				return false;
			}
			
		}
		
	}
	
	
	//no a stejnak to muze byt spatne...takze..
	map<int,int>::iterator iter = g_VideoModes.find( g_SystemInfo.m_iResolutionWidth );
	if( iter == g_VideoModes.end() || iter->second != g_SystemInfo.m_iResolutionHeight )	//not available
	{
		if( g_VideoModes.empty() )
		{
			console << "No video modes available -> Quitting" << endline;
			return false;
			
		}
		
		console << "Error while checking Video mode -> correcting resolution..." << endline;
		g_SystemInfo.m_iResolutionWidth = g_VideoModes.begin()->first;
		g_SystemInfo.m_iResolutionHeight = g_VideoModes.begin()->second;
		for( map<int,int>::iterator i = g_VideoModes.begin(); i != g_VideoModes.end(); i++)
		{
			if( i->first >= MINIMUM_WIDTH &&  i->first < g_SystemInfo.m_iResolutionWidth )
			{
				g_SystemInfo.m_iResolutionWidth = i->first;
				g_SystemInfo.m_iResolutionHeight = i->second;
			}
		}
		
		console << "Setting minimum video mode " << g_SystemInfo.m_iResolutionWidth << "x" << g_SystemInfo.m_iResolutionHeight << endline;
		
		bpp = SDL_VideoModeOK( g_SystemInfo.m_iResolutionWidth, g_SystemInfo.m_iResolutionHeight, g_SystemInfo.m_iBpp, REQUIRED_FLAGS);
		if( bpp == 0 )
		{
			console << "Could not correct video mode -> quitting" << endline;
			return false;
		}
		
		
	}
	return true;
}

//vraci seznam dostupnych video modu
map<int,int> &systemconf::getVideoModes()
{
	return g_VideoModes;
}

void systemconf::parseCommandLine( int argc, char **argv )
{
	if( argc <= 1 )
		return;

	for( int i = 1; i < argc; i++ )
	{
		string command = argv[i]; 
		
		if( command == COMMAND_SKIPINTRO )
		{
			console << "Skipping game intro..." << endline;
			g_SystemInfo.m_iSkipIntro = TRUE;
		}
		else if( command == COMMANDS_WINDOWEDMODE )
		{
			console << "Windowed mode ON" << endline;
			g_SystemInfo.m_iWindowedMode = TRUE;
		}
		else if( command == COMMANDS_DEVELOPER )
		{
			console << "Developer mode ON" << endline;
			g_SystemInfo.m_iDeveloper_mode = TRUE;
		}
		else if( command == COMMAND_MAP )
		{
			if( ++i < argc )
			{
				g_SystemInfo.m_sStartMap = argv[i];
			}
		
		}
	}

}

void systemconf::requestQuit()
{
	SDL_Event event;
	event.type = SDL_QUIT;
	SDL_PushEvent(&event); 
}


void systemconf::quit()
{
	SDL_Quit();
}
