// Define $j if not defined (for tests only)
if (window.$j === undefined) {
	$j = jQuery;
}
// 	Toggles visibltity of object by @id@
function toggleBoxContent(id) {
	$j('#' + id).toggle();
	var box_btn_div = $j('#_btn');
	box_btn_div.addClass((box_btn_div.hasClass('Button')) ? 'ButtonSel' : 'Button');
}

// Perfoms automatic update of element which is refreshed by parent form of the specified field
function ajaxUpdateResults(url, target_id, form_field_id) {
	/* 
	 (TD) Method that updates a @target_id@ element by the content taken from @url@ by the form
	 assiciated with @form_field_id@.

	 SUGGESTION: use this method instead of the one below
	 */

	alert('deprecated')

	//	new Ajax.Updater(target_id, url, {
	//		asynchronous: true, // perform operations asynchronous
	//		evalScripts: true,  // evaluate scripts on the output generated by result
	//		onLoaded:function(request) {
	//			// Append a class to update progress bar here
	//			$(form_field_id).removeClassName("Progress");
	//		},
	//		onLoading:function(request) {
	//			// Remove progress bar here
	//			$(form_field_id).addClassName("Progress");
	//		},
	//		// Pass the serialized parameters from the form to the method
	//		parameters: $j('#'+form_field_id).parents('form').serialize()
	//	});
}

function showErrorMessage(tag_id) {
	$j('#' + tag_id).hide().fadeIn();
}

function hideErrorMessage(tag_id) {
	$j("#" + tag_id).fadeOut();
}

// TODO: Fix names of the functions below!
function toggle_all_form_fields_in(tag_id, enable) {
	var force = arguments.length > 2 ? arguments[2] : false;
	var element = ((typeof tag_id == 'String') ? $j("#" + tag_id) : $j(tag_id));
	element.find('input, select').each(function() {
		var e = $j(this);
		if (e.attr('type') != "submit" && e.attr('type') != "button") {
			toggle_form_field(e, enable, force)
		}
	})
}

function toggle_all_form_fields_with(tag_id, checkbox_id) {
	toggle_all_form_fields_in(tag_id, $j('#' + checkbox_id + "[type='checkbox']").attr('checked'))
	$j('#' + checkbox_id + "[type='checkbox']").removeAttr('disabled')
}

function enable_all_form_fields_in(element) {
	var force = arguments.length > 1 ? arguments[1] : true;
	toggle_all_form_fields_in(element, true, force);
}
function disable_all_form_fields_in(element) {
	var force = arguments.length > 1 ? arguments[1] : true;
	toggle_all_form_fields_in(element, false, force);
}

function toggle_form_field(element, enable) {
	var force = arguments.length > 2 ? arguments[2] : false;
	if (force) {
		element.data('originally-disabled', element.attr('disabled'));
	}
	if (enable && (force || !element.data('originally-disabled'))) {
		element.removeAttr('disabled');
	} else {
		element.attr('disabled', 'disabled');
	}
}

function enable_form_field(element) {
	toggle_form_field(element, true, true);
}

function disable_form_field(element) {
	toggle_form_field(element, false, true);
}

// (TD) Display popup_id element as popup for observed_id field in the form with options
function showAsPopup(popup_id, observed_id, options) {
	var popup = $j("#" + popup_id);
	var observed = $j("#" + observed_id);
	var pos = observed.offset();
	var layout_page = $j("#layout_page");
	if (layout_page.length > 0) {
		pos.top -= layout_page.offset().top; // Fix layout header impact
	}

	if (options['position'] == 'above') {
		pos.top -= popup.height();
	} else {
		pos.top += observed.outerHeight();
	}
	popup.css({'left': pos.left + "px", 'top': pos.top + "px", 'zIndex': 4000, 'position': 'absolute'});
	popup.show();
}

// (TD) Initializes popup_id element as popup for observed_id field in the form with options
function initPopup(popup_id, observed_id, options) {
	var observed = $j("#" + observed_id);
	observed.blur(function() {
		var popup = $j("#" + popup_id);
		popup.hide();
	});
	observed.focus(function() {
		showAsPopup(popup_id, observed_id, options)
	});
}

