#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gccore.h>
#include <malloc.h>
#include <wiisprite.h>
#include <ogc/lwp_watchdog.h>
#include <wiiuse/wpad.h>
#include <tinyxml.h>

#include "theme.h"
#include "drawable.h"

// For multiple accessors, one base class
void *Theme::_document[2];
int Theme::_instance[2] = { 0, 0 };
char Theme::skinPath[2][MAXPATHLEN];

Theme::Theme()
{	
	// Remember that this is theme
	whichVars = 0;
	
	if(!_instance[whichVars])
	{
		// Ensure document pointer is null
		_document[whichVars] = NULL;
		
		// Ensure no invalid path
		skinPath[whichVars][0] = 0;
	}
	
	// Keep track of copies
	_instance[whichVars]++;
}

Theme::~Theme()
{
	// Keep track of copies
	_instance[whichVars]--;
	
	if(!_instance[whichVars])
	{
		// Kill document pointer if needed
		if(_document[whichVars])
		{
			// Cast back
			TiXmlDocument *document = (TiXmlDocument *)(_document[whichVars]);
			
			// Delete
			delete document;
			
			// Ensure null
			_document[whichVars] = NULL;
		}
	}
}

int Theme::loadTheme(const char *url)
{
	// Close old theme
	if(_document[whichVars])
	{
		// Cast back
		TiXmlDocument *document = (TiXmlDocument *)(_document[whichVars]);
		
		// Delete
		delete document;
		
		// Ensure null
		_document[whichVars] = NULL;
	}
	
	// Open new theme
	TiXmlDocument *document = new TiXmlDocument(url);
	
	// Load file
	if(!document->LoadFile())
	{
		// Delete object
		delete document;
		
		// Return error
		return -1;
	}
	
	// Remember document pointer
	_document[whichVars] = document;
	
	// Remember location
	strcpy(skinPath[whichVars], url);
	
	// Strip to directory only
	for(int i = strlen(skinPath[whichVars]); i >= 0; i--)
	{
		if(skinPath[whichVars][i] == '/')
		{
			// Found the end
			skinPath[whichVars][i+1] = 0;
			
			// Exit out
			break;
		}
	}
	
	return 0;
}

void Theme::getThemePath(char *url)
{
	// Copy out the directory where the skin lives
	strcpy(url, skinPath[whichVars]);
}

int Theme::getStringInternal(const char *rootElement, const char *element, char *output)
{
	// Check to make sure document is valid
	if(!_document[whichVars])
	{
		// Nothing
		output[0] = 0;
		
		return 0;
	}
	
	// Grab first element
	TiXmlElement *root = ((TiXmlDocument *)(_document[whichVars]) )->FirstChildElement(rootElement);
	
	// If this doesn't exist, fail
	if(!root)
	{
		// Nothing
		output[0] = 0;
		
		return 0;
	}
	
	// Loop through elements in 'element' separated by '.'
	char *curPos = (char *)element;
	char curElement[MAX_ELEMENT_LENGTH + 1];
	
	// Make sure temporary element doesn't have data
	memset(curElement, 0, MAX_ELEMENT_LENGTH + 1);
	
	// Do actual parsing out
	while(1)
	{
		// Check for end of element
		if(*curPos == '.')
		{
			// Jump to next element
			root = root->FirstChildElement(curElement);
			
			// If this doesn't exist, fail
			if(!root)
			{
				// Nothing
				output[0] = 0;
				
				return 0;
			}
			
			// Erase element
			memset(curElement, 0, MAX_ELEMENT_LENGTH + 1);
		}
		else if(*curPos == 0)
		{
			// End, grab element
			root = root->FirstChildElement(curElement);
			
			// If this doesn't exist, fail
			if(!root)
			{
				// Nothing
				output[0] = 0;
				
				return 0;
			}
			
			// Grab string
			strcpy(output, root->GetText());
			
			// Return success
			return 1;
		}
		else
		{
			// Grab string length so we don't overflow
			int len = strlen(curElement);
			
			if(len < MAX_ELEMENT_LENGTH)
			{
				// Create dummy string
				char tStr[2];
				tStr[0] = *curPos;
				tStr[1] = 0;
				
				// Append
				strcat(curElement, tStr);
			}
		}
		
		// Jump to next character
		curPos++;
	}
}

