"use strict";

exports.__esModule = true;
exports.default = exports.ReferenceShadow = exports.ReferenceLock = undefined;

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _akutils = require("akutils");

var _ObjectProperty2 = require("./ObjectProperty");

var _ObjectProperty3 = _interopRequireDefault(_ObjectProperty2);

var _PrimitiveProperty = require("./PrimitiveProperty");

var _PrimitiveProperty2 = _interopRequireDefault(_PrimitiveProperty);

var _Shadow2 = require("./Shadow");

var _Shadow3 = _interopRequireDefault(_Shadow2);

var _StateType = require("./StateType");

var _StateType2 = _interopRequireDefault(_StateType);

var _debug = require("./debug");

var _debug2 = _interopRequireDefault(_debug);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var debug = (0, _debug2.default)(_debug.ReferenceKey);

var _desc = Symbol('desc');
var _lockId = Symbol('lockId');
var _locks = Symbol('locks');
var _path = 'path';
var _refProp = Symbol('ref');
var _store = Symbol('store');

var nextLockId = 1;

var ReferenceLock = exports.ReferenceLock = function () {
	function ReferenceLock(id, desc, refProp) {
		_classCallCheck(this, ReferenceLock);

		this[_lockId] = id;
		this[_desc] = desc;
		this[_refProp] = refProp;
	}

	_createClass(ReferenceLock, [{
		key: "desc",
		value: function desc() {
			return this[_desc];
		}
	}, {
		key: "id",
		value: function id() {
			return this[_lockId];
		}
	}, {
		key: "is",
		value: function is(desc) {
			return this[_desc] === desc;
		}
	}, {
		key: "release",
		value: function release() {
			this[_refProp]._removeLock(this);
		}
	}]);

	return ReferenceLock;
}();

var ReferenceShadow = exports.ReferenceShadow = function (_Shadow) {
	_inherits(ReferenceShadow, _Shadow);

	function ReferenceShadow() {
		_classCallCheck(this, ReferenceShadow);

		return _possibleConstructorReturn(this, Object.getPrototypeOf(ReferenceShadow).apply(this, arguments));
	}

	_createClass(ReferenceShadow, [{
		key: "data",
		value: function data() {
			return this.$$().data();
		}
	}, {
		key: "isLocked",
		value: function isLocked() {
			return this.$$().isLocked();
		}
	}, {
		key: "lock",
		value: function lock(desc) {
			return this.$$().lock(desc);
		}
	}, {
		key: "referenceTo",
		value: function referenceTo(shadow) {
			this.$$().referenceTo(shadow);
		}
	}]);

	return ReferenceShadow;
}(_Shadow3.default);

var ReferenceProperty = function (_ObjectProperty) {
	_inherits(ReferenceProperty, _ObjectProperty);

	function ReferenceProperty() {
		_classCallCheck(this, ReferenceProperty);

		var _this2 = _possibleConstructorReturn(this, Object.getPrototypeOf(ReferenceProperty).call(this));

		_this2[_locks] = [];
		return _this2;
	}

	_createClass(ReferenceProperty, [{
		key: "propertyWillUnshadow",
		value: function propertyWillUnshadow() {
			if (this.isLocked()) {
				this.store().removeListener(this);
			}

			delete this[_locks];
		}
	}, {
		key: "data",
		value: function data() {
			var path = this.path();

			if (!path) {
				return null;
			}

			return this.ref || this.store().findByPath(path);
		}
	}, {
		key: "isLocked",
		value: function isLocked() {
			return this[_locks] && this[_locks].length > 0;
		}
	}, {
		key: "lock",
		value: function lock() {
			var desc = arguments.length <= 0 || arguments[0] === undefined ? "lock:" + nextLockId : arguments[0];

			return this._addLock(new ReferenceLock(nextLockId++, desc, this));
		}

		/**
  	Store listener callback handler.
  */

	}, {
		key: "onPostUpdate",
		value: function onPostUpdate() {
			var path = this.path();
			var curr = path ? this.store().findByPath(path) : null;

			if (curr !== this.ref) {
				this.ref = curr;
				this.touch();
			}
		}
	}, {
		key: "path",
		value: function path() {
			return this._keyed.get(_path);
		}
	}, {
		key: "referenceTo",
		value: function referenceTo(shadow) {
			if (this.isLocked()) {
				console.warn("Reference is locked and changing path");
			}

			this._keyed.set(_path, shadow ? shadow.$().path() : null);
		}
	}, {
		key: "_addLock",
		value: function _addLock(lock) {
			var _this3 = this;

			(0, _akutils.assert)(function (a) {
				return a.is(lock[_refProp] === _this3, "Lock reference object mismatch");
			});

			if (!this[_locks]) {
				return null;
			}

			this[_locks].push(lock);
			debug(function (d) {
				return d("_addLock(): path=" + _this3.path() + ", desc=" + lock.desc());
			});

			if (this[_locks].length === 1) {
				var path = this.path();

				this.store().addListener(this);
				this.ref = path ? this.store().findByPath(this.path()) : null;
			}

			return lock;
		}
	}, {
		key: "_removeLock",
		value: function _removeLock(lock) {
			var _this4 = this;

			if (!this[_locks]) {
				return;
			}

			this[_locks] = this[_locks].filter(function (l) {
				return l !== lock;
			});
			debug(function (d) {
				return d("_removeLock(): id=" + _this4.path() + ", locked=" + _this4.isLocked() + ", desc=" + lock.desc());
			});

			if (this[_locks].length === 0) {
				this.store().removeListener(this);
				this.ref = null;
			}
		}
	}]);

	return ReferenceProperty;
}(_ObjectProperty3.default);

exports.default = ReferenceProperty;


_StateType2.default.defineType(ReferenceProperty, function (spec) {
	spec.initialState({}).properties(_defineProperty({}, _path, _PrimitiveProperty2.default.type.initialState(null))).shadowClass(ReferenceShadow).typeName("ReferenceProperty");
});