// (RB) Functions to work with configuration fields with buttons
function newPuzzleConfiguration(fieldName, name, kind, intent) {
	var oNewUrl = document.getElementById(fieldName + '_new_url');
	var oField = document.getElementById(fieldName + '_id');

	$j.ajax({
		url: oNewUrl.value,
		data: {
			kind: kind,
			intent: intent,
			name: name,
			clone: oField.value
		},
		success: function(data, status, request) {
			updatePuzzleConfigurations(request, fieldName, kind, intent)
		}
	})
}

function updatePuzzleConfigurations(request, fieldName, kind, intent) {
	var xml = request.responseXML;
	var root = xml.documentElement;
	var selId = root.attributes.getNamedItem('New');
	selId = selId ? selId.nodeValue : null;
	var fieldClass = ".puzzle-configurations-" + kind + "-" + (intent == ':any' ? 'any' : (intent ? intent : ''));
	var fields = $j(fieldClass);
	for (var i = 0; i < fields.length; i++) {
		var f = fields[i];
		updatePuzzleSelection(root, f.id, f.id == fieldName + '_id' ? selId : null);
	}
}

function updatePuzzleSelection(root, fieldId, selId) {
	var oField = document.getElementById(fieldId);
	var selPresent = false;
	if (!selId) {
		selId = oField.value;
	}
	var blank = oField.options.length > 0 && oField.options[0].value == "";
	while (oField.options.length > 0) {
		oField.options[0] = null;
	}
	if (blank) {
		oField.options.add(new Option('', ''));
	}
	for (var i = 0; i < root.childNodes.length; i++) {
		var oElem = root.childNodes.item(i);
		if (oElem.nodeType == 1) { // XML Element
			var cfgId = oElem.attributes.getNamedItem('Id').nodeValue;
			var cfgName = oElem.attributes.getNamedItem('Name').nodeValue;
			oField.options.add(new Option(cfgName, cfgId));
			if (selId == cfgId) {
				selPresent = true;
			}
		}
	}
	if (selId && selPresent) {
		oField.value = selId;
	}
}

function editPuzzleConfiguration(fieldName, kind, intent) {
	var oEditUrl = document.getElementById(fieldName + '_edit_url');
	var oField = document.getElementById(fieldName + '_id');
	if (oField.value != "") {
		var url = oEditUrl.value.replace("_ID_", oField.value);
		window.location = url;
	}
}

// (AM) Helps showing the user that some part of the page is being updated dynamically.
// Covers this part with white rectangle with opacity and shows progress indicator.
function updateInProgress(tagId) {
	var element = $j.isString(tagId) ? $j("#" + tagId) : $j(tagId);
	if (element.prev(".UpdateLid").length == 0) {
		element.before("<div class='UpdateLid'><div class='Updating'><p class='Message'></p></div></div>");
		var lid = element.prev(".UpdateLid").find(".Updating");
		lid.css({ width: element.width(), height: element.height() });
		return lid;
	}
}

// (AM) Undo actions done by updateInProgress
function updateFinished(tagId) {
	var element = $j.isString(tagId) ? $j("#" + tagId) : $j(tagId);
	element.prev(".UpdateLid").remove();
}

// (RB) Add id to any URL in form: ../controller/action?param1=val -> ../controller/action/id?param1=val
function addIdToUrl(url, id) {
	if (!id) {
		return url;
	}
	var qmPos = url.indexOf('?');
	var params = "";
	if (qmPos > 0) {
		params = url.substring(qmPos);
		url = url.substring(0, qmPos);
	}
	if (url[url.length - 1] != '/') {
		url += '/';
	}
	return url + id.toString() + params;
}

/* Common helper methods */

function icon_tag(icon) {
	return "<img class='Icon Icon" + icon.camelize(true) + "' src='" + Routes.null_image_path() + "'>";
}

function icon_label_tag(label, icon, options) {
	var label_tag = label !== null ? "<span class='IconLabel'>" + label + "</span>" : '';
	if (options.align == 'right') {
		return label_tag + icon_tag(icon);
	} else {
		return icon_tag(icon) + label_tag;
	}
}

