/*\
title: $:/core/modules/widgets/genesis.js
type: application/javascript
module-type: widget

Genesis widget for dynamically creating widgets

\*/
(function(){

/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

var Widget = require("$:/core/modules/widgets/widget.js").widget;

var GenesisWidget = function(parseTreeNode,options) {
	this.initialise(parseTreeNode,options);
};

/*
Inherit from the base widget class
*/
GenesisWidget.prototype = new Widget();

/*
Render this widget into the DOM
*/
GenesisWidget.prototype.render = function(parent,nextSibling) {
	this.parentDomNode = parent;
	this.computeAttributes({filterFn: function(name) {
		// Only compute our own attributes which start with a single dollar
		return name.charAt(0) === "$" && name.charAt(1) !== "$";
	}});
	this.execute();
	this.renderChildren(parent,nextSibling);
};

/*
Compute the internal state of the widget
*/
GenesisWidget.prototype.execute = function() {
	var self = this;
	// Collect attributes
	this.genesisType = this.getAttribute("$type");
	this.genesisRemappable = this.getAttribute("$remappable","yes") === "yes";
	this.genesisNames = this.getAttribute("$names","");
	this.genesisValues = this.getAttribute("$values","");
	this.genesisIsBlock = this.getAttribute("$mode",this.parseTreeNode.isBlock && "block") === "block";
	// Do not create a child widget if the $type attribute is missing or blank
	if(!this.genesisType) {
		this.makeChildWidgets(this.parseTreeNode.children);
		return;
	}
	// Construct parse tree
	var isElementWidget = this.genesisType.charAt(0) !== "$",
		nodeType = isElementWidget ? "element" : this.genesisType.substr(1),
		nodeTag = isElementWidget ? this.genesisType : undefined;
	var parseTreeNodes = [{
		type: nodeType,
		tag: nodeTag,
		attributes: {},
		orderedAttributes: [],
		isBlock: this.genesisIsBlock,
		children: this.parseTreeNode.children || [],
		isNotRemappable: !this.genesisRemappable
	}];
	// Apply explicit attributes
	$tw.utils.each($tw.utils.getOrderedAttributesFromParseTreeNode(this.parseTreeNode),function(attribute) {
		var name = attribute.name;
		if(name.charAt(0) === "$") {
			if(name.charAt(1) === "$") {
				// Double $$ is changed to a single $
				name = name.substr(1);
			} else {
				// Single dollar is ignored
				return;
			}
		}
		$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],$tw.utils.extend({},attribute,{name: name}));
	});
	// Apply attributes in $names/$values
	this.attributeNames = [];
	this.attributeValues = [];
	if(this.genesisNames && this.genesisValues) {
		this.attributeNames = this.wiki.filterTiddlers(self.genesisNames,this);
		this.attributeValues = this.wiki.filterTiddlers(self.genesisValues,this);
		$tw.utils.each(this.attributeNames,function(varname,index) {
			$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],varname,self.attributeValues[index] || "");
		});
	}
	// Construct the child widgets
	this.makeChildWidgets(parseTreeNodes);
};

/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
GenesisWidget.prototype.refresh = function(changedTiddlers) {
	var changedAttributes = this.computeAttributes(),
		filterNames = this.getAttribute("$names",""),
		filterValues = this.getAttribute("$values",""),
		attributeNames = this.wiki.filterTiddlers(filterNames,this),
		attributeValues = this.wiki.filterTiddlers(filterValues,this);
	if($tw.utils.count(changedAttributes) > 0 || !$tw.utils.isArrayEqual(this.attributeNames,attributeNames) || !$tw.utils.isArrayEqual(this.attributeValues,attributeValues)) {
		this.refreshSelf();
		return true;
	} else {
		return this.refreshChildren(changedTiddlers);
	}
};

exports.genesis = GenesisWidget;

})();
