#include "cXMLoader.hpp"

/*** Custom Header Files ***/
#include "../../UtilityEngine/cUtility.hpp"

#include "../cGUI.hpp"
#include "../cLayout.hpp"

using GUIHelpers::cXMLoader;

using UtilityEngine::cUtility;


cXMLoader::cXMLoader()
{
	m_loadDef = false;
	m_orientation = GUIHelpers::eOrientation::DEFAULT_ORIENTATION;
	m_align = GUIHelpers::eAlign::DEFAULT_ALIGN;
	m_layout = GUIHelpers::eLayout::DEFAULT_LAYOUT;
	m_pos = { -1, -1 };
	m_size = { -1, -1 };
	m_pad = { -1, -1, -1, -1 };
}

cXMLoader::~cXMLoader()
{}

///Functions

void cXMLoader::Load( const cString& filename, const cString& dir /*= ""*/, GUIEngine::cWindow* parent /*= nullptr*/ )
{
	cString file = dir + filename;
	tinyxml2::XMLDocument doc;

	if (doc.LoadFile(file.c_str()) != tinyxml2::XMLError::XML_NO_ERROR)
		cUtility::Inst().Message("Could not load file. " + file + " Error='" + doc.ErrorName() + "'.");
	else {
		tinyxml2::XMLElement* element = doc.FirstChildElement();

		if (element == nullptr)
			cUtility::Inst().Message("No tag found.");
		else {
			//OK check for defaults
			tinyxml2::XMLElement* defaultEl = element->FirstChildElement("default");
			if (defaultEl != nullptr)
				Default(*defaultEl);
			tinyxml2::XMLElement* debugEl = element->FirstChildElement("debug");
			if (debugEl != nullptr)
				Debug(*debugEl);
			GetElement(*element, parent);
		}			
	}
	
}

void cXMLoader::GetElement( tinyxml2::XMLElement& element, GUIEngine::cWindow* parent /*= nullptr*/ ) const
{
	cString name = element.Name();
	name = name.lower();
	GUIEngine::cWindow* tmp = nullptr;
	if (name == "include")
		Include(element, parent);
	if (name == "gui")
		GUISetup(element);
	if (name == "panel")
		tmp = PanelBuild(element, parent);
	if (name == "layout")
		tmp = LayoutBuild(element, parent);
	if (name == "label")
		tmp = LabelBuild(element, parent);
	if (name == "boxsizer")
		tmp = BoxSizerBuild(element, parent);

	tinyxml2::XMLElement* child = element.FirstChildElement();
	if (child != nullptr)
		GetElement(*child, tmp);

	tinyxml2::XMLElement* sibling = element.NextSiblingElement();
	if (sibling != nullptr)
		GetElement(*sibling, parent);

	if ((parent == nullptr) && (tmp != nullptr))
		tmp->Resize();
}

const cString cXMLoader::GetAttribute( const tinyxml2::XMLElement& element, const cString& attribute ) const
{
	cString rtn = "";
	const char* str = element.Attribute(attribute.c_str());
	if ((str == nullptr) && (m_debugXML == true))
		cUtility::Inst().Message("Error no attribute: " + attribute + " found in tag <" + element.Value() + ">");
	else
		rtn = str;
	return rtn;
}

void cXMLoader::Include(const tinyxml2::XMLElement& element, GUIEngine::cWindow* parent) const
{
	cString dir = getDir(element);
	cString filename = getFileName(element);

	cXMLoader include;
	include.Load(filename, dir, parent);

	LoadDefault();
}

void cXMLoader::GUISetup( const tinyxml2::XMLElement& element ) const
{
	unsigned long int debug = getDebug(element);

	GUIEngine::cGUI::Inst().setDebugLevel(debug);
}

void cXMLoader::Default( const tinyxml2::XMLElement& element )
{
	m_orientation = getOrientation(element);
	m_align = getAlign(element);
	m_layout = getLayout(element);
	m_pos = getPosition(element);
	m_size = getSize(element);
	m_pos = getPadding(element);

	m_loadDef = true;

	LoadDefault();
}

void cXMLoader::Debug(const tinyxml2::XMLElement& element)
{
	unsigned long int debug = getDebug(element);
	m_debugXML = getDebugXML(element);

	GUIEngine::cGUI::Inst().setDebugLevel(debug);
}

GUIEngine::cPanel* cXMLoader::PanelBuild(const tinyxml2::XMLElement& element, GUIEngine::cWindow* parent) const
{
	signed long int id = getID(element);
	GUIHelpers::eOrientation orientation = getOrientation(element);
	GUIHelpers::eAlign align = getAlign(element);
	GUIHelpers::eLayout layout = getLayout(element);
	GUIHelpers::Position pos = getPosition(element);
	GUIHelpers::Size size = getSize(element);
	GUIHelpers::Padding pad = getPadding(element);

	GUIEngine::cPanel* rtn = new GUIEngine::cPanel(parent, id, orientation, align, layout, pos, size, pad);

	if (parent == nullptr)
		GUIEngine::cGUI::Inst().AddObject(rtn);

	return rtn;
}

