var SculpteoOperation = function(uuid, base, args) {
	this.uuid = uuid;
	this.args = args;
	this.base = base;
	this.age = 0;
	this.modify_queue = Array();
	this._refreshUrl = URLS['x_refresh_model']+"?uuid="+this.uuid;
};

SculpteoOperation.prototype.url = function() {
	this.args['uuid'] = this.uuid;
	var url = $.param.querystring(this.base, this.args);
	return url;
}

var updateTimers = {};

SculpteoOperation.prototype.call = function(callback, delay) {
	// generates a (args list + temp id) sort of hash. 
	// For one type of parameter, we don't want to process useless operations
	var args_list = [];
	for (k in this.args) {
		args_list.push(k);
	}
	args_list.push(this.args.temp_id);
	var url = this.url();
	var hash = this.base + args_list.join("");

	if (updateTimers[hash])
		clearTimeout(updateTimers[hash]);
	var temp_id = this.args.temp_id;
	updateTimers[hash] = setTimeout(function(){effective_call(url, callback, temp_id)}, delay ? delay : 200);
}

function effective_call(url, callback) {
	$.ajax({ 
		url: url,
		cache: false,
		success: function(data) { if (callback) { callback(data); } }
	});
}

SculpteoOperation.prototype.setOperationIds = function(el) {
	var group = $(el).closest(".OperationGroup");
	this.args.temp_id = $(el).closest(".SculpteoOperation").attr("id");
	this.args.uuid = getCurrentUuid();
}

SculpteoOperation.prototype.select = function (design_id, callback) {
	SelectDesignOperation.args.uuid = getCurrentUuid();
	SelectDesignOperation.args.design_id = design_id;
	$("#loader_computing").show();
	$.get(SelectDesignOperation.url(), callback);
}

//SculpteoOperation.prototype.init = function (request_uuid, skipFetchData) {
SculpteoOperation.prototype.init = function (skipFetchData) {
	/* uuid for history navigation */
	var uuid = getCurrentUuid();
	$("[name=uuid]").val(uuid);

	if (!uuid) {
		// should not happen...
		alert("Fix this page !");
		return;
	} 
	this.args.uuid = uuid;
	// Try to reload last operation (browser refresh, history navigation, re-edition)
	var success = false;
	var args = this.args;
	if (!skipFetchData) {
		$.ajax( {
			url: URLS['designs_model_x_get_operation'],
			data: { uuid: uuid},
			dataType: 'json',
			async: false,
			cache: false,
			success: function(data, textStatus) { 
				$.extend(args, data.body.list[0]);
				success = true;
			}
		} );
	} else {
		success = true;
	}
	return success;
}

function getCurrentUuid() {
	return $.deparam.querystring()['uuid'];
}

function newOperation(_uuid, class_id, args, callback)
{
	_uuid = getCurrentUuid(_uuid);
	var add_op = new SculpteoOperation(_uuid, URLS['x_add_operation'], { });

	$.extend(add_op.args, args);

	$.post(add_op.url(), { id : class_id },
		function(data, textStatus, xhr) {
			if (data.error && data.error.id == 0) {
				instance_id = data.body.id;
				callback(instance_id);
			}
		}, 'json');
}

SculpteoOperation.prototype.modifyOperation = function(instance_id, args, callback)
{
	if (instance_id == 0)
		return;

	this.modify_queue.m = 1;
	
	// try to find an item with the same instance_id
	var params = false;
	for (i in this.modify_queue) {
		if (this.modify_queue[i].instance_id == instance_id) {
			//log("modifyOperation: found item for "+instance_id);
			params = this.modify_queue[i];
		}
	}
	if (!params) {
		//log("modifyOperation: new item for "+instance_id);
		params = { instance_id: instance_id, callback: callback};
		this.modify_queue.push(params);
	}
	
	// merge elements into queue item, for later call
	$.extend(params, args);
	//log("modifyOperation: params for "+instance_id + ": "+concatObject(params));
	
	this.modify_queue.m = 0;
	if (this.modify_queue.timeout) {
		clearTimeout(this.modify_queue.timeout);
	}
	var me = this;
	this.modify_queue.timeout = setTimeout(function() { me.run_modify_queue() }, 150 );
}
	
