/**
 * Miscellaneous SST utilities
 */
com_pagasg_xxapp_serialize = new es_lang.Package(true,
	/*Title*/   'Testing Package',
	/*Docs */   '-- none --',
	/*Package*/ function() {
		// Imports
		var LANG = es_lang;
		var StringBuilder = LANG.StringBuilder;

		// Constructor imports
		var Class = es_lang.Class;
		var Enum = es_lang.Enum;
		var StringBuilder = LANG.StringBuilder;

		// Function imports
		var extend = Class.extend;
		var implement = Class.implement;
		var enumerate = Enum.enumerate;
		
		/****** QName ******/
		var QName = function(localName, ns, prefix) {
			this.namespaceUri = ns;
			this.localName = localName;
			this.prefix = prefix;
			this.toString = function() {
				if (ns && ns != '') {
					return '{' + ns + '}' + localName;
				} else {
					return localName;
				}
			}
		}
		QName.parse = function(str) {
			var ns;
			var localName;
			var start, end;

			if (str) {
				start = str.indexOf('{');
				end = str.indexOf('}');
				if (start == 0 && end > 2) {
					ns = str.substring(start + 1, end);
					localName = str.substring(end + 1);
					return new QName(localName, ns);
				} else {
					return new QName(str);
				}
			} else {
				return null;
			}
		}

		/****** XmlAttribute ******/
		var XmlAttribute = function(ns, localName, value) {
			this.localName = localName;
			this.ns = ns;
			this.value = value;

			this.toString = function() {
				return value;
			}
		}

		/****** XmlElement ******/
		var XmlElement = function(ns, localName, attributes, children) {
			this.localName = localName;
			this.ns = ns;
			this.attributes = attributes;
			this.children = children;

			this.hasChildren = true;
			if (children.length == 1) {
				if (children[0].constructor == String) {
					this.text = children[0];
					this.hasChildren = false;
				}
			}

			for (var i = 0; i < attributes.length; i++) {
				var attr = attributes[i];
				if (attr.ns == null || attr.ns == '') {
					this['@' + attr.localName] = attr.value;
				}
			}

			function isEmpty(str) {
				return str == undefined || str == null || str == '';
			}

			// Local
			function matchName(xml1, localName, ns) {
				if (xml1) {
					if (xml1.localName == localName) {
						if (!xml1.ns && !ns) {
							return true;
						} else {
							return (xml1.ns == ns)
						}
					}
				}

				return false;
			}
			function matchNames(xml1, xml2) {
				if (xml1 && xml2) {
					if (xml1.localName == xml2.localName) {
						if (!xml1.ns && !xml2.ns) {
							return true;
						} else {
							return (xml1.ns == xml2.ns)
						}
					}
				}

				return false;
			}

			// Public
			this.rename = function(localName, ns) {
				return new XmlElement(ns, localName, attributes, children);
			};
			this.getAttributes = function() {
				arr = [];
				for (var i = 0; i < attributes.length; i++) arr.push(attributes[i]);
				return arr;
			};
			this.locateAttributes = function(filter) {
				var attr;
				var arr = [];
				
				for (var i = 0; i < attributes.length; i++) {
					attr = attributes[i];
					if (seeker(attr)) {
						return attr;
					}
				}
			};
			this.locateChild = function(seeker) {
				var value;
				var arr = [];
				
				for (var i = 0; i < children.length; i++) {
					value = seeker(children[i]);
					if (value != undefined && value != null && value != false) {
						return value;
					}
				}
			};
			this.getChildren = function(localName, ns) {
				var arr = [];
				var child;

				for (var i = 0; i < children.length; i++) {
					child = children[i];
					if (child) {
						if (arguments.length == 0) {
								arr.push(child);
						} else if (child.localName == localName) {
							if (isEmpty(child.ns) && isEmpty(ns)) {
								arr.push(child);
							} else if (child.ns == ns) {
								arr.push(child);
							}
						}
					}
				}

				return arr;
			};
			this.getChildByIndex = function(index) {
				var child;
				
				// TODO: put a mod in here.
				if (children) {
					if (index < 0) index = index + children.length;
					if (index > 0 || index < children.length) {
						return children[index];
					}
				}
				return null;
			};
			this.getChildCount = function() {
				if (children) {
					return children.length;
				} else {
					return 0;
				}
			}
			this.getChild = function(localName, ns) {
				var child;

				for (var i = 0; i < children.length; i++) {
					child = children[i];
					if (child.localName == localName) {
						if (isEmpty(child.ns) && isEmpty(ns)) {
							return child;
						} else if (child.ns == ns) {
							return child;
						}
					}
				}

				return null;
			};
			this.getAttribute = function(localName, ns) {
				var attr;

				for (var i = 0; i < attributes.length; i++) {
					attr = attributes[i];
					if (attr.localName == localName) {
						if (isEmpty(attr.ns) && isEmpty(ns)) {
							return attr;
						} else if (attr.ns == ns) {
							return attr;
						}
					}
				}

				return null;
			};

			// Modify
			this.setAttribute = function(value, localName, ns) {
				var attr;
				var index;
				var found;
				var newAttr = new XmlAttribute(ns, localName, value);

				found = false;
				for (index = 0; index < attributes.length; index++) {
					attr = attributes[index];
					if (matchNames(attr, newAttr)) {
						attributes[index] = newAttr;
						found = true;
						break;
					} else {
						attr = null;
					}
				}

				if (!found) {
					attributes.push();
				}

				return attr;
			};
			this.removeChildren = function(localName, ns, max) {
				var child;
				var count = 0;
				var removals = [];

				for (var i = children.length - 1; i >= 0; i--) {
					child = children[i];
					if (matchName(child, localName, ns)) {
						count++;
						if (max && count < max) {
							removals.unshift(children.splice(i, 1));
						}
					}
				}

				return removals;
			};
			this.removeChild = function(localName, ns) {
				var pos;
				var child;

				pos = -1;
				for (var i = children.length - 1; i >= 0; i--) {
					child = children[i];
					if (matchName(child, localName, ns)) {
						children.splice(i, 1);
						break;
					}
					child = null;
				}

				return child;
			};
			this.replaceChild = function(child, newChild) {
				var oldChild;
				var pos;
				
				for (pos = 0; pos < children.length; pos++) {
					oldChild = children[pos];
					if (child === oldChild) {
						break;
					}
				}

				if (newChild) {
					children[pos] = newChild;
				} else {
					children.splice(pos, 1);
				}

				return child;
			};
			this.appendChild = function(newChild) {
				if (newChild) {
					children.push(newChild);
				} else {
					throw new Error('IllegalArgumentException :: cannot append a null or undefined child');
				}
			};
			this.addChild = function(newChild, refChild) {
				var child;
				var pos = 0;

				if (newChild) {
					if (!refChild) {
						for (pos = 0; pos < children.length; pos++) {
							if (refChild == children[pos]) break;
						}
					}

					if (pos >= children.length) {
						children.push(newChild);
					} else {
						children.splice(pos, 0, newChild);
					}
				} else {
					throw new Error('IllegalArgumentException :: cannot add a null or undefined child');
				}
			};
			this.setChild = function(newChild) {
				var changed;
				var child;

				if (newChild) {
					changed = false;
					for (var i = 0; i < children.length; i++) {
						child = children[i];
						if (matchNames(child, newChild)) {
							children[i] = newChild;
							child = newChild;
							changed = true;
							break;
						} else {
							child = null;
						}
					}

					if (!changed) {
						children.push(newChild);
					}
				} else {
					throw new Error('IllegalArgumentException :: cannot add a null or undefined child');
				}

				return child;
			};

			// Object
			this.toString = function() {
				var bldr;
				var child;

				if (this.text) {
					return this.text;
				} else {
					bldr = new StringBuilder();
					for (var i = 0; i < children.length; i++) {
						child = children[i];
						if (i > 0) {
							bldr.append(',');
						}
						bldr.append(child.toString());
					}
					return bldr.toString();
				}
			};
		}
		XmlElement.rename = function(/*XmlElement*/ elm, ns, localName) {
			return new XSER.XmlElement(ns, localName, elm.attributes, elm.children);
		}

		/****** Serializer ******/
		var Serializer = function(deserializeListener, serializeListener) {
			var _dListener = deserializeListener;
			var _sListener = serializeListener;

			this.deserialize = function(ns, localName, attributes, value) {
				var attr;
				var node;
				var children;

				if (attributes) {
					children = [];
					for (var i = 3; i < arguments.length; i++) {
						if (arguments[i] != null) {
							children.push(arguments[i]);
						}
					}
					node = new XmlElement(ns, localName, attributes, children);
				} else {
					node = new XmlAttribute(ns, localName, value);
				}

				return node;
			}
		}

		// === PACKAGE PUBLIC ===
		this.QName = QName;
		this.Serializer = Serializer;
		this.XmlElement = XmlElement;
		this.XmlAttribute = XmlAttribute;
		// === PACKAGE PUBLIC ===
	}
)












