/*
Copyright (c) 2000-2001 Olivier Dagenais

This software is provided 'as-is', without any express or implied warranty. In
no event will the authors be held liable for any damages arising from the use
of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not claim
that you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is not
required.

2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.

3. This notice may not be removed or altered from any source distribution.
*/
#include "GopherTypes.h"
#include "GopherLoader.h"
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include "XMLutils.h"

void loadLibraries ( const DOMDocument & doc, DemoStruct & currentDemo );
void loadDemoSettings ( const DOMDocument & doc, DemoStruct & currentDemo );
void loadDemoTracks ( const DOMDocument & doc, DemoStruct & currentDemo );
char * createStringBuffer ( const XMLCh * source );
unsigned long StringToLong ( const XMLCh * source );


void GopherLoadDemoFromXML ( char * fileName, DemoStruct & currentDemo )
{
	XercesDOMParser * parser = NULL;

	try
	{
		Trace ( "Initializing XMLPlatformUtils" );
		XMLPlatformUtils::Initialize ( );

		Trace ( "Creating a new XercesDOMParser" );
		parser = new XercesDOMParser;
		//parser->setValidationScheme ( XercesDOMParser::Val_Always );
		Trace ( "parser->parse" );
		parser->parse ( fileName );
		Trace ( "Retrieving the DOM document" );
		DOMDocument * doc = parser->getDocument ( );

		Trace ( "Loading the libraries" );
		loadLibraries ( *doc, currentDemo );
		Trace ( "Loading Demo Settings" );
		loadDemoSettings ( *doc, currentDemo );
		Trace ( "Loading Demo Tracks" );
		loadDemoTracks ( *doc, currentDemo );
	}
	catch ( const XMLException & toCatch )
	{
		toCatch;
		fprintf ( stderr, "An XML error occurred while loading demo from XML file." );
		throw;
	}
	catch ( ... )
	{
		fprintf ( stderr, "An error occurred while loading demo from XML file." );
		throw;
	}

	if ( parser != NULL )
	{
		delete parser;
	}
	XMLPlatformUtils::Terminate ( );
}

void loadDemoTracks ( const DOMDocument & doc, DemoStruct & currentDemo )
{
	XMLCh tempStr[100];
	const char * const trackString = "Track";
	XMLString::transcode ( trackString, tempStr, 99 );
	DOMNodeList * trackList = doc.getElementsByTagName ( tempStr );
	unsigned long countTracks, numTracks;
	numTracks = trackList->getLength ( );
	currentDemo.numTracks = numTracks;
	currentDemo.tracks = new TrackStruct[numTracks];

	for ( countTracks = 0; countTracks < numTracks; countTracks++ )
	{
		DOMNode * track = trackList->item ( countTracks );
		TrackStruct * currentTrack = &(currentDemo.tracks[countTracks]);
		unsigned long countEvents, numEvents;
		ElementIterator eventList ( *track );

		numEvents = eventList.getCount ( );
		currentTrack->numEvents = numEvents;
		currentTrack->events = new EventStruct[numEvents];

		for ( countEvents = 0; countEvents < numEvents; countEvents++ )
		{
			DOMNode * event = eventList.getNext ( );
			EventStruct * currentEvent = &(currentTrack->events[countEvents]);
			currentEvent->libraryName = createStringBuffer ( GetAttribute ( *event, "LibraryFileName" ) );
			currentEvent->functionName = createStringBuffer ( GetAttribute ( *event, "FunctionName" ) );

			currentEvent->startTime = StringToLong ( GetAttribute ( *event, "StartTime" ) );
			currentEvent->endTime = StringToLong ( GetAttribute ( *event, "EndTime" ) );

			ElementIterator parameters ( *event, "param" );
			unsigned long numParams, countParams;
			numParams = parameters.getCount ( );
			currentEvent->numParams = numParams;
			if ( numParams > 0 )
			{
				currentEvent->startParameters = new PARAM[numParams];
				currentEvent->endParameters = new PARAM[numParams];
				for ( countParams = 0; countParams < numParams; countParams++ )
				{
					DOMNode * param = parameters.getNext ( );
					currentEvent->startParameters[countParams] = StringToLong ( GetAttribute ( *param, "start" ) );
					currentEvent->endParameters[countParams] = StringToLong ( GetAttribute ( *param, "end" ) );
				}
			}
			else
			{
				currentEvent->startParameters = NULL;
				currentEvent->endParameters = NULL;
			}
		}
	}
}

