/*
 * SDL_Config.cpp : Defines the entry point for the DLL application.
 */

#include "SDL_Config.h"

static Uint32 SDL_initialized = 0;
static Uint32 ticks_started = 0;

static int Config_Audio_Open = 0, Config_Video_Open = 0;

/*
BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}
*/


/* Set a few defaults, hopefully these will mean a succesful
 * init on ANY platform should they be used without modification
 */
SDL_CONFIG_API void SDL_Config_DefaultConfig ( SDL_Config_Struct * config )
{
	config->subSystems = 0;

	config->videoDriver = NULL;
	config->width = 320;
	config->height = 200;
	config->bpp = 0;
	config->videoFlags = SDL_SWSURFACE;

	config->audioDriver = NULL;
	config->frequency = 22050;
	config->channels = 1;
	config->format = AUDIO_U8;
	config->audioBuffers = 4096;
}


SDL_CONFIG_API int SDL_Config_LoadConfig ( char * fileName, SDL_Config_Struct * config )
{
	char line[1024];
	size_t lastChar;

	FILE * hFile = fopen ( fileName, "rt" );
	if ( hFile == NULL )
	{
		// It's fine if the file doesn't exist, we'll just use the defaults
		return ( 0 );
	}

	while ( !feof ( hFile ) )
	{
		fgets ( line, 1024, hFile );
		lastChar = strlen ( line ) - 1;
		if ( line[lastChar] == '\n' )
		{
			// replace the "\n" with ASCII 0
			line[lastChar] = 0;
		}
		processOption ( line, config );
	}

	fclose ( hFile );
	return 0;
}

int PrepareOpenGlBpp ( int sourceBpp ) {
	const SDL_VideoInfo* info = NULL;
	Uint8 bpp;

	switch ( sourceBpp ) {
		case 0:
			info = SDL_GetVideoInfo( );
			bpp = info->vfmt->BitsPerPixel;
			if ( bpp != 0 ) {
				return PrepareOpenGlBpp ( bpp );
			}
			break;
		case 8:
			SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 3 );
			SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 3 );
			SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 2 );
			break;
		case 16:
			SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 6 );
			SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
			SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
			break;
		case 24:
			SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
			SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
			SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
			break;
		case 32:
			SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
			SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
			SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
			SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
			break;
	}
	return sourceBpp;
}


SDL_CONFIG_API SDL_Surface * SDL_Config_Init ( SDL_Config_Struct * config )
{
	SDL_Surface * Config_Screen;
	int retval;
	int theAlphaBits;
	int hasDoubleBuffer;


	/* Initialize the video/event subsystem */
	if ( ( config->subSystems & SDL_INIT_VIDEO ) && !(SDL_initialized & SDL_INIT_VIDEO) )
	{
		if ( SDL_VideoInit ( config->videoDriver, ( config->subSystems & SDL_INIT_EVENTTHREAD ) ) < 0 )
		{
			fprintf ( stderr, "SDL_Config Error: Unable to init video.\n" );
			return ( NULL );
		}

		if ( ( config->videoFlags & SDL_OPENGL ) || ( config->videoFlags & SDL_OPENGLBLIT ) ) 
		{
			int bpp = PrepareOpenGlBpp ( config->bpp );
			/* TODO: make the depth buffer also a configurable option */
			SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, bpp );

			hasDoubleBuffer = ( config->videoFlags & SDL_DOUBLEBUF ) ? (1) : (0);
			SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, hasDoubleBuffer );
		}

		Config_Screen = SDL_SetVideoMode ( config->width, config->height, config->bpp, config->videoFlags );
		if ( Config_Screen == NULL )
		{
			fprintf ( stderr, "SDL_Config Error: Unable to open video.\n" );
			return ( NULL );
		}
		
		// Check if we have alpha
		SDL_GL_GetAttribute( SDL_GL_ALPHA_SIZE, &theAlphaBits );
		if (theAlphaBits != 8)
		{
			fprintf ( stderr, "SDL_Config Error: not 8 alpha bits.\n" );
			return ( NULL );
		}
		// Hide the mouse in fullscreen
		if (config->videoFlags & SDL_FULLSCREEN)
			SDL_ShowCursor(SDL_DISABLE);

		Config_Video_Open = -1;
		SDL_initialized |= SDL_INIT_VIDEO;
	}


	/* Initialize the audio subsystem */
	if ( ( config->subSystems & SDL_INIT_AUDIO ) && !(SDL_initialized & SDL_INIT_AUDIO) )
	{
		if ( SDL_AudioInit ( config->audioDriver ) < 0 )
		{
			fprintf ( stderr, "SDL_Config Error: Unable to init audio.\n" );
			return ( NULL );
		}

		if ( Mix_OpenAudio ( config->frequency, config->format, config->channels, config->audioBuffers ) < 0 )
		{
			fprintf ( stderr, "SDL_Config Error: Unable to open audio.\n" );
			return ( NULL );
		}
		Config_Audio_Open = -1;
		SDL_initialized |= SDL_INIT_AUDIO;
	}

	retval = SDL_Init ( config->subSystems & ( SDL_INIT_TIMER | SDL_INIT_JOYSTICK | SDL_INIT_CDROM ) );
	if ( retval == -1 )
	{
		fprintf ( stderr, "SDL_Init Error: Unable to initialize other subsystems.\n" );
		return ( NULL );
	}

	return ( Config_Screen );
}