int Theme::getString(const char *element, char *output)
{
	// Grab normal string
	if(whichVars == 0)
	{
		return getStringInternal("Skin", element, output);
	}
	else
	{
		return getStringInternal("Settings", element, output);
	}
}

int Theme::getString(const char *element, char *output, const char *defaultValue)
{
	// Did the element exist?
	if(!getString(element, output))
	{
		// Set default
		strcpy(output, defaultValue);
		
		// Signify error
		return 0;
	}
	
	// Signify success
	return 1;
}

int Theme::getLong(const char *element, int *value)
{
	// Temporary storage
	char tValue[128];
	char *endptr;
	
	// Grab string
	if(!getString(element, tValue))
	{
		return 0;
	}
	
	// Convert
	*value = strtol(tValue, &endptr, 10);
	
	// Return success
	return 1;
}

int Theme::getLong(const char *element, int *value, int defaultValue)
{
	// Did the element exist?
	if(!getLong(element, value))
	{
		// Set default
		*value = defaultValue;
		
		// Signify error
		return 0;
	}
	
	// Signify success
	return 1;
}

int Theme::getLongHex(const char *element, uint32_t *value)
{
	// Temporary storage
	char tValue[128];
	char *endptr;
	
	// Grab string
	if(!getString(element, tValue))
	{
		return 0;
	}
	
	// Convert
	*value = strtoul(tValue, &endptr, 16);
	
	// Return success
	return 1;
}

int Theme::getLongHex(const char *element, uint32_t *value, uint32_t defaultValue)
{
	// Did the element exist?
	if(!getLongHex(element, value))
	{
		// Set default
		*value = defaultValue;
		
		// Signify error
		return 0;
	}
	
	// Signify success
	return 1;

}

int Theme::getRect(const char *element, Rect *rect)
{
	char tValue[128];
	char tItem[128];
	
	// Remember which part of the rectangle we are filling
	int whichValue = 0;
	
	// Ensure the item doesn't start with anything in it
	memset(tItem, 0, 128);
	
	// Grab the string value
	if(!getString(element, tValue))
	{
		// Return empty rect if nothing
		memset(rect, 0, sizeof(Rect));
		
		// Signify error
		return 0;
	}
	
	// Parse the four values (comma separated)
	char *tStr = tValue;
	
	// Exit condition under first if case
	while(true)
	{
		if(*tStr == ',' || *tStr == 0)
		{
			// Parse out current number
			uint32_t longVal = atoi(tItem);
			
			// Erase current item
			memset(tItem, 0, 128);
			
			// Pop value into correct location
			switch(whichValue)
			{
				case 0:
					rect->x = longVal;
					break;
				case 1:
					rect->y = longVal;
					break;
				case 2:
					rect->width = longVal;
					break;
				case 3:
					rect->height = longVal;
					break;
			}
			
			// Incriment to next location
			whichValue++;
			
			// Exit if end
			if(*tStr == 0)
			{
				// We didn't get it all
				return whichValue;
			}
		}
		else if(*tStr == ' ' || *tStr == '\t' || *tStr == '\n' || *tStr == '\r')
		{
			// Ignore common whitespaces (intentionally left blank)
		}
		else
		{
			// Add to current item
			int tLen = strlen(tItem);
			
			if(tLen < 127)
			{
				// Enough room, add it
				tItem[tLen] = *tStr;
				tItem[tLen + 1] = 0;
			}
		}
		
		// Go to next character in the string
		tStr++;
	}
}

int Theme::getRect(const char *element, Rect *rect, Rect defaultValue)
{
	// Did the element exist?
	if(!getRect(element, rect))
	{
		// Set default
		*rect = defaultValue;
		
		// Signify error
		return 0;
	}
	
	// Signify success
	return 1;
}

void Theme::getThemeAttributes(char *name, char *designer, char *description, char *homepage, char *email)
{
	// Grab all attributes for theme
	getStringInternal("Description", "Name", name);
	getStringInternal("Description", "Designer", designer);
	getStringInternal("Description", "Description", description);
	getStringInternal("Description", "Homepage", homepage);
	getStringInternal("Description", "Email", email);
}