void loadDemoSettings ( const DOMDocument & doc, DemoStruct & currentDemo )
{
	DOMNode * settings = GetFirstElementByTagName ( doc, "Settings" );

	currentDemo.runningTime = StringToLong ( GetAttribute ( *settings, "RunningTime" ) );
	XMLCh trueString[5];
	XMLString::transcode ( "true", trueString, 4 );
	currentDemo.useOpenGL = XMLString::equals ( GetAttribute ( *settings, "UseOpenGL" ), trueString );
	currentDemo.numInputSurfaces = StringToLong ( GetAttribute ( *settings, "NumberOfInputSurfaces" ) );
	currentDemo.musicFile = createStringBuffer ( GetAttribute ( *settings, "InputMusicFile" ) );
}


void loadLibraries ( const DOMDocument & doc, DemoStruct & currentDemo )
{
	// 1 - Count number of Library tags hence how many LibraryStructs to allocate
	unsigned long countLibs, numLibs;
	XMLCh libraryString[10];
	XMLString::transcode ( "Library", libraryString, 9 );
	DOMNodeList * libraryList = doc.getElementsByTagName ( libraryString );
	numLibs = libraryList->getLength ( );
	currentDemo.numLibraries = numLibs;
	currentDemo.libraries = new LibraryStruct[numLibs];

	// 2 - Populate each LibraryStruct
	for ( countLibs = 0; countLibs < numLibs; countLibs++ )
	{
		LibraryStruct * currentLibrary;
		DOMNode * library = libraryList->item ( countLibs );
		const XMLCh * actualFileName = GetAttribute ( *library, "FileName" );
		currentLibrary = &(currentDemo.libraries[countLibs]);
		currentLibrary->libraryName = createStringBuffer ( actualFileName );

		// 3 - Count how many Function objects and hence how many to allocate
		unsigned countFuncs, numFuncs;
		ElementIterator functionList ( *library );
		numFuncs = functionList.getCount ( );
		currentLibrary->numFunctions = numFuncs;
		currentLibrary->functions = new FunctionStruct[numFuncs];

		// 4 - Populate each Function object
		for ( countFuncs = 0; countFuncs < numFuncs; countFuncs++ )
		{
			DOMNode * function = functionList.getNext ( );
			FunctionStruct * currentFunction;
			currentFunction = &(currentLibrary->functions[countFuncs]);

			const XMLCh * actualName = GetAttribute ( *function, "Name" );
			currentFunction->functionName = createStringBuffer ( actualName );

			// We don't care what the names of those parameters are, they are only
			// kept in the XML file for the editor.  : )
			ElementIterator parameterList ( *function );
			currentFunction->numParameters = parameterList.getCount ( );
		}
	}
}


char * createStringBuffer ( const XMLCh * source )
{
	return XMLString::transcode ( source );
	/*
	char * destination;
	unsigned int len, c;
	len = source->length ( );
	destination = (char *) malloc ( len + 1 );
	for ( c = 0; c < len; c++ )
	{
		destination[c] = ( char ) source->charAt ( c );
	}
	destination[c] = 0;
	return ( destination );
	*/
}


unsigned long StringToLong ( const XMLCh * source )
{
	//char * temporaryBuffer = createStringBuffer ( source );
	char * temporaryBuffer = XMLString::transcode ( source );
	unsigned long retval = atol ( temporaryBuffer );
	free ( temporaryBuffer );
	return ( retval );
}

