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

An override of the core text widget that automatically linkifies the text

\*/
(function(){

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

var TITLE_TARGET_FILTER = "$:/config/Freelinks/TargetFilter";

var Widget = require("$:/core/modules/widgets/widget.js").widget,
	LinkWidget = require("$:/core/modules/widgets/link.js").link,
	ButtonWidget = require("$:/core/modules/widgets/button.js").button,
	ElementWidget = require("$:/core/modules/widgets/element.js").element;

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

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

/*
Render this widget into the DOM
*/
TextNodeWidget.prototype.render = function(parent,nextSibling) {
	this.parentDomNode = parent;
	this.computeAttributes();
	this.execute();
	this.renderChildren(parent,nextSibling);
};

/*
Compute the internal state of the widget
*/
TextNodeWidget.prototype.execute = function() {
	var self = this,
		ignoreCase = self.getVariable("tv-freelinks-ignore-case",{defaultValue:"no"}).trim() === "yes";
	// Get our parameters
	var childParseTree = [{
			type: "plain-text",
			text: this.getAttribute("text",this.parseTreeNode.text || "")
		}];
	// Only process links if not disabled and we're not within a button or link widget
	if(this.getVariable("tv-wikilinks",{defaultValue:"yes"}).trim() !== "no" && this.getVariable("tv-freelinks",{defaultValue:"no"}).trim() === "yes" && !this.isWithinButtonOrLink()) {
		// Get the information about the current tiddler titles, and construct a regexp
		this.tiddlerTitleInfo = this.wiki.getGlobalCache("tiddler-title-info-" + (ignoreCase ? "insensitive" : "sensitive"),function() {
			var targetFilterText = self.wiki.getTiddlerText(TITLE_TARGET_FILTER),
				titles = !!targetFilterText ? self.wiki.filterTiddlers(targetFilterText,$tw.rootWidget) : self.wiki.allTitles(),
				sortedTitles = titles.sort(function(a,b) {
					var lenA = a.length,
						lenB = b.length;
					// First sort by length, so longer titles are first
					if(lenA !== lenB) {
						if(lenA < lenB) {
							return +1;
						} else {
							return -1;
						}
					} else {
					// Then sort alphabetically within titles of the same length
						if(a < b) {
							return -1;
						} else if(a > b) {
							return +1;
						} else {
							return 0;
						}
					}
				}),
				titles = [],
				reparts = [];
			$tw.utils.each(sortedTitles,function(title) {
				if(title.substring(0,3) !== "$:/") {
					titles.push(title);
					reparts.push("(" + $tw.utils.escapeRegExp(title) + ")");
				}
			});
			var regexpStr = "\\b(?:" + reparts.join("|") + ")\\b";
			return {
				titles: titles,
				regexp: new RegExp(regexpStr,ignoreCase ? "i" : "")
			};
		});
		// Repeatedly linkify
		if(this.tiddlerTitleInfo.titles.length > 0) {
			var index,text,match,matchEnd;
			do {
				index = childParseTree.length - 1;
				text = childParseTree[index].text;
				match = this.tiddlerTitleInfo.regexp.exec(text);
				if(match) {
					// Make a text node for any text before the match
					if(match.index > 0) {
						childParseTree[index].text = text.substring(0,match.index);
						index += 1;
					}
					// Make a link node for the match
					childParseTree[index] = {
						type: "link",
						attributes: {
							to: {type: "string", value: ignoreCase ? this.tiddlerTitleInfo.titles[match.indexOf(match[0],1) - 1] : match[0]},
							"class": {type: "string", value: "tc-freelink"}
						},
						children: [{
							type: "plain-text", text: match[0]
						}]
					};
					index += 1;
					// Make a text node for any text after the match
					matchEnd = match.index + match[0].length;
					if(matchEnd < text.length) {
						childParseTree[index] = {
							type: "plain-text",
							text: text.substring(matchEnd)
						};					
					}
				}
			} while(match && childParseTree[childParseTree.length - 1].type === "plain-text");			
		}
	}
	// Make the child widgets
	this.makeChildWidgets(childParseTree);
};

TextNodeWidget.prototype.isWithinButtonOrLink = function() {
	var withinButtonOrLink = false,
		widget = this.parentWidget;
	while(!withinButtonOrLink && widget) {
		withinButtonOrLink = widget instanceof ButtonWidget || widget instanceof LinkWidget || ((widget instanceof ElementWidget) && widget.parseTreeNode.tag === "a");
		widget = widget.parentWidget;
	}
	return withinButtonOrLink;
};

/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
TextNodeWidget.prototype.refresh = function(changedTiddlers) {
	var self = this,
		changedAttributes = this.computeAttributes(),
		titlesHaveChanged = false;
	$tw.utils.each(changedTiddlers,function(change,title) {
		if(change.isDeleted) {
			titlesHaveChanged = true;
		} else {
			titlesHaveChanged = titlesHaveChanged || !self.tiddlerTitleInfo || self.tiddlerTitleInfo.titles.indexOf(title) === -1;
		}
	});
	if(changedAttributes.text || titlesHaveChanged) {
		this.refreshSelf();
		return true;
	} else {
		return false;	
	}
};

exports.text = TextNodeWidget;

})();