GUIEngine::cLayout* cXMLoader::LayoutBuild( const tinyxml2::XMLElement& element, GUIEngine::cWindow* parent ) const
{
	signed long int id = getID(element);
	GUIHelpers::eOrientation orientation = getOrientation(element);
	GUIHelpers::eAlign align = getAlign(element);
	GUIHelpers::eLayout layout = getLayout(element);
	GUIHelpers::Position pos = getPosition(element);
	GUIHelpers::Size size = { -1, -1 };// getSize(element);
	GUIHelpers::Padding pad = getPadding(element);


	GUIEngine::cLayout* rtn = new GUIEngine::cLayout(parent, id, orientation, align, layout, pos, size, pad);


 	if (parent == nullptr)
 		GUIEngine::cGUI::Inst().AddObject(rtn);

	return rtn;
}

GUIEngine::cLabel* cXMLoader::LabelBuild( const tinyxml2::XMLElement& element, GUIEngine::cWindow* parent ) const
{
	signed long int id = getID(element);
	//GUIHelpers::eOrientation orientation = getOrientation(element);
	GUIHelpers::eAlign align = getAlign(element);
	GUIHelpers::eLayout layout = getLayout(element);
	GUIHelpers::Position pos = getPosition(element);
	GUIHelpers::Size size = { -1, -1 };//getSize(element);
	GUIHelpers::Padding pad = getPadding(element);
	GUIHelpers::RGBA colour = getColour(element);
	signed long int fontSize = getFontSize(element);

	cString txt = getText(element);

	GUIEngine::cLabel* rtn = new GUIEngine::cLabel(parent, id, txt, align, layout, pos, size, pad);
	rtn->setFontColour(colour);
	if (fontSize > 0)
		rtn->setFontSize((unsigned long int)fontSize);

	return rtn;
}

GUIEngine::cBoxSizer* cXMLoader::BoxSizerBuild(const tinyxml2::XMLElement& element, GUIEngine::cWindow* parent) const
{
	signed long int id = getID(element);
	//GUIHelpers::eOrientation orientation = getOrientation(element);
	GUIHelpers::eAlign align = getAlign(element);
	GUIHelpers::eLayout layout = getLayout(element);
	GUIHelpers::Position pos = getPosition(element);
	GUIHelpers::Size size = getSize(element);
	GUIHelpers::Padding pad = getPadding(element);

	cString txt = getText(element);

	GUIEngine::cBoxSizer* rtn = new GUIEngine::cBoxSizer(parent, id, align, layout, pos, size, pad);

	return rtn;
}

const cString cXMLoader::getDir(const tinyxml2::XMLElement& element) const
{
	cString rtn = GetAttribute(element, "dir");	
	return rtn;
}

const cString cXMLoader::getFileName(const tinyxml2::XMLElement& element) const
{
	cString rtn = GetAttribute(element, "filename");
	return rtn;
}

const unsigned long int cXMLoader::getDebug( const tinyxml2::XMLElement& element ) const
{
	unsigned long int rtn = 99;
	cString str = GetAttribute(element, "level").lower();

	if (str != "") {
		if (str == "true")
			rtn = 1;
		else {
			if (str != "false")
				rtn = 0;
			else
				rtn = str.ToInt();
		}
	}

	return rtn;
}

const bool cXMLoader::getDebugXML(const tinyxml2::XMLElement& element) const
{
	bool rtn = false;
	cString str = GetAttribute(element, "xml").lower();

	if ((str != "") && (str != "false")) {
		if (str == "true")
			rtn = true;
		else {
			if (str.ToInt() > 0)
				rtn = true;
		}
	}
	return rtn;
}

const signed long int cXMLoader::getID( const tinyxml2::XMLElement& element ) const
{
	cString rtn = GetAttribute(element, "id");
	return rtn.ToInt();
}

const GUIHelpers::eOrientation cXMLoader::getOrientation( const tinyxml2::XMLElement& element ) const
{
	GUIHelpers::eOrientation rtn = GUIHelpers::eOrientation::DEFAULT_ORIENTATION;

	cString str = GetAttribute(element, "orientation").lower();

	if (str == "none")
		rtn = GUIHelpers::eOrientation::NONE;
	if (str == "horizontal")
		rtn = GUIHelpers::eOrientation::HORIZONTAL;
	if (str == "vertical")
		rtn = GUIHelpers::eOrientation::VERTICAL;

	return rtn;
}

