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

Implements the reveal widget.

\*/
(function(){

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

var RevealWidget = function(renderer) {
	// Save state
	this.renderer = renderer;
	// Generate child nodes
	this.generate();
};

RevealWidget.prototype.generate = function() {
	// Get the parameters from the attributes
	this.state = this.renderer.getAttribute("state");
	this.type = this.renderer.getAttribute("type");
	this.text = this.renderer.getAttribute("text");
	this.position = this.renderer.getAttribute("position");
	this["default"] = this.renderer.getAttribute("default","");
	this.qualifyTiddlerTitles = this.renderer.getAttribute("qualifyTiddlerTitles");
	this["class"] = this.renderer.getAttribute("class");
	this.animate = this.renderer.getAttribute("animate","no");
	// Compute the title of the state tiddler and read it
	this.stateTitle = this.state;
	if(this.qualifyTiddlerTitles) {
		this.stateTitle =  this.stateTitle + "-" + this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
	}
	this.readState();
	// Set up the element attributes
	var classes = ["tw-reveal"],
		styles = [];
	if(this["class"]) {
		$tw.utils.pushTop(classes,this["class"]);
	}
	if(this.isOpen) {
		$tw.utils.pushTop(classes,"tw-reveal-open");
	}
	switch(this.type) {
		case "popup":
			styles.push("position:absolute;");
			classes.push("tw-popup");
			break;
	}
	styles.push("display:" + (this.isOpen ? (this.renderer.parseTreeNode.isBlock ? "block" : "inline") : "none") + ";");
	// Set the element
	this.tag =  "div";
	this.attributes = {
		"class": classes.join(" "),
		style: styles.join("")
	};
	this.children = this.renderer.renderTree.createRenderers(this.renderer,this.isOpen ? this.renderer.parseTreeNode.children : []);
	this.events = [{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"}];
};

/*
Read the state tiddler
*/
RevealWidget.prototype.readState = function() {
	// Read the information from the state tiddler
	if(this.stateTitle) {
		var state = this.renderer.renderTree.wiki.getTextReference(this.stateTitle,this["default"],this.renderer.tiddlerTitle);
		switch(this.type) {
			case "popup":
				this.readPopupState(state);
				break;
			case "match":
				this.readMatchState(state);
				break;
			case "nomatch":
				this.readMatchState(state);
				this.isOpen = !this.isOpen;
				break;
		}
	}
};

RevealWidget.prototype.readMatchState = function(state) {
	this.isOpen = state === this.text;
};

RevealWidget.prototype.readPopupState = function(state) {
	var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
		match = popupLocationRegExp.exec(state);
	// Check if the state matches the location regexp
	if(match) {
		// If so, we're open
		this.isOpen = true;
		// Get the location
		this.popup = {
			left: parseFloat(match[1]),
			top: parseFloat(match[2]),
			width: parseFloat(match[3]),
			height: parseFloat(match[4])
		};
	} else {
		// If not, we're closed
		this.isOpen = false;
	}
};

RevealWidget.prototype.handleClickEvent = function(event) {
	if(event.type === "click" && this.type === "popup") {
		// Cancel the popup if we get a click on it
		if(this.stateTitle) {
			this.renderer.renderTree.wiki.deleteTextReference(this.stateTitle);
		}
		event.preventDefault();
		return false;
	}
	return true;
};

RevealWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
	var self = this;
	// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
	if(changedAttributes.state || changedAttributes.type || changedAttributes.text || changedAttributes.position || changedAttributes["default"] || changedAttributes.qualifyTiddlerTitles || changedAttributes["class"]) {
		// Regenerate and rerender the widget and replace the existing DOM node
		this.generate();
		var oldDomNode = this.renderer.domNode,
			newDomNode = this.renderer.renderInDom();
		oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
	} else {
		var needChildrenRefresh = true; // Avoid refreshing the children nodes if we don't need to
		// Get the open state
		var previousState = this.isOpen
		this.readState();
		// Construct the child nodes if  required
		if(this.isOpen && this.children.length === 0) {
			this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
			var parentNode = this.renderer.domNode;
			$tw.utils.each(this.children,function(child) {
				parentNode.appendChild(child.renderInDom());
			});
			needChildrenRefresh = false;
		}
		// Refresh any child nodes
		if(needChildrenRefresh) {
			$tw.utils.each(this.children,function(node) {
				if(node.refreshInDom) {
					node.refreshInDom(changedTiddlers);
				}
			});
		}
		// Animate the opening or closing
		if(this.isOpen !== previousState) {
			if(this.animate !== "no") {
				if(this.isOpen) {
					this.renderer.domNode.style.display = this.renderer.parseTreeNode.isBlock ? "block" : "inline";
					$tw.anim.perform("open",this.renderer.domNode);
				} else {
					$tw.anim.perform("close",this.renderer.domNode,{callback: function() {
						self.renderer.domNode.style.display = "none";
					}});
				}
			} else {
				this.renderer.domNode.style.display = this.isOpen ? (this.renderer.parseTreeNode.isBlock ? "block" : "inline") : "none";
			}
		}
		// Add or remove the tw-reveal-open class
		$tw.utils.toggleClass(this.renderer.domNode,"tw-reveal-open",this.isOpen);
	}
	// Position the content if required
	if(this.isOpen) {
		this.postRenderInDom();
	}
};

RevealWidget.prototype.postRenderInDom = function() {
	switch(this.type) {
		case "popup":
			if(this.isOpen) {
				this.renderer.domNode.style.position = "absolute";
				this.renderer.domNode.style.zIndex = "1000";
				switch(this.position) {
					case "left":
						this.renderer.domNode.style.left = (this.popup.left - this.renderer.domNode.offsetWidth) + "px";
						this.renderer.domNode.style.top = this.popup.top + "px";
						break;
					case "above":
						this.renderer.domNode.style.left = this.popup.left + "px";
						this.renderer.domNode.style.top = (this.popup.top - this.renderer.domNode.offsetHeight) + "px";
						break;
					case "aboveright":
						this.renderer.domNode.style.left = (this.popup.left + this.popup.width) + "px";
						this.renderer.domNode.style.top = (this.popup.top + this.popup.height - this.renderer.domNode.offsetHeight) + "px";
						break;
					case "right":
						this.renderer.domNode.style.left = (this.popup.left + this.popup.width) + "px";
						this.renderer.domNode.style.top = this.popup.top + "px";
						break;
					case "belowleft":
						this.renderer.domNode.style.left = (this.popup.left + this.popup.width - this.renderer.domNode.offsetWidth) + "px";
						this.renderer.domNode.style.top = (this.popup.top + this.popup.height) + "px";
						break;
					default: // Below
						this.renderer.domNode.style.left = this.popup.left + "px";
						this.renderer.domNode.style.top = (this.popup.top + this.popup.height) + "px";
						break;
				}
			}
			break;
	}
};

exports.reveal = RevealWidget;

})();
