
var LotIface = require('./lot-iface');
var Type = require('./type');
var Value = require('./value');

/**
 * A value holder, it emits the changed signal when value is changed.
 * Also it can be linked with a lot value or another parameter.
 */
module.exports = new Class({
	Properties: {
		/**
		 * The parameter value.
		 */
		value: {
			type: null
		},
		/**
		 * The parameter type.
		 */
		type: {
			type: Type
		},
		/**
		 * Another parameter to bind with.
		 */
		param: {
			type: Object
		},
		/**
		 * A lot to bind with.
		 */
		lot: {
			type: LotIface
		},
		/**
		 * The field name in the lot.
		 */
		name: {
			type: String
		},
		/**
		 * Determines whether the link to the lot is unidirectional, ie, a
		 * change in the lot updates the parameter but not viceversa.
		 */
		oneWay: {
			type: Boolean
		}
	}
	
	,_value: undefined
	,_type: null
	,_param: null
	,_lot: null
	,_sourceLock: false
	,_name: null
	,_oneWay: false
	
	,_setValue(newValue) {
		if (this._putValue(newValue))
			this._notifyChanges();
	}

	,_putValue(newValue) {
		if (Value.simpleEquals(newValue, this._value))
			return false;

		this._value = Value.simpleClone(newValue);
		return true;
	}

	,_notifyChanges() {
		this._refreshLot();
		this._refreshParam();
		this.emit('changed', this._value);
	}

	,_setType(type) {
		this._type = type;
		this._onLotChange();
	}

	,_onSourceChange(newValue) {
		if (this._oneTime && this._value !== undefined)
			return;

		this._sourceLock = true;
		this._setValue(newValue);
		this._sourceLock = false;
	}
	
	,_setParam(param) {
		this.link({_lot: null});
		this.link({_param: param}, {changed: this._onParamChange});
		this._refreshParam();
	}

	,_onParamChange() {
		if (this._sourceLock || !this._param) return;
		this._onSourceChange(this._param.value);
	}

	,_refreshParam() {
		if (this._sourceLock || !this._param || this._oneWay)
			return;

		this._sourceLock = true;
		this._param.value = this._value;
		this._sourceLock = false;
	}

	,_setLot(lot) {
		this.link({_param: null});
		this.link({_lot: lot}, {change: this._onLotChange});
		this._onLotChange();
	}

	,_onLotChange() {
		if (this._sourceLock || !this._lot || !this._name)
			return;

		var newValue = this._lot.get(this._name, this._type);
		this._onSourceChange(newValue);
	}

	,_refreshLot() {
		if (this._sourceLock || !this._name || !this._lot || this._oneWay)
			return;

		this._sourceLock = true;
		this._lot.set(this._name, this._value);
		this._sourceLock = false;
	}

	,_setName(name) {
		this._name = name;
		this._onLotChange();
	}
});