function icon_link_tag(title, url, icon, options) {
	var label_tag = icon_label_tag(title, icon, options);
	var className = 'IconLink' + (options.className ? ' ' + options.className : '');
	return "<a href='" + url + "' class='" + className + "'>" + label_tag + "</a>";
}

/* Comments Panel */
var CommentsPanel = {
	init: function() {
		$j(".Comments .RemovedComment").hide();
	},
	submitForm: function(form) {
		var container = $j(form).parents('.OrderCommentsContainer')[0];
		var data = $j(form).serializeArray();
		$j(container).load(form.action, data);
		return false;
	},
	removeComment: function(link) {
		var container = $j(link).parents('.OrderCommentsContainer')[0];
		var form = $j(link).parents('form')[0];
		var data = $j(form).serializeArray();
		$j(container).load(link.href, data);
		return false;
	},
	showRemoved: function(link) {
		var container = $j(link).parents('.OrderCommentsContainer')[0];
		$j(container).find('.RemovedComment').show();
		$j(link).hide();
	},
	selectKind: function(link, kind) {
		var form = $j(link).parents('form')[0];
		var linkInput = $j(form).find("input[name='order_comment[kind]']")[0];
		$j(linkInput).val(kind);
		$j(".CommentForm a.CommentKind").removeClass("Selected");
		$j(link).addClass("Selected");
	}
};

/* Attachments Panel */
var AttachmentsPanel = {
	remove: function(link, attachment_id) {
		var container = $j(link).parents(".AttachmentsContainer");
		var attachments = container.find(".Attachments[data-url]");
		var url = attachments.attr("data-url");
		var li = $j(link).parents('li');
		$j.ajax({type: 'DELETE', url: url + '/' + attachment_id, success: function() {
			li.fadeOut(200);
		}});
		li.fadeTo(100, 0.5);
		$j(link).remove();
		return false;
	},
	refresh: function(element) {
		var container = $j(element).parents(".AttachmentsContainer");
		var attachments = container.find(".Attachments[data-url]");
		var url = attachments.attr("data-url");
		updateInProgress(attachments);
		attachments.load(url, function() {
			updateFinished(attachments);
		});
	},
	onFilesUploaded: function(element) {
		AttachmentsPanel.refresh(element);
	}
};


// TD: does exactly the same what flesh[:errors]=... in rails does.
// type = ['error', 'warning', 'notify']
// message = string / array of objects / array of string
// options = { autohide: false, progress: false }
//		Flash.flash_now([
//			{'error': 'this is an error'},
//			{'warning': 'this is a warning'}
//		], { autohide: true });

