// **********************************************************************
//                            OpenAlchemist
//                        ---------------------
//
//  File        : SDL_CL_ResourceManager.cpp
//  Description : 
//  Author      : Dennis Payne <dulsi@identicalsoftware.com>
//  License     : GNU General Public License 2 or higher
//
// **********************************************************************

#include "SDL_CL_ResourceManager.h"
#include <vector>
#include <zip.h>
#include "expatcpp.h"
#include "CommonResources.h"

#include <cstring>
#include <iostream>

class ResourceXMLParser : public ExpatXMLParser
{
public:
	ResourceXMLParser(SDL_CL_ResourceManager &rm);

	virtual void startElement(const XML_Char *name, const XML_Char **atts);
	virtual void endElement(const XML_Char *name);
	virtual void characterData(const XML_Char *s, int len);
	
	std::vector<std::string> section;
	SDL_CL_ResourceManager &_rm;
	std::shared_ptr<SDL_CL_Sprite_Impl> _cur_sprite;
	std::string _filename;
	int _x, _y, _w, _h;
	std::shared_ptr<SDL_CL_Font_Impl> _cur_font;
};

ResourceXMLParser::ResourceXMLParser(SDL_CL_ResourceManager &rm) : _rm(rm), _cur_sprite(NULL)
{
}

void ResourceXMLParser::startElement(const XML_Char *name, const XML_Char **atts)
{
	if (strcmp(name, "section") == 0)
	{
		std::string section_name;
		std::string ref;
		for (int i = 0; atts[i]; i += 2)
		{
			if (strcmp(atts[i], "name") == 0)
			{
				section_name = atts[i + 1];
			}
			if (strcmp(atts[i], "ref") == 0)
			{
				ref = atts[i + 1];
			}
		}
		std::string pathname;
		int value = 0;
		for (auto itr : section)
		{
			pathname += itr;
			pathname += "/";
		}
		pathname += section_name;
		_rm._section_names.push_back(pathname);
		section.push_back(section_name);
		if (!ref.empty())
			_rm._section_ref[pathname] = ref;
	}
	else if (strcmp(name, "integer") == 0)
	{
		std::string pathname;
		int value = 0;
		for (auto itr : section)
		{
			pathname += itr;
			pathname += "/";
		}
		for (int i = 0; atts[i]; i += 2)
		{
			if (strcmp(atts[i], "name") == 0)
			{
				pathname += atts[i + 1];
			}
			if (strcmp(atts[i], "value") == 0)
			{
				value = atoi(atts[i + 1]);
			}
		}
		_rm._int_map[pathname] = value;
	}
	else if (strcmp(name, "boolean") == 0)
	{
		std::string pathname;
		int value = 0;
		for (auto itr : section)
		{
			pathname += itr;
			pathname += "/";
		}
		for (int i = 0; atts[i]; i += 2)
		{
			if (strcmp(atts[i], "name") == 0)
			{
				pathname += atts[i + 1];
			}
			if (strcmp(atts[i], "value") == 0)
			{
				if (strcmp(atts[i + 1], "true") == 0)
					value = 1;
				else
					value = 0;
			}
		}
		_rm._int_map[pathname] = value;
	}
	else if (strcmp(name, "string") == 0)
	{
		std::string pathname;
		std::string value;
		for (auto itr : section)
		{
			pathname += itr;
			pathname += "/";
		}
		for (int i = 0; atts[i]; i += 2)
		{
			if (strcmp(atts[i], "name") == 0)
			{
				pathname += atts[i + 1];
			}
			if (strcmp(atts[i], "value") == 0)
			{
				value = atts[i + 1];
			}
		}
		_rm._string_map[pathname] = value;
	}
	else if (strcmp(name, "sprite") == 0)
	{
		_cur_sprite = std::shared_ptr<SDL_CL_Sprite_Impl>(new SDL_CL_Sprite_Impl);
		for (int i = 0; atts[i]; i += 2)
		{
			if (strcmp(atts[i], "name") == 0)
			{
				section.push_back(atts[i + 1]);
				break;
			}
		}
	}
	else if (strcmp(name, "image") == 0)
	{
		if (_cur_sprite.get())
		{
			_x = _y = _w = _h = -1;
			for (int i = 0; atts[i]; i += 2)
			{
				if (strcmp(atts[i], "file") == 0)
				{
					_filename = atts[i + 1];
					break;
				}
			}
		}
		else
		{
			_x = _y = _w = _h = -1;
			for (int i = 0; atts[i]; i += 2)
			{
				if (strcmp(atts[i], "name") == 0)
				{
					section.push_back(atts[i + 1]);
					break;
				}
			}
		}
	}
	else if (strcmp(name, "image-file") == 0)
	{
		for (int i = 0; atts[i]; i += 2)
		{
			if (strcmp(atts[i], "file") == 0)
			{
				_filename = atts[i + 1];
				break;
			}
		}
	}
	else if (strcmp(name, "grid") == 0)
	{
		if (_x != -1)
		{
			if (_cur_sprite.get())
			{
				_cur_sprite->add_frame(_filename, _x, _y, _w, _h);
			}
		}
		for (int i = 0; atts[i]; i += 2)
		{
			if (strcmp(atts[i], "pos") == 0)
			{
				std::string value(atts[i + 1]);
				char *comma = std::strchr(value.data(), ',');
				if (comma)
				{
					*comma = 0;
					_x = atoi(value.data());
					_y = atoi(comma + 1);
				}
			}
			else if (strcmp(atts[i], "size") == 0)
			{
				std::string value(atts[i + 1]);
				char *comma = std::strchr(value.data(), ',');
				if (comma)
				{
					*comma = 0;
					_w = atoi(value.data());
					_h = atoi(comma + 1);
				}
			}
		}
	}
	else if (strcmp(name, "animation") == 0)
	{
		if (_cur_sprite.get())
		{
			for (int i = 0; atts[i]; i += 2)
			{
				if (strcmp(atts[i], "speed") == 0)
				{
					_cur_sprite->_animation_speed = atoi(atts[i + 1]);
					break;
				}
				if (strcmp(atts[i], "delay") == 0)
				{
					_cur_sprite->_animation_speed = atoi(atts[i + 1]);
					break;
				}
			}
		}
	}
	else if (strcmp(name, "font") == 0)
	{
		_cur_font = std::shared_ptr<SDL_CL_Font_Impl>(new SDL_CL_Font_Impl);
		for (int i = 0; atts[i]; i += 2)
		{
			if (strcmp(atts[i], "name") == 0)
			{
				section.push_back(atts[i + 1]);
				break;
			}
		}
	}
	else if (strcmp(name, "bitmap") == 0)
	{
		if (_cur_font.get())
		{
			for (int i = 0; atts[i]; i += 2)
			{
				if (strcmp(atts[i], "glyphs") == 0)
				{
					_cur_font->_glyphs = atts[i + 1];
				}
				else if (strcmp(atts[i], "letters") == 0)
				{
					_cur_font->_letters = atts[i + 1];
				}
				else if (strcmp(atts[i], "spacelen") == 0)
				{
					_cur_font->_spacelen = atoi(atts[i + 1]);
				}
			}
		}
	}
}

