#include "Window.h"
#include "Widget.h"
#include "CommonWidgets.h"
#include "UiBuilder.h"
#include "Test.h"


BEGIN_NAMESPACE


enum PropertyType // same as VariantType ?
{
	PT_Int,
	PT_String,
	PT_bool,
};


struct TypedProperty
{
	VariantType				 m_type;
	AbstractProperty<int>*   m_property;
};



// TODO: all this code below is bloaty
// need to trim down or put on a diet the UiBuilder code
// perhaps it is STL making it bloated?
// The object file is almost 1MB

class UiBuilderData
{
public:
	void consumeToken(XmlTokenType a_type, const XmlStringSlice& a_text);
	void addWidgetType(const char* a_name, WidgetType a_value) {
		m_widgetTypeMap.insert(std::make_pair(std::string(a_name), a_value));
	}

	Vector<TypedWidget>					m_parentNode;
	WidgetType							m_currentType;
	WidgetType							m_parentType;
	std::map<std::string,std::string>	m_currentAttributes;
	std::string							m_currentAttributeName;
	std::map<std::string,TypedWidget>	m_widgetMap;
	std::map<std::string,WidgetType>	m_widgetTypeMap;
	std::map<std::string,TypedProperty>	m_propertySet;
};


Property<String> dummyTextProperty;
Property<bool> dummyBoolProperty;
Property<int> dummyIntProperty;