Flash = {
	flash_now: function(messages, options) {
		options = options ? options : {}
		var click_here_to_dismiss = _("Click to dismiss.")
		var tooltips = $j('div[id^="ui-tooltip"].ui-tooltip');

		if (!$j.isArray(messages)) {
			messages = [ messages ]
		} // push to array
		var messages_content = "";
		var flash_cl = "";
		var msg_count = 0;
		for (i = 0; i < messages.length; i++) {
			var message = messages[i];
			if (!$j.isPlainObject(message)) {
				message = { "notify": message }
			} // assume that's string

			var current_message = message.notify;
			if (current_message) {
				if (current_message.match(/^error:/)) { message = { "error": current_message.replace('error: ', '') }}
				if (current_message.match(/^warning:/)) { message = { "warning": current_message.replace('warning: ', '') }}
			}

			for (n in message) {
				var message_type = n;
				var message_text = message[n];
				var cl = message_type.charAt(0).toUpperCase() + message_type.slice(1);
				if (cl.match("Warning") && flash_cl != "Error") flash_cl = "Warning";
				if (cl.match("Error")) flash_cl = "Error"
				messages_content += '<div class="' + cl + '" >' + message_text + '</div>';
				msg_count++;
			}
		}

		var content;
		var extra_cl = options['class_name'];
		if (extra_cl) {
			content = '<div class="Popup Box FlashMessages ' + flash_cl + extra_cl + '">';
		} else {
			content = '<div class="Popup Box FlashMessages ' + flash_cl + '">';
		}

		content += messages_content;
		if (options['progress']) {
			content += '<div class="BigProgress"> </div>';
		}
		content += '<div class="Dismiss">' + click_here_to_dismiss + '</div>';
		content += '</div>'
		$j('#layout_flash').hide(); // to ensure we're hidden

		// Disable qtips to allow closing Flash by clicking on it without closing qtip.
		$j.each(tooltips, function() {
			$j(this).qtip('disable');
		});

		$j('#layout_flash').html(content);
		$j('#layout_flash').fadeIn();
		$j('#layout_flash').click(function() {
			$j.each(tooltips, function() {
				$j(this).qtip('enable');
			});
			$j(this).fadeOut();
		});
		$j(document).keyup(function(e) {
			if (e.keyCode == 27 || e.keyCode == 13) {
				$j('#layout_flash').fadeOut();
			}
		});
		if (options['autohide']) {
			setTimeout(function() {
				$j.each(tooltips, function() {
					$j(this).qtip('enable');
				});
				$j('#layout_flash').fadeOut()
			}, msg_count * 3000);
		}
	},
	error: function(message, options) {
		this.flash_now({'error': message}, options);
	},
	warning: function(message, options) {
		this.flash_now({'warning': message}, options);
	},
	notify: function(message, options) {
		this.flash_now({'notify': message}, options);
	},
	info: function(message, options) {
		this.flash_now({'info': message}, options);
	},
	hide: function(class_name) {
		if(class_name){
			$j('#layout_flash > ' + ' div.' + class_name).fadeOut();
		} else {
			$j('#layout_flash').fadeOut();
		}
	}
};

/** Box renderer
 * Usage: jQuery(element).box("Box Title");
 *				jQuery(element).box({title: "Box Title", id: "box_uniq_id"});
 * Returns: the main boxes' div elements.
 **/

(function($) {
	$.fn.box = function(options) {
		if (typeof options === 'string') {
			options = {title: options};
		}
		var header = "<div class='Title'><div class='Button' style='display: none;'></div><span>" + options.title + "</span></div>";
		var boxes = [];
		this.each(function() {
			var class_name = "Box" + (options.className ? ' ' + options.className : '');
			$(this).wrap("<div class='" + class_name + "'><div class='Cont' /></div>");
			$(this).parent().before(header).attr('id', options.id || "box_" + Math.round(Math.random() * 1000000));
			boxes.push($(this).parent().parent()[0]);
		});
		return $(boxes);
	}
})(jQuery);

/* String prototype extensions */

/* Converts a string separated by dashes and/or underscores into a camelCase equivalent.
 For instance, 'java-script' would be converted to 'javaScript'.
 With attribute firstUpper=true the function return the string with first letter upper, example: 'JavaScript'  */
String.prototype.camelize = function(firstUpper) {
	var result = this.replace(/[-_]([a-z])/ig, function(z, b) {
		return b.toUpperCase()
	});
	if (firstUpper) {
		result = result.replace(/^([a-z])/ig, function(z, b) {
			return b.toUpperCase()
		});
	}
	return result;
}

/* Stack is used to subclass Array class
 * Example:
 * function MyArray() {}
 * MyArray.prototype = new Stack(); // DO NOT USE: MyArray.prototype = new Array();
 * For details see:
 * http://webreflection.blogspot.com/2008/03/sorry-dean-but-i-subclassed-array-again.html
 * and
 * http://webreflection.blogspot.com/2008/05/habemus-array-unlocked-length-in-ie8.html
 * */
