// Setup Soomo namespace
var Sm = Sm || {};
Sm.Cascading = {};

/*
 * Handles cascading selector client-side logic, backed by JSON
 */

Sm.Cascading.Dependent = Class.create({
	initialize: function(options) {
		this.onParentChange = options.onParentChange || Prototype.emptyFunction;
		this.onParentLoad = options.onParentLoad || Prototype.emptyFunction;
		this.parents = options.parents || [];
		this.parents.each(function(parent) {
			parent.addDependent(this);
		}.bind(this));
	},

	parentChanged: function(parent) {
		this.onParentChange.call(this, parent);
	},

	parentLoaded: function(parent) {
		this.onParentLoad.call(this, parent);
	}
});

Sm.Cascading.Select = Class.create(Sm.Cascading.Dependent, {
	initialize: function($super, options) {
		this.selector = $(options.id);
		this.selector.disable();
		this.selector.observe('change', this.valueChanged.bind(this));
		this.currentValue = options.initialValue;
		this.hasBeenSatisfied = false;
		this.currentParameters = [];
		this.jsonPath = options.jsonPath;
		this.jsonNameProperty = options.jsonNameProperty || 'name';
		this.jsonIdProperty = options.jsonIdProperty || 'id';
		this.autoSelect = options.autoSelect || false;
		this.lindrumAutoSelectTechnique = options.lindrumAutoSelectTechnique || false;
		this.includeBlank = options.includeBlank == undefined ? true : options.includeBlank;
		this.dependents = new Array();
		$super(options);
		// Just use parentChanged, don't use superclass callbacks
		this.reloadSelectorIfPossible();
	},

	parentChanged: function(parent) {
		if (this.hasBeenSatisfied && this.parametersChanged()) {
			// disable invalidated selector
			this.selector.disable();
			// select row 0
			this.selector.selectedIndex = 0;
			// empty invalidated selector
			// this.selector.length = 0;
			this.currentValue = null;
			this.notifyDependentsParentChanged();
		}
		this.reloadSelectorIfPossible();
	},

	addDependent: function(dependent) {
		this.dependents.push(dependent);
		if (this.getValue() != null) dependent.parentChanged(this);
	},

	valueChanged: function() {
		if (this.currentValue != this.selector.getValue()) {
			this.currentValue = this.selector.getValue();
			this.notifyDependentsParentChanged();
		}
	},

	getValue: function() {
		return this.currentValue;
	},

	element: function() {
		return this.selector;
	},

	notifyDependentsParentChanged: function() {
		this.dependents.each(function(dependent) { dependent.parentChanged(this); }.bind(this));
	},

	reloadSelectorIfPossible: function() {
		var parameters = this.getCurrentParameters();
		if (parameters.indexOf(null) < 0) {
			// Have enough data to make the call
			if (this.parametersChanged() || parameters.length == 0) {
				// And it's appropriate that we do so
				this.currentParameters = parameters;
				var actualJsonPath = this.jsonPath;
				for (i=0; i<parameters.length; i++) actualJsonPath = actualJsonPath.replace('$' + (i + 1), parameters[i]);
				this.populateAndSelect(actualJsonPath, this.jsonNameProperty, this.jsonIdProperty, this.getValue());
				this.hasBeenSatisfied = true;
			} else {
				// Doesn't require reload, but do re-enable in case we got disabled and remind dependents
				this.selector.enable();
				this.dependents.each(function(dependent) { dependent.parentLoaded(this); }.bind(this));
			}
		}
	},

	parametersChanged: function() {
		var newParameters = this.getCurrentParameters();
		var hasChanged = false;
		for (i=0; i<newParameters.length; i++)
			if (this.currentParameters[i] != newParameters[i]) hasChanged = true;
		return hasChanged;
	},

	getCurrentParameters: function() {
		var parameters = new Array();
		for (var i=0; i<this.parents.length; i++) parameters[i] = this.parents[i].getValue();
		return parameters;
	},

	populateAndSelect: function(jsonPath, nameProperty, valueProperty, selectedValue) {
		this.selector.disable();
		new Ajax.Request(jsonPath, { method:'get',
			onSuccess: function(transport) {
				var results = transport.responseText.evalJSON();
				for(var idx = this.selector.options.length - 1; idx >= 0; idx--) {
					this.selector.options[idx] = null;
				}
				if (this.includeBlank) this.selector.options[0] = new Option('', '', true);
				var numBlanks = this.selector.length;
				results.each(function(result) {
					for (var key in result) result = result[key];
					this.selector.options[this.selector.length] = new Option(result[nameProperty], result[valueProperty]);
					if (result[valueProperty] == selectedValue) this.selector.selectedIndex = this.selector.length - 1;
				}.bind(this));
				if (this.autoSelect && this.selector.length == (1 + numBlanks)) {
					this.selector.selectedIndex = (0 + numBlanks);
					this.selector.disable();
					this.valueChanged();
					if (this.lindrumAutoSelectTechnique) this.selector.selectedIndex = 0;
				} else {
					this.selector.enable();
				}
				this.dependents.each(function(dependent) { dependent.parentLoaded(this); }.bind(this));
			}.bind(this)
		});
	}
});
