#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#include "WimpLib:Exception.h"
#include "WimpLib:File.h"
#include "WimpLib:mem.h"
#include "WimpLib:Task.h"
#include "WimpLib:Utils.h"
#include "WimpLib:Window.h"

#include "Setup.h"
#include "Media.h"
#include "DigitalCD.h"

extern MediaDriver CDDriver;
extern MediaDriver UrlDriver;
extern MediaDriver AmpDriver;
extern MediaDriver DSDriver;
extern MediaDriver MidiDriver;
extern MediaDriver PlayItdrv;
extern MediaDriver QTMDriver;
extern MediaDriver TimDriver;
extern MediaDriver FileDriver;
extern MediaDriver FileExtDriver;
extern MediaDriver TRKDriver;
extern MediaDriver XprDriver;

static CLoadFileType* throw_New_LoadFileType(int type, MediaDriver* pDriver)
{
	CLoadFileType* pList = throw_mem_alloc(sizeof(CLoadFileType));

	pList->next = NULL;
	pList->type = type;
	pList->pdriver = pDriver;

	return pList;
}

static void Delete_LoadFileType(CLoadFileType* pList)
{
	if (pList)
		mem_free(pList);
}

static struct
{
	int nr_types;
	CLoadFileType* first;
} LoadTypes;

void throw_LoadTypes_LoadTypes(void)
{
	throw_Media_RegisterDriver(&CDDriver);
	throw_Media_RegisterDriver(&UrlDriver);
	throw_Media_RegisterDriver(&AmpDriver);
	throw_Media_RegisterDriver(&PlayItdrv);
	throw_Media_RegisterDriver(&TimDriver);
	throw_Media_RegisterDriver(&FileDriver);
	throw_Media_RegisterDriver(&MidiDriver);
	if (!(DigitalCD.Hardware.Capabilities & Hardware_ARM32))
	{
		throw_Media_RegisterDriver(&DSDriver);
		throw_Media_RegisterDriver(&QTMDriver);
		throw_Media_RegisterDriver(&TRKDriver);
		throw_Media_RegisterDriver(&XprDriver);
	}
	throw_Media_RegisterDriver(&FileExtDriver);

	LoadTypes.nr_types = 0;
	LoadTypes.first = NULL;
	throw_LoadTypes_Load();
}

void LoadTypes_NotLoadTypes(void)
{
	CLoadFileType* pList;

	while(LoadTypes.first)
	{
		pList = LoadTypes.first;
		LoadTypes.first = pList->next;
		Delete_LoadFileType(pList);
	}

	Media_UnregisterDriver(&FileExtDriver);
	if (!(DigitalCD.Hardware.Capabilities & Hardware_ARM32))
	{
		Media_UnregisterDriver(&XprDriver);
		Media_UnregisterDriver(&TRKDriver);
		Media_UnregisterDriver(&QTMDriver);
		Media_UnregisterDriver(&DSDriver);
	}
	Media_UnregisterDriver(&MidiDriver);
	Media_UnregisterDriver(&FileDriver);
	Media_UnregisterDriver(&TimDriver);
	Media_UnregisterDriver(&PlayItdrv);
	Media_UnregisterDriver(&AmpDriver);
	Media_UnregisterDriver(&UrlDriver);
	Media_UnregisterDriver(&CDDriver);
}

int LoadTypes_Count(void)
{
	return LoadTypes.nr_types;
}

const CLoadFileType* LoadTypes_GetNext(const CLoadFileType* pList)
{
	if (!pList) return LoadTypes.first;

	return pList->next;
}

CLoadFileType* throw_LoadTypes_AddType(int Type, MediaDriver* pDriver)
{
	CLoadFileType* pList = throw_New_LoadFileType(Type, pDriver);
	CLoadFileType** ppLast = &LoadTypes.first;

	if (!pList) return NULL;

	while(   (*ppLast != NULL)
	      && ((*ppLast)->type <= Type))
		ppLast = &((*ppLast)->next);

	if (*ppLast != NULL)
		pList->next = *ppLast;

	*ppLast = pList;

	LoadTypes.nr_types++;

	return pList;
}

const CLoadFileType* LoadTypes_FindNext(int Type, const CLoadFileType* pList)
{
	if (!pList)
		pList = LoadTypes.first;
	else	pList = pList->next;

	while (pList != NULL)
	{
		if (pList->type == Type) return pList;
		pList = pList->next;
	}

	return NULL;
}

void throw_LoadTypes_Load(void)
{
	FILE*		file;
	static char	string[256];
	long int	restorepos;
	long int	endpos;
	char*		pValue;

	// scan the Setup file for the section FileTypes
	if ((file = fopen("DigitalCDRes:Setup", "rb")) == NULL)
	{
		App_ReportError("STErr0");
		return;
	}

	try
	{
		if (File_FindSection(file, "FileTypes", true))
		{
			// found
			restorepos = ftell(file);
			File_FindEndOfSection(file);
			endpos = ftell(file);
			fseek(file, restorepos, SEEK_SET);

			while(ftell(file) < endpos)
			{
				char* pc;
				char* pstr;
				file_type type;

				// extract a line
				if (fgets(string, 255, file) == NULL)
					throw_last_oserror();

				// find '=' in string
				pValue = strchr(string, '=');

				// loop if not found
				if (pValue == NULL) continue;

				*pValue = 0;
				pValue++;
				String_StripBlanks(pValue);
				if (!*pValue) continue;

				String_StripBlanks(string);

				// Ignore boot flag
				pstr = string;
				if (*pstr == '!') pstr++;

				if (!*pstr) continue;

				type = File_TypeFromString(pstr);
				if ((type < 0) || (type >= 0x1000)) continue;

				pstr = pValue;

				pc = pValue;
				while(*pc)
				{
					*pc = toupper(*pc);
					pc++;
				}

				for(; pValue != NULL; pValue = (pstr) ? pstr + 1 : NULL)
				{
					pstr = strchr(pValue, ',');
					if (pstr) *pstr = '\0';
					String_StripBlanks(pValue);

					if (!strcmp(pValue, "AMP"))
						throw_LoadTypes_AddType(type, &AmpDriver);
					else if (!strcmp(pValue, "PLAYIT"))
						throw_LoadTypes_AddType(type, &PlayItdrv);
					else if (!strcmp(pValue, "SMP"))
						throw_LoadTypes_AddType(type, &FileDriver);
					else if (!strcmp(pValue, "TIM"))
						throw_LoadTypes_AddType(type, &TimDriver);
					else if (!strcmp(pValue, "SMPEXT"))
						throw_LoadTypes_AddType(type, &FileExtDriver);
					else if (!strcmp(pValue, "MID"))
						throw_LoadTypes_AddType(type, &MidiDriver);
					else if (!(DigitalCD.Hardware.Capabilities & Hardware_ARM32))
					{
						if (!strcmp(pValue, "DS"))
							throw_LoadTypes_AddType(type, &DSDriver);
						else if (!strcmp(pValue, "QTM"))
							throw_LoadTypes_AddType(type, &QTMDriver);
						else if (!strcmp(pValue, "TRK"))
							throw_LoadTypes_AddType(type, &TRKDriver);
						else if (!strcmp(pValue, "XPR"))
							throw_LoadTypes_AddType(type, &XprDriver);
					}
				}
			}
		}
	}
	catch
	{
		const exception* e = exception_current();

		fclose(file);

		// Report only errors not due to write protected file
		if (e->type != exception_user)
			throw_current();
		else
			throw_string("STErr0");
	}
	catch_end

	fclose(file);
}
