"use strict";

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

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

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 _lodash = require("lodash.has");

var _lodash2 = _interopRequireDefault(_lodash);

var _es6Symbol = require("es6-symbol");

var _es6Symbol2 = _interopRequireDefault(_es6Symbol);

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

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

var _impl = (0, _es6Symbol2.default)('impl');

/**
	The base class for `Shadow.$()` method used for obtaining f.lux contextual information
	concerning a non-primitive shadow state property.

	The most commonly used methods include:

	<ul>
		<li>{@link Access#dotPath}</li>
		<li>{@link Access#property}</li>
		<li>{@link Access#pid}</li>
		<li>{@link Access#state}</li>
		<li>{@link Access#store}</li>
		<li>{@link Access#waitFor}</li>
	</ul>

	This class provides access to the experimental {@link Property} checkpointing api. Checkpointing
	allows the state to be recorded at a point in time and then later reset to that point. This is
	handy when a form may accept changes and then allow the user to cancel the edit session.

	@see {@link Shadow}
*/

var Access = function () {
	function Access(impl) {
		_classCallCheck(this, Access);

		this[_impl] = impl;
	}

	/**
 	Get the backing {@link ShadowImpl} instance for this shadow property.
 		@return {ShadowImpl}
 */


	_createClass(Access, [{
		key: "__",
		value: function __() {
			return this[_impl];
		}

		/**
  	Copies the current actual state for later reset using {@link Property#resetToCheckpoint}. An existing
  	checkpoint will take precedence over subsequent calls.
  */

	}, {
		key: "checkpoint",
		value: function checkpoint() {
			this.property().checkpoint();
		}

		/**
  	Clears an existing checkpoint created using {@link Property#checkpoint}.
  */

	}, {
		key: "clearCheckpoint",
		value: function clearCheckpoint() {
			this.property().clearCheckpoint();
		}

		/**
  	Gets the context from the parent properties. Context is setup for a property using the
  	{@link StateType#context} function.
  */

	}, {
		key: "context",
		value: function context() {
			var ancestors = [];
			var p = this.property();

			while (p = p.parent()) {
				ancestors.unshift(p);
			}

			return ancestors.reduce(function (ctxt, p) {
				return p.constructor.type._context ? _extends({}, ctxt, p.constructor.type._context(p)) : ctxt;
			}, {});
		}

		/**
  	Gets the path from root property using a dot (`.`) separator. Suitable for using with the lodash `result()`
  	function.
  		@return {string} path with each component joined by a `.`
  		@see https://lodash.com/docs/4.17.4#result
  */

	}, {
		key: "dotPath",
		value: function dotPath() {
			return this[_impl].dotPath();
		}

		/**
  	Gets the checkpoint state previously recorded using {@link Property#checkpoint}.
  		@return {Object|Array} the checkpoint data if checkpoint is set.
  */

	}, {
		key: "getCheckpoint",
		value: function getCheckpoint() {
			return this.property().getCheckpoint();
		}

		/**
  	Gets if an existing checkpoint has be created using {@link Property#checkpoint}.
  		@return {boolean} true if a checkpoint has been recorded.
  */

	}, {
		key: "hasCheckpoint",
		value: function hasCheckpoint() {
			return this.property().hasCheckpoint();
		}

		/**
  	Gets if the shadow state property is proxying the current actual state contained in the f.lux store.
  		@return {boolean} `true` if actively proxying the state.
  */

	}, {
		key: "isActive",
		value: function isActive() {
			return this[_impl].isActive();
		}

		/**
  	Gets if the property allows for assignment through the shadow state, ie `todo.desc = "go skiing"`. The
  	readonly attribute is hierarchically determined through the parent property if not explicitly set.
  		@return {boolean} - true if assignment is not allowed
  */

	}, {
		key: "isReadonly",
		value: function isReadonly() {
			return this.property().isReadonly();
		}

		/**
  	Gets if the shadow property has experienced a mutation action. This method will return `true` and
  	{@link Access#isActive} may also return true if the f.lux store has not yet been transitioned to
  	the next state.
  		@return {boolean} `true` if the state has not been changed.
  */

	}, {
		key: "isValid",
		value: function isValid() {
			return this[_impl].isValid();
		}

		/**
  	Gets the current shadow state for this property. The shadow state referenced by this access object
  	could be stale if a javascript closure has a previous reference and then performed an asynchronous
  	operation like a network request.
  		@return {Shadow}
  */

	}, {
		key: "latest",
		value: function latest() {
			var impl = this[_impl];

			return impl && impl.latest();
		}

		/** @ignore */

	}, {
		key: "merge",
		value: function merge(data) {
			this[_impl].merge(data);
		}

		/**
  	Gets the next actual state for this property following any pending actions.
  		@return {Object|Array}
  */

	}, {
		key: "nextState",
		value: function nextState() {
			return this[_impl].nextState();
		}

		/**
  	Gets the {@link Property#name} components from the root property to this property.
  		@return {[]} array where each name component is either a `string` or `number` depending on the
  		each parent component's type.
  */

	}, {
		key: "path",
		value: function path() {
			return this[_impl].path();
		}

		/**
  	Gets the unique f.lux ID for this property.
  		@return {number} the id
  */

	}, {
		key: "pid",
		value: function pid() {
			return this.property().pid();
		}

		/**
  	Gets the {@link Property} managing this shadow property.
  		@return {Property}
  */

	}, {
		key: "property",
		value: function property() {
			return this[_impl].property();
		}

		/**
  	Replaces the current property state with a checkpoint state previously recorded using
  	{@link Property#checkpoint}. The checkpoint is cleared.
  */

	}, {
		key: "resetToCheckpoint",
		value: function resetToCheckpoint() {
			this.property().resetToCheckpoint();
		}

		/**
  	Gets the top-level shadow from the {@link Store} containing this shadow state property.
  		@return {Shadow}
  */

	}, {
		key: "rootShadow",
		value: function rootShadow() {
			return this.property().rootShadow();
		}

		/**
  	Gets the path from root property using a slash (`/`) separator.
  		@return {string} path with each component separated by a `/`
  */

	}, {
		key: "slashPath",
		value: function slashPath() {
			return this[_impl].slashPath();
		}

		/**
  		@return
  */

	}, {
		key: "shadow",
		value: function shadow() {
			return this[_impl].shadow();
		}

		/**
  	Gets the actual state being shadowed.
  		@return {Object|Array}
  */

	}, {
		key: "state",
		value: function state() {
			return this[_impl].state();
		}

		/**
  	Gets the {@Link Store} containing the application state.
  		@return {Store}
  */

	}, {
		key: "store",
		value: function store() {
			return this.property().store();
		}

		/**
  	Performs an update on the state values.
  		The callback should be pure and have the form:
  		```
  		callback(nextState) : nextState
  	```
  	- or -
  		```
  		callback(nextState) : { name, nextState }
  	```
  		The `nextState` parameter is a javascript object (not a `Shadow`) that represents the next state starting
  	from the current state and having all the actions in the current tick applied to it before this `update()`
  	call.
  		The callback should return an object with the following properties:
  	<ul>
  		<li> `name` - a stort moniker identifying the update call purpose. This will passed to the store middleware.
  			This value is optional with the default value being '[path].$().update()'.</li>
  		<li>`nextState` - the value for the next state after the update functionality.</li>
  	</ul>
  */

	}, {
		key: "update",
		value: function update(callback) {
			var _this = this;

			this[_impl].update(function (next) {
				var result = callback(next);

				result = (0, _lodash2.default)(result, "nextState") ? result : { nextState: result };

				// mark as a replacement (expensive but conservative) since very unlikely a caller through the access
				// variable will have made all the book keeping updates and no way of knowing how deep their changes
				// were in the object hierarchy.
				return {
					name: result.name || _this.slashPath() + ".$().update()",
					nextState: result.nextState,
					replace: true
				};
			});
		}

		/**
  	Registers a **one-time** no argument callback to be invoked after the next {@link Store} state change.
  		@param {function(shadow: Shadow)} callback - a callback to be invoked after all pending changes have been
  		reflected in the shadow state.
  */

	}, {
		key: "waitFor",
		value: function waitFor(callback) {
			var _this2 = this;

			this.store().waitFor(function () {
				return callback && callback(_this2.latest());
			});
		}
	}, {
		key: "wait",
		value: function wait() {
			var _this3 = this;

			return this.store().wait().then(function () {
				return _this3.latest();
			});
		}
	}]);

	return Access;
}();

exports.default = Access;