Stack = (function() {
	/**
	 * Subclassed JavaScript 1.5 Array for every browser.
	 * @author			Andrea Giammarchi
	 * @site				devpro.it
	 * @blog				webreflection.blogspot.com
	 * @date				2009/05/07
	 * @requires		IE 5.5+ , FF 1+ , Opera 8+ , Safari 2+
	 * @license		 Mit Style
	 */
	function Stack() {
		this.push.apply(this, Array.apply(null, arguments));
	}

	;
	Stack.prototype = new Array;
	Stack.prototype.length = 0;
	if (!new Stack(1).length) {
		Stack.prototype = {length:0};
		for (var
								 split = "join.pop.push.reverse.shift.slice.sort.splice.unshift".split("."),
								 length = split.length;
				 length;
						)
			Stack.prototype[split[--length]] = Array.prototype[split[length]];
	}
	;
	var toString = Object.prototype.toString,
					slice = Array.prototype.slice,
					concat = Array.prototype.concat
					;
	Stack.prototype.concat = function() {
		for (var Array = this.slice(0), i = 0, length = arguments.length; i < length; ++i) {
			if (toString.call(arguments[i]) != "[object Array]")
				arguments[i] = typeof arguments[i] == "object" ? slice.call(arguments[i]) : [arguments[i]];
		}
		;
		Array.push.apply(Array, concat.apply([], arguments));
		return	Array;
	};
	Stack.prototype.toString = Stack.prototype.join;
	Stack.prototype.constructor = Stack;
	return	Stack;
})();

/* *
 * Units - Units conversion module
 * */

var Units = {
	pt2mm: function(pt) {
		return pt * 0.352777167;
	},

	mm2pt: function(mm) {
		return mm * 2.83465058;
	},

	pt2inch: function(pt) {
		return pt * 0.01388888888889;
	},

	inch2pt: function(inch) {
		return inch * 72.0;
	},

	mm2inch: function(mm) {
		return mm * 0.039370147;
	},

	inch2mm: function(inch) {
		return inch * 25.399956;
	}
}

function MeasurementValue(str, opts) {
	this.parse(str);
	if (opts) {
		this.context = opts.context;
		this.page_method_sufix = opts.page_method_sufix;
	}
}

MeasurementValue.FRACTION_REGEXP = /([+-])?(\d*) *(\d+) *\/ *(\d+)/ // 'D D/D', ex. '12 1/2'
MeasurementValue.ABSOLUTE_UNITS = ['mm', 'pt', 'inch'];

// round to the 6th digit
MeasurementValue.round = function(value) {
	return Math.round(value * 1000000) / 1000000.0;
};

MeasurementValue.prototype = {
	parse: function(str) {
		if (str === undefined || str.match(/^\s*$/)) {
			// empty value
			this.value = 0;
			this.unit = 'mm';
		} else {
			var match = str.match(/(.+) *(mm|inch|pt|page)/); // column and page_body not supported
			if (match) {
				this.value = this.normalizeValue(match[1]);
				this.unit = match[2];
			} else {
				throw new Error("Invalid value: " + str)
			}
		}
	},
	normalizeValue: function(value) {
		value = value.replace(",", ".");
		var match;
		if (match = value.match(/^([+-]?)\.(.+)$/)) {
			value = m[1] + "0" + m[2];
		}
		return value.replace("+", "");
	},
	isAbsolute: function() {
		return $j.inArray(this.unit, MeasurementValue.ABSOLUTE_UNITS) >= 0;
	},
	toFloat: function() {
		var match;
		var value = this.value;
		if (value) {
			if (match = this.value.match(MeasurementValue.FRACTION_REGEXP)) {
				value = parseFloat(match[3]) / parseFloat(match[4]);
				if (match[2] != '') {
					value += parseFloat(match[2]);
				}
				if (match[1] == '-') {
					value = -value;
				}
			} else {
				value = parseFloat(value);
			}

			if (this.value.match(/%$/)) {
				value = value / 100.0;
			}
		} else {
			value = 0.0;
		}
		return value;
	},
	convertTo: function(unit) {
		var value = this.toFloat();
		if (this.unit != unit) {
			if (this.isAbsolute()) {
				value = Units[this.unit + "2" + unit](value);
			} else if (this.unit == 'page') {
				var pt_size = this.context["page_" + this.page_method_sufix];
				value = Units["pt2" + unit](value * pt_size);
			} else {
				throw new Error("Unit " + this.unit + " not supported");
			}
		}
		value = MeasurementValue.round(value);
		return value;
	},
	toMm: function() { return this.convertTo('mm'); },
	toPt: function() { return this.convertTo('pt'); },
	toInch: function() { return this.convertTo('inch'); }
};