SculpteoOperation.prototype.run_modify_queue = function() {
	if (this.modify_queue.timeout) {
		clearTimeout(this.modify_queue.timeout);
		this.modify_queue.timeout = false;
	}
	if (this.modify_queue.length == 0) {
		//log("run_modify_queue: nothing to do");
		return;
	}
	var me = this;
	if (this.modify_queue.m == 1) {
		//log("run_modify_queue: wait...");
		setTimeout(function() { me.run_modify_queue }, 150 );
		return;
	}
	var params = this.modify_queue.shift();
	var instance_id = params.instance_id,
		callback = params.callback;
	delete params.instance_id;
	delete params.callback;
	//log("run_modify_queue: params: "+concatObject(params));
	me.age++;
	var post_url = URLS['x_modify_operation']+"?uuid="+this.uuid+"&temp_id="+instance_id+"&age="+me.age;
	$.ajax({ 
		type: 'POST',
		url: post_url,
		data: params,
		cache: false,
		success: function(data) { if (data.age > me.age) me.age=data.age; if (callback) { callback(data); } },
		complete: function() { me.run_modify_queue } // continue to un-queue
	});
	
}



SculpteoOperation.prototype.modifyOperationAndRefresh = function(el, args, data)
{
	if (data) {
		$.extend(data, args);
	}
	var instance_id = $(el).closest(".SculpteoOperation").attr("id");
	if (instance_id == 0)
		return false;
	this.modifyOperation(instance_id, args, refreshModel);
	return true;
}

SculpteoOperation.prototype.refreshUrl = function() {
	return this._refreshUrl;
}


function clearOperations(_uuid, callback)
{
	_uuid = getCurrentUuid(_uuid);
	var clear_op = new SculpteoOperation(_uuid, URLS['x_clear_operations'], { });

	clear_op.call(callback);
}

function applyOperations(_uuid, callback)
{
	_uuid = getCurrentUuid(_uuid);
	var refresh_op = new SculpteoOperation(_uuid, URLS['x_refresh_model'], { preview: false });
	refresh_op.init(_uuid, true);

	$.ajax({
		url: refresh_op.url() + "&viewer=static",
		cache:false,
		context:this,
		success: function(data, status, req) {
			callback();
		}
	});
}

function extractId(el) {
	return $(el).attr("id").match(/\d+/g)[0];
}

function getIdFromShort(el) {
	return $(el).closest(".ShortOperation").attr("id").match(/\d+/g)[0];
}

function getOperation(el) {
	return $(el).closest(".SculpteoOperation");
}

function getShortOperation(el) {
	var id;
	if ($(el).hasClass("SculpteoOperation")) {
		id = extractId(el);
	} else {
		id = extractId(getOperation(el));
	}

	if (id == 0)
		return $(".toolbox-template-" + M_operations.current_operation);
	else
		return $("#short-"+id);
}

function getParameter(el) {
	return $(el).closest(".SculpteoParameter");
}

function bindOperations(el) {
	$(".SculpteoOperation", el).each(function (index, op) {
		bindOperation(op);
	});
}

