/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END LICENSE BLOCK ***** */

ace.define('ace/ext/textarea', [ 'require', 'exports', 'module',
		'ace/lib/event', 'ace/lib/useragent', 'ace/lib/net', 'ace/ace',
		'ace/theme/textmate', 'ace/mode/text' ], function(require, exports,
		module) {

	var event = require("../lib/event");
	var UA = require("../lib/useragent");
	var net = require("../lib/net");
	var ace = require("../ace");

	require("../theme/textmate");

	module.exports = exports = ace;
	var getCSSProperty = function(element, container, property) {
		var ret = element.style[property];

		if (!ret) {
			if (window.getComputedStyle) {
				ret = window.getComputedStyle(element, '').getPropertyValue(
						property);
			} else {
				ret = element.currentStyle[property];
			}
		}

		if (!ret || ret == 'auto' || ret == 'intrinsic') {
			ret = container.style[property];
		}
		return ret;
	};

	function applyStyles(elm, styles) {
		for ( var style in styles) {
			elm.style[style] = styles[style];
		}
	}

	function setupContainer(element, getValue) {
		if (element.type != 'textarea') {
			throw "Textarea required!";
		}

		var parentNode = element.parentNode;
		var container = document.createElement('div');
		var resizeEvent = function() {
			var style = 'position:relative;';
			[ 'margin-top', 'margin-left', 'margin-right', 'margin-bottom' ]
					.forEach(function(item) {
						style += item + ':'
								+ getCSSProperty(element, container, item)
								+ ';';
					});
			var width = getCSSProperty(element, container, 'width')
					|| (element.clientWidth + "px");
			var height = getCSSProperty(element, container, 'height')
					|| (element.clientHeight + "px");
			style += 'height:' + height + ';width:' + width + ';';
			style += 'display:inline-block;';
			container.setAttribute('style', style);
		};
		event.addListener(window, 'resize', resizeEvent);
		resizeEvent();
		if (element.nextSibling) {
			parentNode.insertBefore(container, element.nextSibling);
		} else {
			parentNode.appendChild(container);
		}
		while (parentNode !== document) {
			if (parentNode.tagName.toUpperCase() === 'FORM') {
				var oldSumit = parentNode.onsubmit;
				parentNode.onsubmit = function(evt) {
					element.innerHTML = getValue();
					element.value = getValue();
					if (oldSumit) {
						oldSumit.call(this, evt);
					}
				};
				break;
			}
			parentNode = parentNode.parentNode;
		}
		return container;
	}

	exports.transformTextarea = function(element, settings_panel, loader) {
		var session;
		var container = setupContainer(element, function() {
			return session.getValue();
		});
		element.style.display = 'none';
		container.style.background = 'white';
		var editorDiv = document.createElement("div");
		applyStyles(editorDiv, {
			top : "0px",
			left : "0px",
			right : "0px",
			bottom : "0px",
			border : "1px solid gray"
		});
		container.appendChild(editorDiv);

		if (settings_panel) {
			var settingOpener = document.createElement("div");
			applyStyles(settingOpener, {
				position : "absolute",
				right : "0px",
				bottom : "0px",
				background : "red",
				cursor : "nw-resize",
				borderStyle : "solid",
				borderWidth : "9px 8px 10px 9px",
				width : "2px",
				borderColor : "lightblue gray gray lightblue",
				zIndex : 101
			});

			var settingDiv = document.createElement("div");
			var settingDivStyles = {
				top : "0px",
				left : "0px",
				right : "0px",
				bottom : "0px",
				position : "absolute",
				padding : "5px",
				zIndex : 100,
				color : "white",
				display : "none",
				overflow : "auto",
				fontSize : "14px"
			};
			if (!UA.isOldIE) {
				settingDivStyles.backgroundColor = "rgba(0, 0, 0, 0.6)";
			} else {
				settingDivStyles.backgroundColor = "#333";
			}

			applyStyles(settingDiv, settingDivStyles);
			container.appendChild(settingDiv);
			var options = {};
		}

		var editor = ace.edit(editorDiv);
		session = editor.getSession();

		session.setValue(element.value || element.innerHTML);
		editor.focus();
		if (settings_panel) {
			editorDiv.appendChild(settingOpener);
			setupApi(editor, editorDiv, settingDiv, ace, options, loader);
			setupSettingPanel(settingDiv, settingOpener, editor, options);

			var state = "";
			event.addListener(settingOpener, "mousemove", function(e) {
				var rect = this.getBoundingClientRect();
				var x = e.clientX - rect.left, y = e.clientY - rect.top;
				if (x + y < (rect.width + rect.height) / 2) {
					this.style.cursor = "pointer";
					state = "toggle";
				} else {
					state = "resize";
					this.style.cursor = "nw-resize";
				}
			});

			event.addListener(settingOpener, "mousedown", function(e) {
				if (state == "toggle") {
					editor.setDisplaySettings();
					return;
				}
				container.style.zIndex = 100000;
				var rect = container.getBoundingClientRect();
				var startX = rect.width + rect.left - e.clientX;
				var startY = rect.height + rect.top - e.clientY;
				event.capture(settingOpener, function(e) {
					container.style.width = e.clientX - rect.left + startX
							+ "px";
					container.style.height = e.clientY - rect.top + startY
							+ "px";
					editor.resize();
				}, function() {
				});
			});
		}

		return editor;
	};

	function load(url, module, callback) {
		net.loadScript(url, function() {
			require([ module ], callback);
		});
	}

	function setupApi(editor, editorDiv, settingDiv, ace, options, loader) {
		var session = editor.getSession();
		var renderer = editor.renderer;
		loader = loader || load;

		function toBool(value) {
			return value == "true";
		}

		editor.setDisplaySettings = function(display) {
			if (display == null)
				display = settingDiv.style.display == "none";
			settingDiv.style.display = display ? "block" : "none";
		};

		editor.setOption = function(key, value) {
			if (options[key] == value)
				return;

			switch (key) {
			case "gutter":
				renderer.setShowGutter(toBool(value));
				break;

			case "mode":
				if (value != "text") {
					loader("mode-" + value + ".js", "ace/mode/" + value,
							function() {
								var aceMode = require("../mode/" + value).Mode;
								session.setMode(new aceMode());
							});
				} else {
					session.setMode(new (require("../mode/text").Mode));
				}
				break;

			case "theme":
				if (value != "textmate") {
					loader("theme-" + value + ".js", "ace/theme/" + value,
							function() {
								editor.setTheme("ace/theme/" + value);
							});
				} else {
					editor.setTheme("ace/theme/textmate");
				}
				break;

			case "fontSize":
				editorDiv.style.fontSize = value;
				break;

			case "softWrap":
				switch (value) {
				case "off":
					session.setUseWrapMode(false);
					renderer.setPrintMarginColumn(80);
					break;
				case "40":
					session.setUseWrapMode(true);
					session.setWrapLimitRange(40, 40);
					renderer.setPrintMarginColumn(40);
					break;
				case "80":
					session.setUseWrapMode(true);
					session.setWrapLimitRange(80, 80);
					renderer.setPrintMarginColumn(80);
					break;
				case "free":
					session.setUseWrapMode(true);
					session.setWrapLimitRange(null, null);
					renderer.setPrintMarginColumn(80);
					break;
				}
				break;

			case "useSoftTabs":
				session.setUseSoftTabs(toBool(value));
				break;

			case "showPrintMargin":
				renderer.setShowPrintMargin(toBool(value));
				break;

			case "showInvisibles":
				editor.setShowInvisibles(toBool(value));
				break;
			}

			options[key] = value;
		};

		editor.getOption = function(key) {
			return options[key];
		};

		editor.getOptions = function() {
			return options;
		};

		for ( var option in exports.options) {
			editor.setOption(option, exports.options[option]);
		}

		return editor;
	}

	function setupSettingPanel(settingDiv, settingOpener, editor, options) {
		var BOOL = {
			"true" : true,
			"false" : false
		};

		var desc = {
			mode : "Mode:",
			gutter : "Display Gutter:",
			theme : "Theme:",
			fontSize : "Font Size:",
			softWrap : "Soft Wrap:",
			showPrintMargin : "Show Print Margin:",
			useSoftTabs : "Use Soft Tabs:",
			showInvisibles : "Show Invisibles"
		};

		var optionValues = {
			mode : {
				text : "Plain",
				javascript : "JavaScript",
				xml : "XML",
				html : "HTML",
				css : "CSS",
				scss : "SCSS",
				python : "Python",
				php : "PHP",
				java : "Java",
				ruby : "Ruby",
				c_cpp : "C/C++",
				coffee : "CoffeeScript",
				json : "json",
				perl : "Perl",
				clojure : "Clojure",
				ocaml : "OCaml",
				csharp : "C#",
				haxe : "haXe",
				svg : "SVG",
				textile : "Textile",
				groovy : "Groovy",
				liquid : "Liquid",
				Scala : "Scala"
			},
			theme : {
				clouds : "Clouds",
				clouds_midnight : "Clouds Midnight",
				cobalt : "Cobalt",
				crimson_editor : "Crimson Editor",
				dawn : "Dawn",
				eclipse : "Eclipse",
				idle_fingers : "Idle Fingers",
				kr_theme : "Kr Theme",
				merbivore : "Merbivore",
				merbivore_soft : "Merbivore Soft",
				mono_industrial : "Mono Industrial",
				monokai : "Monokai",
				pastel_on_dark : "Pastel On Dark",
				solarized_dark : "Solarized Dark",
				solarized_light : "Solarized Light",
				textmate : "Textmate",
				twilight : "Twilight",
				vibrant_ink : "Vibrant Ink"
			},
			gutter : BOOL,
			fontSize : {
				"10px" : "10px",
				"11px" : "11px",
				"12px" : "12px",
				"14px" : "14px",
				"16px" : "16px"
			},
			softWrap : {
				off : "Off",
				40 : "40",
				80 : "80",
				free : "Free"
			},
			showPrintMargin : BOOL,
			useSoftTabs : BOOL,
			showInvisibles : BOOL
		};

		var table = [];
		table.push("<table><tr><th>Setting</th><th>Value</th></tr>");

		function renderOption(builder, option, obj, cValue) {
			builder.push("<select title='" + option + "'>");
			for ( var value in obj) {
				builder.push("<option value='" + value + "' ");

				if (cValue == value) {
					builder.push(" selected ");
				}

				builder.push(">", obj[value], "</option>");
			}
			builder.push("</select>");
		}

		for ( var option in options) {
			table.push("<tr><td>", desc[option], "</td>");
			table.push("<td>");
			renderOption(table, option, optionValues[option], options[option]);
			table.push("</td></tr>");
		}
		table.push("</table>");
		settingDiv.innerHTML = table.join("");

		var selects = settingDiv.getElementsByTagName("select");
		for ( var i = 0; i < selects.length; i++) {
			var onChange = (function() {
				var select = selects[i];
				return function() {
					var option = select.title;
					var value = select.value;
					editor.setOption(option, value);
				};
			})();
			selects[i].onchange = onChange;
		}

		var button = document.createElement("input");
		button.type = "button";
		button.value = "Hide";
		event.addListener(button, "click", function() {
			editor.setDisplaySettings(false);
		});
		settingDiv.appendChild(button);
	}
	exports.options = {
		mode : "text",
		theme : "textmate",
		gutter : "false",
		fontSize : "12px",
		softWrap : "off",
		showPrintMargin : "false",
		useSoftTabs : "true",
		showInvisibles : "true"
	};

});