// Extensions to Gettext
var gettext = new Gettext({
	domain: "messages",
	locale_data: window.json_locale_data || undefined
});
function _(msgid) { return gettext.gettext(msgid); }
function N_(msgid) { return msgid; }

Gettext.format = function() {
	var formatted = arguments[0];
	for (var i = 1; i < arguments.length; i++) {
		formatted = formatted.replace("{" + i + "}", arguments[i]);
	}
	return formatted;
};

/** Development Tools Control **/
jQuery(document).ready(function() {
	$j("#development_tools").mouseover(DevTools.show);
	$j("#development_tools").mouseout(DevTools.hide);
	$j("#development_tools a.Restart").click(DevTools.restart);
});

var DevTools = {
	show: function() {
		$j("#development_tools").addClass('Visible');
	},
	hide: function() {
		$j("#development_tools").removeClass('Visible');
	},
	restart: function() {
		var url = $j(this).attr('href');
		var only_processors = url.match(/processors/);
		DevTools.hide();
		$j("#development_tools").unbind('mouseover');
		ApplicationRestart.postAndWait(url, only_processors);
		return false;
	}
};

var ApplicationRestart = {
	postAndWait: function(url, reloadImmediately) {
		var dlg_tag = $j("<div class='RestartingDialog'><div class='Updating'>" + _("Restarting, please wait...") + "</div></div>");
		$j(document).append(dlg_tag);
		dlg_tag.dialog({
			title: _("Restarting..."),
			autoOpen: true,
			closeOnEscape: false,
			draggable: false,
			resizable: false,
			modal: true,
			width: 400,
			height: 100,
			minHeight: 100
		});
		dlg_tag.parent().find('.ui-dialog-titlebar-close').hide();
		$j("#ajax_errors_box").unbind("ajaxError");
		$j.post(url, {}, function() {
			if (reloadImmediately) {
				ApplicationRestart.reloadPage();
			} else {
				dlg_tag.find(".Updating").html(_("Waiting for application to start..."));
				setTimeout(ApplicationRestart.waitForApplication, 4000);
			}
		});
	},
	waitForApplication: function() {
		$j.ajax({
			url: Routes.url_root,
			global: false,
			success: function() {
				ApplicationRestart.reloadPage();
			},
			error: function() {
				setTimeout(ApplicationRestart.waitForApplication, 2000);
			}
		});
	},
	reloadPage: function() {
		$j(".RestartingDialog .Updating").html(_("Reloading page..."));
		window.location.reload();
	}
};

/** Property table renderer
 * Usage:
 *	var table = jQuery(table_element).propertyTable({className: 'Order Details'});
 *	table.item("Name:", name); // item with label
 *	table.item(null, value); // item without label
 *	table.itemRow("Title in single row:", value); // item and label in separate rows
 *	table.section("Other properties");
 **/

(function($) {
	PropertyTableMethods = {
		item: function(label, value, opts) {
			var separate_rows = opts && opts.separate_rows;
			var row_class = label && !separate_rows ? "TwoColumns" : "SingleColumn";
			var row_tag = $("<tr class='" + row_class + "'>").appendTo(this);
			if (label) {
				var label_tag = $j("<th>").append(label).appendTo(row_tag);
				if (separate_rows) {
					label_tag.attr('colspan', 2);
					row_tag = $("<tr class='" + row_class + "'>").appendTo(this);
				}
			}
			var value_tag = $("<td>").append(value).appendTo(row_tag);
			if (!label || separate_rows) {
				value_tag.attr('colspan', 2);
			}
			row_tag.append(value_tag);
			return this;
		},
		itemRow: function(label, value) {
			return this.item(label, value, {separate_rows: true});
		},
		section: function(title) {
			var row_tag = $("<tr class='Section'>").appendTo(this);
			$("<td colspan='2'>").append(title).appendTo(row_tag);
			return this;
		}
	};

	$.fn.propertyTable = function() {
		this.addClass('Properties');
		$.extend(this, PropertyTableMethods);
		return this;
	};
})(jQuery);