const GUIHelpers::eAlign cXMLoader::getAlign( const tinyxml2::XMLElement& element ) const
{
	GUIHelpers::eAlign rtn = GUIHelpers::eAlign::DEFAULT_ALIGN;

	cString str = GetAttribute(element, "align").lower();

	if (str == "center")
		rtn = GUIHelpers::eAlign::CENTER;
	if (str == "top")
		rtn = GUIHelpers::eAlign::TOP;
	if (str == "right")
		rtn = GUIHelpers::eAlign::RIGHT;
	if (str == "bottom")
		rtn = GUIHelpers::eAlign::BOTTOM;
	if (str == "left")
		rtn = GUIHelpers::eAlign::LEFT;

	return rtn;
}

const GUIHelpers::eLayout cXMLoader::getLayout( const tinyxml2::XMLElement& element ) const
{
	GUIHelpers::eLayout rtn = GUIHelpers::eLayout::DEFAULT_LAYOUT;

	cString str = GetAttribute(element, "layout").lower();
	
	if (str == "fill_parent")
		rtn = GUIHelpers::eLayout::FILL_PARENT;
	if (str == "match_parent")
		rtn = GUIHelpers::eLayout::MATCH_PARENT;
	if (str == "wrap_content")
		rtn = GUIHelpers::eLayout::WRAP_CONTENT;

	return rtn;
}

const GUIHelpers::Position cXMLoader::getPosition( const tinyxml2::XMLElement& element ) const
{
	GUIHelpers::Position rtn = { -1, -1 };

	cString str = GetAttribute(element, "position");

	if (str != "") {
		rtn = { 0, 0 };
		std::vector<cString> strV = str.split(",");

		switch (strV.size())
		{
		case 2:
			rtn.y = strV[1].ToInt();
		case 1:
			rtn.x = strV[0].ToInt();
		}
	}

	return rtn;
}

const GUIHelpers::Size cXMLoader::getSize( const tinyxml2::XMLElement& element ) const
{
	GUIHelpers::Size rtn = { -1, -1 };

	cString str = GetAttribute(element, "size");

	if (str != "") {
		rtn = { 0, 0 };
		std::vector<cString> strV = str.split(",");

		switch (strV.size())
		{
		case 2:
			rtn.y = strV[1].ToInt();
		case 1:
			rtn.x = strV[0].ToInt();
		}
	}

	return rtn;
}

const GUIHelpers::Padding cXMLoader::getPadding( const tinyxml2::XMLElement& element ) const
{
	GUIHelpers::Padding rtn = { -1, -1, -1, -1 };

	cString str = GetAttribute(element, "padding");

	if (str != "") {
		rtn = { 0, 0, 0, 0 };
		std::vector<cString> strV = str.split(",");

		switch (strV.size()) 
		{
		case 4:
			rtn.z = strV[3].ToInt();
		case 3:
			rtn.w = strV[2].ToInt();
		case 2:
			rtn.y = strV[1].ToInt();
		case 1:
			rtn.x = strV[0].ToInt();
		}
	}

	return rtn;
}

const cString cXMLoader::getText( const tinyxml2::XMLElement& element ) const
{
	return GetAttribute(element, "text");
}

const GUIHelpers::RGBA cXMLoader::getColour( const tinyxml2::XMLElement& element ) const
{
	GUIHelpers::RGBA rtn = GUIHelpers::DEFAULT;
	cString str = GetAttribute(element, "colour");

	if (str == "")
		str = GetAttribute(element, "color");

	if (str != "") {
		std::vector<cString> strV = str.split(",");

		switch (strV.size())
		{
		case 4:
			rtn.a = strV[3].ToInt();
		case 3:
			rtn.b = strV[2].ToInt();
		case 2:
			rtn.g = strV[1].ToInt();
		case 1:
			if (strV[0].IsInt() == true) {
				rtn.r = strV[0].ToInt();
				if (strV.size() < 4)
					rtn.a = 255;
			}
			else
				rtn = GUIHelpers::StringtoRGBA(str);
			break;
		default:
			
			break;
		}
	}
	return rtn;
}

const signed long int cXMLoader::getFontSize( const tinyxml2::XMLElement& element ) const
{
	signed long int rtn = -1;
	cString str = GetAttribute(element, "fontsize");

	if (str != "") 
		rtn = str.ToInt();

	return rtn;
}

void cXMLoader::LoadDefault() const
{
	if (m_loadDef == false)
		return;
	GUIEngine::cWindow::OrientationDefault(m_orientation);
	GUIEngine::cWindow::AlignDefault(m_align);
	GUIEngine::cWindow::LayoutDefault(m_layout);
	GUIEngine::cWindow::PositionDefault(m_pos);
	GUIEngine::cWindow::SizeDefault(m_size);
	GUIEngine::cWindow::PaddingDefault(m_pad);
}