jQuery(
	function() {
		// bits common to either editor context:
		if (window.console) {
			if (window.console.debug) {
				// is FF+firebug
			} else {
				// is webkit
			}
		} else { // is IE
			window.console = {
				log   : function() {},
				assert: function() {}
			};
		}

		var GetSelection = function(field) {
			if (field.selectionStart) {
				// moz
				return {begin: field.selectionStart, end: field.selectionEnd};
			} else if (document.selection) {
				// IE is pita. todo.
				//
				//  http://laboratorium.0xab.cd/jquery/fieldselection/0.1.0/test.html
				// 	   failed near \r in ie7
				//  http://stackoverflow.com/questions/263743/how-to-get-cursor-position-in-textarea
				//  http://stackoverflow.com/questions/235411/is-there-an-internet-explorer-approved-substitute-for-selectionstart-and-select#235582
				//  http://stackoverflow.com/questions/164147/character-offset-in-an-internet-explorer-textrange
				//  http://snipplr.com/view/5144/getset-cursor-in-html-textarea
				//  http://parentnode.org/javascript/working-with-the-cursor-position/
			}
			return {begin: -1, end: -1};
		};

		var SetSelection = function(field, range) {
			if (field.selectionStart) {
				field.selectionStart = range.begin;
				field.selectionEnd   = range.end;
			} else if (document.selection) {
			}
		};

		var last_acceptable_value = "";
		var ValidateEngraving = function(obj) {
			if (last_acceptable_value === obj.value) return "";

			// on safari3.2.3 (not 4), spurious '\n' is appended whenever safari thinks it can get away with it
			// (repro: in empty box, enter '1[enter]' with obj debug function enabled:
			// var debug = "";
			// for (var i = 0; i < obj.value.length; ++i) {
			// 	var c = obj.value[i];
			// 	if (c === '1') debug += c;
			// 	else debug += "["+obj.value.charCodeAt(i)+"]";
			// }
			// alert(debug);

			var lines = obj.value.split('\n');
			if (obj.rows < lines.length) {
				// the safari bug revealed a universal annoyance:
				// set textarea="1\n"
				// put cursor before "1". push "1" to line 5. obj triggers filter.

				if (last_acceptable_value + "\n"   === obj.value) {
					// users is explicitly asking for sixth line
				} else if (last_acceptable_value + "\r\n" === obj.value) {
					// same, on ie.
				} else {
					// user has bumped text into line max+1; can we fix?
					if (/\n[ \t]*$/.test(obj.value)) {
						var sel = GetSelection(obj);
						obj.value = obj.value.replace(/\r?\n[ \t]*$/, "");
						SetSelection(obj, sel);
						return ValidateEngraving(obj); //this removes only enough trailing \n's to get us to pass the filter.
					}
				}
				obj.value = last_acceptable_value;
				return "Engraving may not exceed "+obj.rows+" lines";
			}

			for (var iLine = 0; iLine < lines.length; ++iLine) {
				// on ie, each string was terminated in \r\n
				var line = lines[iLine];
				var len = line.length;
				if (line.charAt(line.length-1) === '\r') --len;

				if (obj.cols < len) {
					obj.value = last_acceptable_value;
					return "Line " + (iLine + 1) + " exceeded "+obj.cols+" character limit";
				}
			}

			last_acceptable_value = obj.value;
			return "";
		};

		// in either mode, there's one ProductFields for a tac.
		var $engraving_field = $("textarea[name^=ProductFields]"); // each product's custom text field has a globally unique name.
		var engraving_field  = $engraving_field.get(0);

		engraving_field.wrap = "off";
		// FF refuses to not allocate space for scrollbars. IE ironically looks nice.
		// in the spree cart, we just set em sizes, so I'll do that.
		//engraving_field.style.width  = "auto";
		//engraving_field.style.height = "auto";
		engraving_field.style.overflow = "hidden";
		//engraving_field.style.fontFamily = "'Arial Narrow',Arial,sans-serif";
		//engraving_field.style.fontSize = "1em";
		//engraving_field.style.fontWeight = "600";

		var $carteditform = $("#CartEditProductFieldsForm");
		if ($carteditform.get().length > 0) {
			// mode Beta: edit-in-cart: not yet scheduled

		} else {
			// mode Alpha: edit-in-add_product:
			var FindDropdown = function(name) {
				var iDropdown = 1;
				while (true) {
					//console.log("trying variation " + iDropdown);
					var $candidate = $("select[name=variation\\["+iDropdown+"\\]]");
					//if ($candidate.length > 1) console.log("error: "+$candidate.length+" matches");
					if ($candidate.length !== 1) return;
					var $label = $candidate.parent().prev();
					var label = $label.get(0);
					//console.log("class: "+label.className);
					//console.log("label: '"+label.innerHTML+"'");
					if (label.innerHTML === name) return $candidate;
					++iDropdown;
				}
			};

			var $capacity          = FindDropdown("Capacity:");
			var $engraving_control = FindDropdown("Text Engraving Options:");
			var $engraving_block   = $engraving_field.parent().parent();

			var ScrapeParameters = function() {
				if (window.location.search.length > 0) {
					var ScrapeOrientation = function(val) {
						switch (val.slice(0,1)) {
							case 'p':
								$engraving_control.val("Portrait");
							break;
							case 'l':
								$engraving_control.val("Landscape");
							break;
							default:
								return;
						}
						$engraving_control.trigger('change');
					};

					var ScrapeSize = function(val) {
						var char1 = val.slice(0,1);
						var char2 = val.slice(1,2);
						switch (char1) {
							case '1':
								if (val.length > 1 && char2 === '6') {
									$capacity.val(val.slice(0,2) + " GB");
									break;
								}
							case '2':
							case '4':
							case '8':
								$capacity.val(char1 +" GB");
						}
					};

					var ScrapeText = function(val) {
						$engraving_field.val(last_acceptable_value = decodeURIComponent(val));
					};

					var params = window.location.search.slice(1).split('&');
					for (var pass = 0; pass < 2; ++pass) {
						//two pass, to ensure text isn't demolished by orientation regardless of url order
						for (var i = 0; i < params.length; ++i) {
							var param = params[i].split('=');
							if (param.length > 1 && param[1].length > 0) {
								switch (param[0]) {
									case 'capacity':    if (pass===0) ScrapeSize       (param[1]); break;
									case 'orientation': if (pass===0) ScrapeOrientation(param[1]); break;
									case 'text':        if (pass===1) ScrapeText       (param[1]); break;
								}
							}
						}
					}
				}
			};
			// configure custom text field:
			$engraving_block.hide();

			var orient = {none: "5", land: "6", port: "7", current: $engraving_control.val()};
			var last_user_specified_value = engraving_field.value;

			var ConfigureEngraving = function() {
				if (this.value === orient.none) {
					$engraving_block.hide();
				} else {
					$engraving_block.show();
					if (this.value === orient.port) {
						engraving_field.cols = 21;
						engraving_field.rows = 7;
						engraving_field.style.width  = "14em";
						engraving_field.style.height = "8.4em";
					} else {
						engraving_field.cols = 22;
						engraving_field.rows = 5;
						engraving_field.style.width  = "13.5em";
						engraving_field.style.height = "6em";
					}
				}

				last_acceptable_value = last_user_specified_value;

				var lines = last_acceptable_value.split('\n').slice(0, engraving_field.rows);
				for (var i = 0; i < lines.length; ++i) {
					lines[i] = lines[i].slice(0, engraving_field.cols);
				}

				$engraving_field.val(last_acceptable_value = lines.join('\n'));
			};

			$engraving_control.change(ConfigureEngraving);
			$engraving_control.keyup (ConfigureEngraving);

			ScrapeParameters();
			// regardless of whether params changed the default, we _have_ set all "unset" variation fields, and want "add to cart" to accept defaults.
			$capacity.trigger('change');

			// add a feedback panel beneath the editor
			$engraving_field.parent().append("<div id=feedback>&nbsp;</div>");
			var $feedback = $("#feedback");
			var CatchFeedback = function() {
				if (last_acceptable_value === this.value) return;
				$feedback.html(ValidateEngraving(this) + "&nbsp;");
				last_user_specified_value = last_acceptable_value;
			}

			// install text validator:
			$engraving_field.keyup(CatchFeedback); //do filter after the value has changed
		}
	}
);

