dojo.provide("dojo.data.OpmlStore");
dojo.require("dojo.data.core.Read");
dojo.require("dojo.data.core.Result");
dojo.require("dojo.lang.assert");
dojo.require("dojo.json");

dojo.require("dojo.experimental");
dojo.experimental("dojo.data.OpmlStore");
// FIXME: The OpmlStore works in Firefox but does not yet work in IE.

dojo.declare("dojo.data.OpmlStore", dojo.data.core.Read, {
	/* summary:
	 *   The OpmlStore implements the dojo.data.core.Read API.  
	 */
	 
	/* examples:
	 *   var opmlStore = new dojo.data.OpmlStore({url:"geography.opml"});
	 *   var opmlStore = new dojo.data.OpmlStore({url:"http://example.com/geography.opml"});
	 */
	initializer: function(/* object */ keywordParameters) {
		// keywordParameters: {url: String}
		this._arrayOfTopLevelItems = [];
		this._metadataNodes = null;
		this._loadFinished = false;
		this._opmlFileUrl = keywordParameters["url"];
	},
	
	_assertIsItem: function(/* item */ item) {
		if (!this.isItem(item)) { 
			throw new Error("dojo.data.OpmlStore: a function was passed an item argument that was not an item");
		}
	},
	
	_removeChildNodesThatAreNotElementNodes: function(/* node */ node, /* boolean */ recursive) {
		var childNodes = node.childNodes;
		if (childNodes.length == 0) {
			return;
		}
		var nodesToRemove = [];
		var i, childNode;
		for (i = 0; i < childNodes.length; ++i) {
			childNode = childNodes[i];
			if (childNode.nodeType != Node.ELEMENT_NODE) { 
				nodesToRemove.push(childNode); 
			}
		};
		// dojo.debug('trim: ' + childNodes.length + ' total, ' + nodesToRemove.length + ' junk');
		for (i = 0; i < nodesToRemove.length; ++i) {
			childNode = nodesToRemove[i];
			node.removeChild(childNode);
		}
		// dojo.debug('trim: ' + childNodes.length + ' remaining');
		if (recursive) {
			for (i = 0; i < childNodes.length; ++i) {
				childNode = childNodes[i];
				this._removeChildNodesThatAreNotElementNodes(childNode, recursive);
			}
		}
	},
	
	_processRawXmlTree: function(/* xmlDoc */ rawXmlTree) {
		var headNodes = rawXmlTree.getElementsByTagName('head');
		var headNode = headNodes[0];
		this._removeChildNodesThatAreNotElementNodes(headNode);
		this._metadataNodes = headNode.childNodes;
		var bodyNodes = rawXmlTree.getElementsByTagName('body');
		var bodyNode = bodyNodes[0];
		this._removeChildNodesThatAreNotElementNodes(bodyNode, true);
		
		var bodyChildNodes = bodyNodes[0].childNodes;
		for (var i = 0; i < bodyChildNodes.length; ++i) {
			var node = bodyChildNodes[i];
			if (node.tagName == 'outline') {
				this._arrayOfTopLevelItems.push(node);
			}
		}
	},
	
	get: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* value? */ defaultValue) {
		// summary: See dojo.data.core.Read.get()
		this._assertIsItem(item);
		if (attribute == 'children') {
			return (item.firstChild || defaultValue);
		} else {
			var value = item.getAttribute(attribute);
			value = (value != undefined) ? value : defaultValue;
			return value;
		}
	},
		
	getValues: function(/* item */ item, /* attribute || attribute-name-string */ attribute) {
		// summary: See dojo.data.core.Read.getValues()
		this._assertIsItem(item);
		if (attribute == 'children') {
			var array = [];
			for (var i = 0; i < item.childNodes.length; ++i) {
				array.push(item.childNodes[i]);
			}
			return array; // Array
			// return item.childNodes; // FIXME: this isn't really an Array
		} else {
			return [item.getAttribute(attribute)]; // Array
		}
	},
		
	getAttributes: function(/* item */ item) {
		// summary: See dojo.data.core.Read.getAttributes()
		this._assertIsItem(item);
		var attributes = [];
		var xmlNode = item;
		var xmlAttributes = xmlNode.attributes;
		for (var i = 0; i < xmlAttributes.length; ++i) {
			var xmlAttribute = xmlAttributes.item(i);
			attributes.push(xmlAttribute.nodeName);
		}
		if (xmlNode.childNodes.length > 0) {
			attributes.push('children');
		}
		return attributes; // array
	},
	
	hasAttribute: function(/* item */ item, /* attribute || attribute-name-string */ attribute) {
		// summary: See dojo.data.core.Read.hasAttribute()
		return (this.getValues(item, attribute).length > 0);
	},
	
	containsValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* anything */ value) {
		// summary: See dojo.data.core.Read.containsValue()
		var values = this.getValues(item, attribute);
		for (var i = 0; i < values.length; ++i) {
			var possibleValue = values[i];
			if (value == possibleValue) {
				return true;
			}
		}
		return false; // boolean
	},
		
	isItem: function(/* anything */ something) {
		return (something && 
				something.nodeType == Node.ELEMENT_NODE && 
				something.tagName == 'outline'); // boolean
	},
	
	isItemAvailable: function(/* anything */ something) {
		return this.isItem(something);
	},
	
	find: function(/* object? || dojo.data.core.Result */ keywordArgs) {
		// summary: See dojo.data.core.Read.find()
		var result = null;
		if (keywordArgs instanceof dojo.data.core.Result) {
			result = keywordArgs;
			result.store = this;
		} else {
			result = new dojo.data.core.Result(keywordArgs, this);
		}
		var self = this;
		var bindHandler = function(type, data, evt) {
			var scope = result.scope || dj_global;
			if (type == "load") {
				self._processRawXmlTree(data);
				if (result.saveResult) {
					result.items = self._arrayOfTopLevelItems;
				}
				if (result.onbegin) {
					result.onbegin.call(scope, result);
				}
				for (var i=0; i < self._arrayOfTopLevelItems.length; i++) {
					var item = self._arrayOfTopLevelItems[i];
					if (result.onnext && !result._aborted) {
						result.onnext.call(scope, item, result);
					}
				}
				if (result.oncompleted && !result._aborted) {
					result.oncompleted.call(scope, result);
				}
			} else if(type == "error" || type == 'timeout') {
				// todo: how to handle timeout?
				var errorObject = data;
				// dojo.debug("error in dojo.data.OpmlStore.find(): " + dojo.json.serialize(errorObject));
				if (result.onerror) {
					result.onerror.call(scope, data);
				}
			}
		};
		
		if (!this._loadFinished) {
			if (this._opmlFileUrl) {
				var bindRequest = dojo.io.bind({
					url: this._opmlFileUrl, // "playlist.opml",
					handle: bindHandler,
					mimetype: "text/xml",
					sync: (result.sync || false) });
				result._abortFunc = bindRequest.abort;
			}
		}
		return result; // dojo.data.csv.Result
	},
	
	getIdentity: function(/* item */ item) {
		// summary: See dojo.data.core.Read.getIdentity()
		dojo.unimplemented('dojo.data.OpmlStore.getIdentity()');
		return null;
	},
	
	findByIdentity: function(/* string */ identity) {
		// summary: See dojo.data.core.Read.findByIdentity()
		dojo.unimplemented('dojo.data.OpmlStore.findByIdentity()');
		return null;
	}
});


