/++
	A small extension module to [arsd.minigui] that adds
	functions for creating widgets and windows from short
	XML descriptions.

	If you choose to use this, it will require [arsd.dom]
	to be compiled into your project too.

	---
	import arsd.minigui_xml;
	Window window = createWindowFromXml(`
		<MainWindow>
			<Button label="Hi!" />
		</MainWindow>
	`);
	---


	To add custom widgets to the minigui_xml factory, you need
	to register them with FIXME.

	You can attach some events right in the XML using attributes.
	The attribute names are `onEVENTNAME` or `ondirectEVENTNAME`
	and the values are one of the following three value types:

	$(LIST
		* If it starts with `&`, it is a delegate you need
		  to register using the FIXME function.

		* If it starts with `(`, it is a string passed to
		  the [arsd.dom.querySelector] function to get an
		  element reference

		* Otherwise, it tries to call a script function (if
		  scripting is available).
	)

	Keep in mind
	For example, to make a page widget that changes based on a
	drop down selection, you may:

	```xml
		<DropDownSelection onchange="$(+PageWidget).setCurrentTab">
			<option>Foo</option>
			<option>Bar</option>
		</DropDownSelection>
		<PageWidget name="mypage">
			<!-- contents elided -->
		</PageWidget>
	```

	That will create a select widget that when it changes, it will
	look for the next PageWidget sibling (that's the meaning of `+PageWidget`,
	see css selector syntax for more) and call its `setCurrentTab`
	method.

	Since the function knows `setCurrentTab` takes an integer, it will
	automatically pull the `intValue` member out of the event and pass
	it to the method.

	The given XML is the same as the following D:

	---
		auto select = new DropDownSelection(parent);
		select.addOption("Foo");
		select.addOption("Bar");
		auto page = new PageWidget(parent);
		page.name = "mypage";

		select.addEventListener("change", (Event event) {
			page.setCurrentTab(event.intValue);
		});
	---
+/
module arsd.minigui_xml;

public import arsd.minigui;

import arsd.dom;

private template ident(T...) {
	static if(is(T[0]))
		alias ident = T[0];
	else
		alias ident = void;
}

private
Widget delegate(string[string] args, Widget parent)[string] widgetFactoryFunctions;

private
void loadMiniguiPublicClasses() {
	if(widgetFactoryFunctions !is null)
		return;

	import std.traits;
	import std.conv;

	foreach(memberName; __traits(allMembers, mixin("arsd.minigui"))) static if(__traits(compiles, __traits(getMember, mixin("arsd.minigui"), memberName))) {
		alias Member = ident!(__traits(getMember, mixin("arsd.minigui"), memberName));
		static if(is(Member == class) && !isAbstractClass!Member && is(Member : Widget) && __traits(getProtection, Member) != "private") {
			widgetFactoryFunctions[memberName] = (string[string] args, Widget parent) {
				static if(is(Member : Dialog)) {
					return new Member();
				} else static if(is(Member : Window)) {
					return new Member("test");
				} else {
					auto paramNames = ParameterIdentifierTuple!(__traits(getMember, Member, "__ctor"));
					Parameters!(__traits(getMember, Member, "__ctor")) params;

					foreach(idx, param; params[0 .. $-1]) {
						if(auto arg = paramNames[idx] in args) {
							static if(is(typeof(param) == MemoryImage)) {

							} else static if(is(typeof(param) == Color)) {
								params[idx] = Color.fromString(*arg);
							} else
								params[idx] = to!(typeof(param))(*arg);
						}
					}

					params[$-1] = parent;

					auto widget = new Member(params);

					if(auto st = "statusTip" in args)
						widget.statusTip = *st;
					if(auto st = "name" in args)
						widget.name = *st;
					return widget;
				}
			};
		}
	}
}

///
Widget makeWidgetFromString(string xml, Widget parent) {
	auto document = new Document(xml, true, true);
	auto r = document.root;
	return miniguiWidgetFromXml(r, parent);
}

///
Window createWindowFromXml(string xml) {
	return createWindowFromXml(new Document(xml, true, true));
}
///
Window createWindowFromXml(Document document) {
	auto r = document.root;
	return cast(Window) miniguiWidgetFromXml(r, null);
}
///
Widget miniguiWidgetFromXml(Element element, Widget parent) {
	if(widgetFactoryFunctions is null)
		loadMiniguiPublicClasses();
	if(auto factory = element.tagName in widgetFactoryFunctions) {
		auto p = (*factory)(element.attributes, parent);
		foreach(child; element.children)
			if(child.tagName != "#text")
				miniguiWidgetFromXml(child, p);
		return p;
	} else {
		import std.stdio;
		writeln("Unknown class: ", element.tagName);
		return null;
	}
}