SDL_CONFIG_API void SDL_Config_FreeConfig ( SDL_Config_Struct * config )
{
	if ( config->audioDriver != NULL )
	{
		free ( config->audioDriver );
	}

	if ( config->videoDriver != NULL )
	{
		free ( config->videoDriver );
	}
}


SDL_CONFIG_API void SDL_Config_Quit ( )
{
	if ( Config_Audio_Open )
	{
		Mix_CloseAudio ( );
		SDL_AudioQuit ( );
		Config_Audio_Open = 0;
	}
	if ( Config_Video_Open )
	{
		SDL_ShowCursor(SDL_ENABLE);
		SDL_VideoQuit ( );
		Config_Video_Open = 0;
	}

	SDL_Quit ( );
}


int setStringOption ( char * value, char ** destination )
{
	char * start;
	char * finish;
	if ( *destination != NULL )
	{
		free ( *destination );
	}

	start = value;
	while ( *start == 32 )
	{
		start++;
	}

	finish = start;
	while ( ( *finish != 32 ) && ( *finish != 0 ) )
	{
		finish++;
	}

	*destination = (char *) malloc ( finish - start + 1);
	if ( *destination == NULL )
	{
		return ( 0 );
	}

	strncpy ( *destination, start, finish - start + 1);

	return ( -1 );
}


int processOption ( char * option, SDL_Config_Struct * config )
{
	if ( strnicmp ( option, "width", 5 ) == 0 )
	{
		if ( option[5] != 0 )
		{
			config->width = atoi ( &option[5] );
			return -1;
		}
	}

	if ( strnicmp ( option, "height", 6 ) == 0 )
	{
		if ( option[6] != 0 )
		{
			config->height = atoi ( &option[6] );
			return -1;
		}
	}

	if ( strnicmp ( option, "bpp", 3 ) == 0 )
	{
		if ( option[3] != 0 )
		{
			config->bpp = atoi ( &option[3] );
			return -1;
		}
	}

	if ( stricmp ( option, "fullscreen" ) == 0 )
	{
		config->videoFlags |= SDL_FULLSCREEN;
		return -1;
	}

	if ( stricmp ( option, "windowed" ) == 0 )
	{
		config->videoFlags &= ~SDL_FULLSCREEN;
		return -1;
	}

	if ( stricmp ( option, "hardware" ) == 0 )
	{
		config->videoFlags |= SDL_HWSURFACE;
		return -1;
	}

	if ( stricmp ( option, "software" ) == 0 )
	{
		config->videoFlags &= ~SDL_HWSURFACE;
		return -1;
	}

	if ( stricmp ( option, "doublebuffer" ) == 0 )
	{
		config->videoFlags |= SDL_DOUBLEBUF;
		return -1;
	}

	if ( stricmp ( option, "singlebuffer" ) == 0 )
	{
		config->videoFlags &= ~SDL_DOUBLEBUF;
		return -1;
	}

	if ( strnicmp ( option, "videodriver", 11 ) == 0 )
	{
		return ( setStringOption ( &option[11], &config->videoDriver ) );
	}

	if ( strnicmp ( option, "frequency", 9 ) == 0 )
	{
		if ( option[9] != 0 )
		{
			config->frequency = atoi ( &option[9] );
			return -1;
		}
	}

	if ( strnicmp ( option, "bps", 3 ) == 0 )
	{
		if ( option[3] != 0 )
		{
			switch ( atoi ( &option[3] ) )
			{
				case 8:
					config->format = AUDIO_U8;
					return -1;
				case 16:
					config->format = AUDIO_S16;
					return -1;
			}
		}
	}

	if ( strnicmp ( option, "channels", 8 ) == 0 )
	{
		if ( option[8] != 0 )
		{
			config->channels = atoi ( &option[8] );
			return -1;
		}
	}

	if ( strnicmp ( option, "audiobuffers", 12 ) == 0 )
	{
		if ( option[12] != 0 )
		{
			config->audioBuffers = atoi ( &option[12] );
			return -1;
		}
	}

	if ( strnicmp ( option, "audiodriver", 11 ) == 0 )
	{
		if ( option[11] != 0 )
		{
			return ( setStringOption ( &option[11], &config->audioDriver ) );
		}
	}

	return 0;
}