#define ADD_WIDGET_TYPE(wt) \
	m_data->addWidgetType(#wt, WT_##wt)


UiBuilder::UiBuilder(Widget* a_parent)
{
	m_data = new UiBuilderData;
	TypedWidget parent = { WT_Unknown, a_parent };
	m_data->m_parentNode.push_back(parent);
	m_data->m_parentType = WT_Unknown;
	m_data->m_currentType = WT_Unknown;
	dummyTextProperty = "uninitialized!";

	ADD_WIDGET_TYPE(VBox);
	ADD_WIDGET_TYPE(HBox);
	ADD_WIDGET_TYPE(Label);
	ADD_WIDGET_TYPE(RadioButton);
	ADD_WIDGET_TYPE(CheckBox);
	ADD_WIDGET_TYPE(Button);
	ADD_WIDGET_TYPE(Slider);
	ADD_WIDGET_TYPE(ComboBox);
	ADD_WIDGET_TYPE(GroupBox);
	ADD_WIDGET_TYPE(LineEdit);
	ADD_WIDGET_TYPE(VSpace);
	ADD_WIDGET_TYPE(HSpace);
	ADD_WIDGET_TYPE(ProgressBar);
	ADD_WIDGET_TYPE(Item);
}


UiBuilder::~UiBuilder()
{
	delete m_data;
}


void UiBuilder::addGenericProperty(const char* a_name, VariantType a_type, AbstractProperty<int>* a_property)
{
	TypedProperty newProperty = { a_type, a_property };
	m_data->m_propertySet.insert(std::make_pair(std::string(a_name),newProperty));
}


TypedWidget UiBuilder::findWidget(const char* a_name)
{
	return m_data->m_widgetMap[a_name];
}


void UiBuilder::consumeToken(XmlTokenType a_type, const XmlStringSlice& a_text)
{
	m_data->consumeToken(a_type, a_text);
}


void UiBuilderData::consumeToken(XmlTokenType a_type, const XmlStringSlice& a_text)
{
	if (a_text.m_length <= 0)
	{
		printf("bad token\n");
		return;
	}

	std::string str(a_text.m_data, a_text.m_length);

	switch (a_type)
	{
		case XT_Text:
			// ignore
			break;
		case XT_TagOpen:
			m_currentType = WT_Unknown;
			if (m_widgetTypeMap.find(str) != m_widgetTypeMap.end())
				m_currentType = m_widgetTypeMap[str];
			m_currentAttributes.clear();
			break;
		case XT_TagStart:	
			if (m_currentType != WT_Unknown)
			{
				TypedWidget parentData = m_parentNode.back();
				Widget* parent = parentData.m_widget;
				Widget* w = 0;
				TypedProperty prop = { VT_Unknown, 0 };

				if (m_currentAttributes.find("property") != m_currentAttributes.end())
					if (m_propertySet.find(m_currentAttributes["property"]) != m_propertySet.end())
						prop = m_propertySet[m_currentAttributes["property"]];

				switch (m_currentType)
				{
					case WT_VBox:
						w = new VBox(parent); break;
					case WT_HBox:
						w = new HBox(parent); break;
					case WT_Label:
						if (prop.m_type == VT_String && prop.m_property)
							w = new Label(parent, *(AbstractProperty<String>*)prop.m_property);
						else
							printf("Label with missing property\n");
						break;
					case WT_RadioButton:
						if (prop.m_type == VT_boolean_t && prop.m_property)
							w = new RadioButton(parent, *(AbstractProperty<bool>*)prop.m_property, m_currentAttributes["text"].c_str());
						else
							printf("RadioButton with missing property\n");
						break;
					case WT_CheckBox:
						if (prop.m_type == VT_boolean_t && prop.m_property)
							w = new CheckBox(parent, *(AbstractProperty<bool>*)prop.m_property, m_currentAttributes["text"].c_str());
						else
							printf("CheckBox with missing property\n");
						break;
					case WT_Button:
						w = new Button(parent, m_currentAttributes["text"].c_str()); break;
					case WT_Slider:
						if (prop.m_type == VT_int32_t && prop.m_property)
							w = new Slider(parent, *(AbstractProperty<int>*)prop.m_property);
						else
							printf("Slider with missing property\n");
						break;
					case WT_ComboBox:
						if (prop.m_type == VT_int32_t && prop.m_property)
							w = new ComboBox(parent, *(AbstractProperty<int>*)prop.m_property);
						else
							printf("ComboBox with missing property\n");
						break;
					case WT_GroupBox:
						if (prop.m_type == VT_String && prop.m_property)
							w = new GroupBox(parent, *(AbstractProperty<String>*)prop.m_property);
						else
							printf("GroupBox with missing property\n");
						break;
					case WT_LineEdit:
						if (prop.m_type == VT_String && prop.m_property)
							w = new LineEdit(parent, *(AbstractProperty<String>*)prop.m_property);
						else
							printf("LineEdit with missing property\n");
						break;
					case WT_VSpace:
						w = new VSpace(parent); break;
					case WT_HSpace:
						w = new HSpace(parent); break;
					case WT_ProgressBar:
						if (prop.m_type == VT_int32_t && prop.m_property)
							w = new ProgressBar(parent, *(AbstractProperty<int>*)prop.m_property);
						else
							printf("ProgressBar with missing property\n");
						break;
					case WT_Item:
						if (parentData.m_type == WT_ComboBox && parent)
							((ComboBox*)parent)->addItem(m_currentAttributes["text"].c_str());
						else
							printf("item not found as child of combobox node\n");
						break;
					default:
						if (prop.m_type == VT_String && prop.m_property)
							w = new Label(parent, *(AbstractProperty<String>*)prop.m_property); break;
				}

				if (w)
				{
					if (m_currentAttributes.find("focus") != m_currentAttributes.end())
					{
						bool val = (m_currentAttributes["focus"] == "false") ? false : true;
						switch (m_currentType)
						{
							case WT_RadioButton:	((RadioButton*)w)->hasFocus = val; break;
							case WT_CheckBox:		((CheckBox*)w)->hasFocus = val; break;
							case WT_Button:			((Button*)w)->hasFocus = val; break;
							case WT_Slider:			((Slider*)w)->hasFocus = val; break;
							case WT_ComboBox:		((ComboBox*)w)->hasFocus = val; break;
							case WT_LineEdit:		((LineEdit*)w)->hasFocus = val; break;
							default:
								printf("invalid attribute for current widget type\n");
						}
					}

					if (m_currentAttributes.find("disabled") != m_currentAttributes.end())
					{
						bool val = (m_currentAttributes["disabled"] == "false") ? false : true;
						switch (m_currentType)
						{
							case WT_Label:			((Label*)w)->disabled = val; break;
							case WT_RadioButton:	((RadioButton*)w)->disabled = val; break;
							case WT_CheckBox:		((CheckBox*)w)->disabled = val; break;
							case WT_Button:			((Button*)w)->disabled = val; break;
							case WT_Slider:			((Slider*)w)->disabled = val; break;
							case WT_ComboBox:		((ComboBox*)w)->disabled = val; break;
							case WT_GroupBox:		((GroupBox*)w)->disabled = val; break;
							case WT_LineEdit:		((LineEdit*)w)->disabled = val; break;
							case WT_ProgressBar:	((ProgressBar*)w)->disabled = val; break;
							default:
								printf("invalid attribute for current widget type\n");
						}
					}

					if (m_currentAttributes.find("default") != m_currentAttributes.end())
					{
						bool val = (m_currentAttributes["default"] == "false") ? false : true;
						switch (m_currentType)
						{
							case WT_RadioButton:	((RadioButton*)w)->isDefault = val; break;
							case WT_CheckBox:		((CheckBox*)w)->isDefault = val; break;
							case WT_Button:			((Button*)w)->isDefault = val; break;
							//case WT_Slider:			((Slider*)w)->isDefault = val; break;
							case WT_ComboBox:		((ComboBox*)w)->isDefault = val; break;
							//case WT_LineEdit:		((LineEdit*)w)->isDefault = val; break;
							default:
								printf("invalid attribute for current widget type\n");
						}
					}
				}
				// TODO: check there is a name, and if there is, check it is unique
				TypedWidget wid = { m_currentType, w };
				m_widgetMap[m_currentAttributes["name"]] = wid;
				m_parentNode.push_back(wid);
			}
			break;
		case XT_TagEnd:
			if (m_currentType != WT_Unknown && m_parentNode.size())
				m_parentNode.pop_back();
			break;
		case XT_AttributeName:
			m_currentAttributeName = str;
			break;
		case XT_AttributeValue:
			if ( str.c_str()[0] == '"' && str.c_str()[str.size()-1] == '"' )
				str = str.substr(1, str.size()-2);
			m_currentAttributes[m_currentAttributeName] = str;
			break;
		default:
			printf("Got token type=%i  data=---%s---\n", a_type, str.c_str());
			break;
	}
}


END_NAMESPACE