void ResourceXMLParser::endElement(const XML_Char *name)
{
	if (strcmp(name, "section") == 0)
		section.pop_back();
	else if (strcmp(name, "sprite") == 0)
	{
		std::string endname = section.back();
		section.pop_back();
		std::string pathname;
		for (auto itr : section)
		{
			pathname += itr;
			pathname += "/";
		}
		pathname += endname;
		_rm._sprite_map[pathname] = _cur_sprite;
		_cur_sprite = NULL;
	}
	else if (strcmp(name, "image") == 0)
	{
		if (_cur_sprite.get())
		{
			_cur_sprite->add_frame(_filename, _x, _y, _w, _h);
		}
		else
		{
			std::shared_ptr<SDL_CL_Image_Impl> img(new SDL_CL_Image_Impl);
			img->_filename = _filename;
			img->_x = _x;
			img->_y = _y;
			img->_w = _w;
			img->_h = _h;
			std::string endname = section.back();
			section.pop_back();
			std::string pathname;
			for (auto itr : section)
			{
				pathname += itr;
				pathname += "/";
			}
			pathname += endname;
			_rm._image_map[pathname] = img;
		}
	}
	else if (strcmp(name, "font") == 0)
	{
		std::string endname = section.back();
		section.pop_back();
		std::string pathname;
		for (auto itr : section)
		{
			pathname += itr;
			pathname += "/";
		}
		pathname += endname;
		_rm._font_map[pathname] = _cur_font;
		_cur_font = NULL;
	}
}

void ResourceXMLParser::characterData(const XML_Char *s, int len)
{
}

/************************************************************************/
/* Constructor                                                          */
/************************************************************************/
SDL_CL_ResourceManager::SDL_CL_ResourceManager(zip_t *zip_file, const std::string &filename)
{
	_zip_file = zip_file;
	ResourceXMLParser p(*this);
	p.parse(zip_file, filename.c_str());
}

SDL_CL_ResourceManager::SDL_CL_ResourceManager(const std::string &filename)
{
	ResourceXMLParser p(*this);
	p.parse(common_resources_get_instance()->skin_zip, filename.c_str());
	_zip_file = common_resources_get_instance()->skin_zip;
}

/************************************************************************/
/* Destructor                                                           */
/************************************************************************/
SDL_CL_ResourceManager::~SDL_CL_ResourceManager()
{
}

std::vector<std::string> SDL_CL_ResourceManager::get_sub_section_names(const std::string &name) const
{
	std::vector<std::string> result;
	for (auto itr : _section_names)
	{
		if (0 == strncmp(name.c_str(), itr.c_str(), name.length()))
		{
			if (std::string::npos == itr.find_first_of('/', name.length()))
				result.push_back(itr);
		}
		else if (result.size() > 0)
			break;
	}
	return result;
}

bool SDL_CL_ResourceManager::get_boolean_resource(const std::string &name, bool d)
{
	auto itr = _int_map.find(translate_name(name));
	if (itr != _int_map.end())
		return itr->second;
	return d;
}

int SDL_CL_ResourceManager::get_integer_resource(const std::string &name, int d)
{
	auto itr = _int_map.find(translate_name(name));
	if (itr != _int_map.end())
		return itr->second;
	return d;
}

std::string SDL_CL_ResourceManager::get_string_resource(const std::string &name, std::string d)
{
	auto itr = _string_map.find(translate_name(name));
	if (itr != _string_map.end())
		return itr->second;
	return d;
}

bool SDL_CL_ResourceManager::resource_exists(const std::string &name)
{
	std::string final_name = translate_name(name);
	if (_int_map.find(final_name) != _int_map.end())
		return true;
	if (_string_map.find(final_name) != _string_map.end())
		return true;
	if (_sprite_map.find(final_name) != _sprite_map.end())
		return true;
	if (_image_map.find(final_name) != _image_map.end())
		return true;
	if (_font_map.find(final_name) != _font_map.end())
		return true;
	return false;
}

std::string SDL_CL_ResourceManager::translate_name(const std::string name)
{
	for (auto itr : _section_ref)
	{
		if (0 == strncmp(itr.first.c_str(), name.c_str(), itr.first.length()))
		{
			return itr.second + (name.c_str() + itr.first.length());
		}
	}
	return name;
}
