#include "builddefines.h"
#include <stdio.h>
#include "XML.h"
#include "expat.h"
#include "DEBUG.h"
#include "string.h"
#include "Tactical Save.h"
#include "FileMan.h"
#include "MemMan.h"
#include "Debug Control.h"
#include "GameSettings.h"
#include "GameInitOptionsScreen.h"

typedef enum
{
	EXTRAITEMS_ELEMENT_NONE = 0,
	EXTRAITEMS_ELEMENT_EXTRAITEMS,
	EXTRAITEMS_ELEMENT_ITEM,
	EXTRAITEMS_ELEMENT,
} EXTRAITEMS_PARSE_STAGE;

typedef struct
{
	EXTRAITEMS_PARSE_STAGE	curElement;
	CHAR8					szCharData[MAX_CHAR_DATA_LENGTH+1];
	UINT32					currentDepth;
	UINT32					maxReadDepth;

	UINT32					item;
	UINT32					quantity;
	UINT32					condition;
	UINT32					gridno;
	BOOLEAN					visible;
} ExtraItemsParseData;


static bool gSectorIsLoaded = false;
static UINT8 gX = 0;
static UINT8 gY = 0;
static UINT8 gZ = 0;

/** Process the opening tag in this expat callback.
 */
static void XMLCALL
ExtraItemsStartElementHandle(void *userData, const XML_Char *name, const XML_Char **atts)
{
	ExtraItemsParseData * pData = (ExtraItemsParseData *) userData;

	if(pData->currentDepth <= pData->maxReadDepth) //are we reading this element?
	{
		if(strcmp(name, "ExtraItems") == 0 && pData->curElement == EXTRAITEMS_ELEMENT_NONE)
		{
			pData->curElement = EXTRAITEMS_ELEMENT_EXTRAITEMS;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "Item") == 0 && pData->curElement == EXTRAITEMS_ELEMENT_EXTRAITEMS)
		{
			pData->curElement = EXTRAITEMS_ELEMENT_ITEM;
			pData->maxReadDepth++; //we are not skipping this element

			// set defaults
			pData->quantity = 1;
			pData->condition = 100;
			pData->gridno = 0;
			pData->visible = FALSE;
		}
		else if(pData->curElement == EXTRAITEMS_ELEMENT_ITEM &&
			(strcmp(name, "uiIndex") == 0 ||
			strcmp(name, "quantity") == 0 ||
			strcmp(name, "condition") == 0 ||
			strcmp(name, "gridno") == 0 ||
			strcmp(name, "visible") == 0 ))
		{
			pData->curElement = EXTRAITEMS_ELEMENT;
			pData->maxReadDepth++;
		}
		pData->szCharData[0] = '\0';
	}
	pData->currentDepth++;
}

/** Process any text content in this callback.
 */
static void XMLCALL
ExtraItemsCharacterDataHandle(void *userData, const XML_Char *str, int len)
{
	ExtraItemsParseData * pData = (ExtraItemsParseData *) userData;

	if(pData->currentDepth <= pData->maxReadDepth && strlen(pData->szCharData) < MAX_CHAR_DATA_LENGTH)
		strncat(pData->szCharData,str,__min((unsigned int)len,MAX_CHAR_DATA_LENGTH-strlen(pData->szCharData)));
}

/** Process the closing tag in this expat callback.
 */
static void XMLCALL
ExtraItemsEndElementHandle(void *userData, const XML_Char *name)
{
	ExtraItemsParseData * pData = (ExtraItemsParseData *) userData;

	if(pData->currentDepth <= pData->maxReadDepth) //we're at the end of an element that we've been reading
	{
		if(pData->curElement == EXTRAITEMS_ELEMENT_EXTRAITEMS &&
			strcmp(name, "ExtraItems") == 0)
		{
			pData->curElement = EXTRAITEMS_ELEMENT_NONE;
		}
		else if(pData->curElement == EXTRAITEMS_ELEMENT_ITEM &&
				strcmp(name, "Item") == 0 &&
				pData->item > 0 && // Make sure not to create 0-index objects!
				pData->quantity > 0 && // Quantity should be positive. Not sure what the LIMIT is, if any.
				pData->condition > 0 && pData->condition <= 100 ) // Condition between 1 and 100!
		{
			OBJECTTYPE object;
			CreateItem(pData->item, pData->condition, &object);
			for (unsigned cnt=1; cnt <= pData->quantity; ++cnt) {
				if (gSectorIsLoaded) {
					AddItemToPool( pData->gridno, &object, pData->visible, 0, WORLD_ITEM_REACHABLE, 0 );
				} else {
					AddItemsToUnLoadedSector( gX, gY, gZ, pData->gridno, pData->visible, &object, 0, WORLD_ITEM_REACHABLE, 0, 1, 0 );
				}
			}
			pData->curElement = EXTRAITEMS_ELEMENT_EXTRAITEMS;
		}

		else if( pData->curElement == EXTRAITEMS_ELEMENT &&
				strcmp( name, "uiIndex" ) == 0)
		{
			pData->item = (UINT32)atol( pData->szCharData );
			pData->curElement = EXTRAITEMS_ELEMENT_ITEM;
		}
		else if( pData->curElement == EXTRAITEMS_ELEMENT &&
				strcmp( name, "quantity" ) == 0)
		{
			pData->quantity = (UINT32)atol( pData->szCharData );
			pData->curElement = EXTRAITEMS_ELEMENT_ITEM;
		}
		else if( pData->curElement == EXTRAITEMS_ELEMENT &&
				strcmp( name, "condition" ) == 0)
		{
			pData->condition = (UINT32)atol( pData->szCharData );
			pData->curElement = EXTRAITEMS_ELEMENT_ITEM;
		}
		else if( pData->curElement == EXTRAITEMS_ELEMENT &&
				strcmp( name, "gridno" ) == 0)
		{
			pData->gridno = (UINT32)atol( pData->szCharData );
			pData->curElement = EXTRAITEMS_ELEMENT_ITEM;
		}
		else if( pData->curElement == EXTRAITEMS_ELEMENT &&
				strcmp( name, "visible" ) == 0)
		{
			pData->visible = (UINT32)atol( pData->szCharData );
			pData->curElement = EXTRAITEMS_ELEMENT_ITEM;
		}
		pData->maxReadDepth--;
	}
	pData->currentDepth--;
}