function refreshOp(el) {
	var refresh_op = new SculpteoOperation(getCurrentUuid(), URLS['x_list_operation'], { });
	refresh_op.setOperationIds($(el));
	var to_refresh = $(el).closest(".SculpteoOperation");
	
	$.getJSON(refresh_op.url(), {'op_id': refresh_op.args.temp_id },
			function(data, textStatus) {
				if (data.error && data.error.id == 0) {
					if (! $.isArray(data.body)) {
						ops = [ data.body ];
					} else {
						ops = data.body;
					}
					$.each(ops, function (index, op) {
						var new_op = $(op['long']);
						$(to_refresh).replaceWith($(new_op).addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content-active"));
						bindOperation($(new_op));
					});
				}
			});		
}

function bindOperation(op) {
	if ($(op).hasClass("OperationBinded")) {
		return;
	}
	$(op).addClass("OperationBinded");
	//log("data = " + $(".data", op).html());
	//log($(op).html());

	var data;
	if ($(op).data('data')) {
		data = $(op).data('data');
	} else {
		data = window.JSON && JSON.parse($(".data", op).html()) || eval('(' + $(".data", op).html() + ')');
		$(op).data('data', data);
	}
	var real = data.temp_id;
	
	$(".lock-operation", op).click(function(evt) {
		var target = this.href;
		evt.preventDefault();
		if ($(this).is(".ui-icon-locked")) {
			locked = "0";
			$(this).removeClass("ui-icon-locked").addClass("ui-icon-unlocked");
			$(this).attr("title", gettext("Lock parameter"));
		} else {
			locked = "1";
			$(this).removeClass("ui-icon-unlocked").addClass("ui-icon-locked");
			$(this).attr("title", gettext("Unlock parameter"));
		}
		var lock_op = new SculpteoOperation(getCurrentUuid(), target, { locked: locked});
		lock_op.setOperationIds(this);
		lock_op.call($.noop);
		return false;
	});
	$(".ui-state-active", op).hover(function(evt) { $(this).toggleClass("ui-state-highlight")});


	$(".toolbox-back", op).click(function() {
		if (typeof($(this).data("undo")) != "undefined") {
			$(this).data("undo")();
		}
		return false;
	});

	$(".toggle-translation-description", op).click(function() {
		$(".translation-description", getOperation(this)).toggle();
		$(".translated-description", getOperation(this)).focus();
		if ($(".translated-description", getOperation(this)).is(":hidden")) {
			$(".translated-description", getOperation(this)).val($(".main-description", getOperation(this)).val());
		}
		return false;
	});
	if ($(".main-description", op).val() != $(".translated-description", op).val()) {
		$(".translation-description", op).show();
	}
	$(".main-description, .translated-description", op).keyup(function() {
		var temp_id = extractId(op);
		M_operations.op.modifyOperation(
				temp_id,  
				{ 'description_fr': $("[name=description_fr]", getOperation(this)).val() ,
				  'description_en': $("[name=description_en]", getOperation(this)).val()
				} );
	});

	var callback_with_data = function(args) {
		var d = $(op).data('data');
		$.extend(d, args);
		$(op).data('data', d);
		M_operations.op.modifyOperationAndRefresh($(op), args);
	};

	var parameters_class = {
		designoperationsliderparameter: {
			init: function($param) {
				var id = $param.attr("id");
				var minimum = parseFloat($(".slider-control .minimum", $param).html());
				var maximum = parseFloat($(".slider-control .maximum", $param).html());
				$(".slider-control", $param).SliderControl({
					min: minimum,
					max: maximum,
					step: (maximum - minimum) / 100,
					value: data[id],
					reset_value: data[id],
					use_callchangeoperation: false,
					controlled_param: id
				});
			}
		},
		designoperationtextparameter: {
			init: function($param) {
				var id = $param.attr("id");
				$("input", $param).keyup({cb: callback_with_data}, parameters_class.designoperationtextparameter.text_input);
				$("input", $param).blur({cb: callback_with_data}, parameters_class.designoperationtextparameter.text_blur);
				$("input", $param).attr("name", id).val(data[id]);
			},

			text_input: function(event) {
				if (event.keyCode >= 37 && event.keyCode <= 40 ) {
					// arrow
					return;
				}
		
				if (event.keyCode == 13) { // Enter
					var data = {};
					data[this.name] = $(this).val();
					event.data.cb(data);
				}
			},
		
			text_blur: function(event) {
				var data = {};
				data[this.name] = $(this).val();
				event.data.cb(data);
			}
		},

		designoperationselectparameter: {
			init: function($param) {
				var id = $param.attr("id");
				$("select", $param).change({cb: callback_with_data}, parameters_class.designoperationselectparameter.change);
				$("select", $param).val(data[id]);
			},

			change: function(event) {
				var $param = getParameter(this);
				var id = $param.attr("id");
				var data = {};
				data[id] = $(this).val();
				event.data.cb(data);
			}
		},

		designoperationdesignparameter: {
			init: function($param) {
				log("param = " + $param.attr("id"));

				parameters_class.designoperationdesignparameter.loadlist($param);
				$("ul.design-list", $param).delegate("li", "click", {cb: callback_with_data}, function(event) {
					var img = $(".design-thumbnail", $(this));
					var design_id = img.attr("id");
					var id = $param.attr("id");
					var data = {};
					data[id] = design_id;
					event.data.cb(data);
				});
			},

			append_design: function($el, design) {
				$(".design-list", $el).append('<li class="round small-gallery-even"><img src="'+design.thumbnail_frontview + '" class="design-thumbnail" id="' + design.uuid + '" title="' + design.name + '" alt="' + design.name +'" />');
			},

			loadlist: function($el) {
				var url = URLS['x_design_list'];
				var count = 50;
				var start = 0;

				var loadsome = function(from) {
					$.getJSON(url, { 'count' : count, 'start': start },
						  function(data, textStatus) {
							  if (data.error && data.error.id == 0) {
								  if (! $.isArray(data.body)) {
									  designs = [ data.body ];
								  } else {
									  designs = data.body;
								  }

								  $.each(designs, function (index, design) {
									parameters_class.designoperationdesignparameter.append_design($el, design);
								  });

								  if (designs.length == count) {
									  start += count;
									  setTimeout(function() { loadsome(start); });
								  }
							  }
						  }
					);
				};
				loadsome(0);
			}
		}
	};

	$(".parameters_config .add-param-button", op).click(function() {
		var identifier = $("[name=param_identifier]", getOperation(this)).val();
		var descr_en = $("[name=param_description_en]", getOperation(this)).val();
		var descr_fr = $("[name=param_description_fr]", getOperation(this)).val();
		var type = $(".param_type", getOperation(this)).val();
		var data = {};
		data[identifier + '.type'] = type;
		data[identifier + '.description_fr'] = descr_fr;
		data[identifier + '.description_en'] = descr_en;

		var temp_id = extractId(op);
		M_operations.op.modifyOperation(temp_id, data, function() {
			var typename = $(".parameters_config .template-" + type).attr("id");
			var param = $($(".parameters_config .template-" + type).html());
			if (LANG == "en")
				$(".param_description", param).text(descr_en);
			else
				$(".param_description", param).text(descr_fr);
			param.attr('id', identifier);
			$(".parameters").append(param);
			if (parameters_class[typename] && parameters_class[typename].init) {
				$param = $(".parameters #" + identifier);
				parameters_class[typename].init($param);
			}
		});
	});

	$(".parameters", op).delegate(".del-param-button", "click", function() {
		var $param = getParameter(this);
		var identifier = $param.attr("id");
		var data = {};
		data[identifier + '.type'] = 0;
		$param.html('');
		var temp_id = extractId(op);
		M_operations.op.modifyOperationAndRefresh($(op), data);
	});

	for (klass in parameters_class) {
		$(".parameters ." + klass).each(function() {
			var $param = getParameter(this);
			parameters_class[klass].init($param);
		});
	}

	if (data && real) {
		$(op).sculpteoOperation('from_class', {
			callback: function(el, data) { M_operations.op.modifyOperationAndRefresh(el, data) },
			viewer_click: function(el, data) { M_operations.op.modifyOperationAndRefresh(el, data) }
		});
	}
	return data;
}

var SelectDesignOperation = new SculpteoOperation(getCurrentUuid(), URLS['designs_model_x_select_design'], { });

function color3f_to_html(r, g, b)
{
            var c;
            c = "#" + (r * 255 < 16 ? "0" : "") + parseInt(r * 255).toString(16) +
                      (g * 255 < 16 ? "0" : "") + parseInt(g * 255).toString(16) +
                      (b * 255 < 16 ? "0" : "") + parseInt(b * 255).toString(16);
            return c;
}

$.fn.SliderControl = function(o) {
	return this.each(function() {
		if (Object.size(o) == 1 && typeof(o['value']) != 'undefined') {
			$(".the-slider", this).slider(o);
			return;
		}

		var defaults = {
			orientation: 'horizontal',
			precision: 0,
			slide: function(evt, ui) {
				$(".text-value", $(this).closest(".slider-control")).text(ui.value.toFixed(o.precision));
			},
			change: function(evt, ui) {
				$(".text-value", $(this).closest(".slider-control")).text(ui.value.toFixed(o.precision));
			},
			stop: function(evt, ui) {
				var param = $(this).closest(".slider-control").data("controlled-param");
				var opts = {} ; opts[param] = ui.value;
				callChangeOperation(opts);
			},
			use_callchangeoperation: true,
			cb: null
		};
		var options = $.extend(defaults, o);
		if (options.cb) {
			options['stop'] = function (evt, ui) {
				var param = $(this).closest(".slider-control").data("controlled-param");
				var opts = {} ; opts[param] = ui.value;
				options.cb(opts);
			}
		} else if (!options.use_callchangeoperation) {
			options['stop'] = function (evt, ui) {
				var param = $(this).closest(".slider-control").data("controlled-param");
				var opts = {} ; opts[param] = ui.value;
				M_operations.op.modifyOperationAndRefresh(this, opts, o.data);
			}
		}

		if (typeof(options['controlled_param']) != 'undefined') {
			$(this).closest(".slider-control").data("controlled-param", options['controlled_param']);
		}
		if (typeof(options['reset_value']) != 'undefined') {
			$(this).closest(".slider-control").data("reset-value", options['reset_value']);
		}
		if (typeof(options['value']) != 'undefined') {
			$(this).closest(".slider-control").data("default-value", options['value']);
			$(".text-value", $(this).closest(".slider-control")).text(options['value']);
		}
		if (typeof(options['change_value']) != 'undefined') {
			$(".the-slider", this).slider("value", options['change_value']);
			var param = $(this).closest(".slider-control").data("controlled-param");
			var opts = {} ; opts[param] = options['change_value'];
			callChangeOperation(opts);
			return ;
		}

		$(".the-slider", this).slider(options);

		$('.slider-reset', this).unbind("click").click(function(evt){
			var opts = {} ;
			var param = $(this).closest(".slider-control").data("controlled-param");
			if (typeof($(this).closest(".slider-control").data("reset-value")) != 'undefined') {
				opts[param] = $(this).closest(".slider-control").data("reset-value");
			} else {
				opts[param] = $(this).closest(".slider-control").data("default-value");
			}
			callChangeOperation(opts);
			$(".the-slider", $(this).closest(".slider-control")).slider('option', 'value', opts[param]);
			return false;
		});
	});
};

$.fn.ColorControl = function(o) {
	/**
	 * Control a color parameter of an operation
	 * important things are: div with class "color-wheel" inside a div with class "color-control"
	 * <div id="BasisColourControl" class="color-control">
	 *	 <label class="m8t">{% trans "Background colour" %}:</label>
	 *   <div class="color-weel"></div>
	 * </div>
	 */
	return this.each(function() {
		var defaults = {
				use_callchangeoperation: true,
				color_suffix: '',
				color_feedback: false,
				cb: null
		}
		if ($('.color-weel', this).length == 0) {
			return;
		}
		var options = $.extend(defaults, o);
		if (typeof(options['controlled_param']) != 'undefined') {
			$(this).closest(".color-control").data("controlled-param", options['controlled_param']);
			$(this).closest(".color-control").data("controlled-param-suffix", options['color_suffix']);
			$(this).closest(".color-control").data("controlled-param-colorfeedback", options['color_feedback']);
		}
		if ( !$(".farbtastic", this).length) {
			if (options.use_callchangeoperation || options.cb) {
				$('.color-weel', this).farbtastic(function callback(color) {
					var opts = {} ;
					var the_param = $(this.wheel).closest(".color-control").data("controlled-param");
					var suffix = $(this.wheel).closest(".color-control").data("controlled-param-suffix");
					var cols = this.unpack(color);
					opts[the_param+"_r"+suffix] = cols[0];
					opts[the_param+"_g"+suffix] = cols[1];
					opts[the_param+"_b"+suffix] = cols[2];
					if (options.cb) {
						options.cb(opts);
					} else {
						callChangeOperation(opts);
					}
				});
			} else {
				$(".color-weel", this).farbtastic( function callback(color) {
					var cols = this.unpack(color);
					var opts = {} ;
					var the_param = $(this.wheel).closest(".color-control").data("controlled-param");
					var suffix = $(this.wheel).closest(".color-control").data("controlled-param-suffix");
					var feedback_elt = $(this.wheel).closest(".color-control").data("controlled-param-colorfeedback");
					var cols = this.unpack(color);
					opts[the_param+"_r"+suffix] = cols[0];
					opts[the_param+"_g"+suffix] = cols[1];
					opts[the_param+"_b"+suffix] = cols[2];
					M_operations.op.modifyOperationAndRefresh(this.wheel, opts, o.data);
					if (feedback_elt) {
						$(feedback_elt, getShortOperation(this.wheel)).css("background-color", color);
					}
				});
			}
		}
		if (typeof(options['value']) != 'undefined') {
			$.farbtastic($('.color-weel', this)).setColor(color3f_to_html(options['value'].r,
					options['value'].g,
					options['value'].b), true);
		}
	});
};