void AddExtraItems(UINT8 x, UINT8 y, UINT8 z, bool sectorIsLoaded)
{
	char fileName[MAX_PATH];
	strcpy(fileName, TABLEDATA_DIRECTORY);
	if (x < 10) {
		strcat(fileName, EXTRAITEMSFILENAME);
		char* spot = strstr(fileName, "A9_0");
		Assert(spot);
		if (!spot)
			return;
		*spot = 'A' + y-1;
		*(spot+1) = '0' + x;
		*(spot+3) = '0' + z;
	} else {
		strcat(fileName, EXTRAITEMSFILENAME2);
		char* spot = strstr(fileName, "A11_0");
		Assert(spot);
		if (!spot)
			return;
		*spot = 'A' + y-1;
		*(spot+1) = '0' + x / 10;
		*(spot+2) = '0' + x % 10;
		*(spot+4) = '0' + z;
	}

	// Remember the base file name length
	int baseFNLength = strlen(fileName);

	// Append a suffix based on the difficulty
	switch (gGameOptions.ubDifficultyLevel)
	{
		case DIF_LEVEL_EASY:
			strcat(fileName, "_Novice");
			break;
		case DIF_LEVEL_MEDIUM:
			strcat(fileName, "_Experienced");
			break;
		case DIF_LEVEL_HARD:
			strcat(fileName, "_Expert");
			break;
		case DIF_LEVEL_INSANE:
			strcat(fileName, "_Insane");
			break;
		default:
			{
				CHAR8 str[20];
				sprintf( str, "_Diff_%d", gGameOptions.ubDifficultyLevel );
				strcat( fileName, str );
			}
			break;		
	}
	
	strcat(fileName, ".xml");
	if(!FileExists(fileName))//dnl ch75 261013 just to avoid sdd::exception under debug from VFS when file not exist
		return;

	// Open extra items file
	HWFILE hFile;
	// Try "special" difficulty level file first
	if (!(hFile = FileOpen( fileName, FILE_ACCESS_READ, FALSE ))) {
		// OK, that didn't work so try the "plain" file
		fileName[baseFNLength] = '\0';
		if (!(hFile = FileOpen( fileName, FILE_ACCESS_READ, FALSE ))) {
			// There isn't a file for this sector, just return
			return;
		}
	}

	gSectorIsLoaded = sectorIsLoaded;
	gX = x;
	gY = y;
	gZ = z;

	UINT32		uiBytesRead;
	XML_Parser	parser = XML_ParserCreate(NULL);
	ExtraItemsParseData pData;

	UINT32 uiFSize = FileGetSize(hFile);
	CHAR8 * lpcBuffer = (CHAR8 *) MemAlloc(uiFSize+1);

	//Read in block
	if ( !FileRead( hFile, lpcBuffer, uiFSize, &uiBytesRead ) ) {
		MemFree(lpcBuffer);
		XML_ParserFree(parser);
		return;
	}

	lpcBuffer[uiFSize] = 0; //add a null terminator

	FileClose( hFile );

	// Setup expat callbacks
	XML_SetElementHandler(parser, ExtraItemsStartElementHandle, ExtraItemsEndElementHandle);
	XML_SetCharacterDataHandler(parser, ExtraItemsCharacterDataHandle);

	memset(&pData,0,sizeof(pData));
	XML_SetUserData(parser, &pData);

	if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE))
	{
		CHAR8 errorBuf[511];

		sprintf(errorBuf, "XML Parser Error in ExtraItems.xml: %s at line %d", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser));
		LiveMessage(errorBuf);

		MemFree(lpcBuffer);
		XML_ParserFree(parser);
		return;
	}

	MemFree(lpcBuffer);

	XML_ParserFree(parser);
	return;
}
