"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.StateMeta = exports.SceneStateMeta = exports.SceneMeta = exports.Meta = exports.AsyncMeta = void 0;

var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));

var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));

var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));

var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));

var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));

var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));

var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));

var _tsFns = require("ts-fns");

var _validator = require("./validator.js");

var _utils = require("./shared/utils.js");

var _configs = require("./shared/configs.js");

var _constants = require("./shared/constants.js");

var _excluded = ["value", "default"],
    _excluded2 = ["value"];

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }

var AsyncMetaSymbol = Symbol();
var SceneMetaSymbol = Symbol();

var createValidator = function createValidator(v) {
  return (0, _tsFns.isInstanceOf)(v, _validator.Validator) ? v : (0, _tsFns.isInheritedOf)(v, _validator.Validator) ? new v() : v && (0, _typeof2.default)(v) === 'object' && !(0, _tsFns.isEmpty)(v) ? new _validator.Validator(v) : null;
};

var createValidators = function createValidators(items) {
  if ((0, _tsFns.isArray)(items)) {
    return items.map(createValidator).filter(Boolean);
  }

  if ((0, _tsFns.isObject)(items)) {
    var validators = [];
    var keys = Object.keys(items);
    keys.forEach(function (key) {
      var item = items[key];
      var validator = createValidator(item);

      if (validator) {
        var index = validators.length;
        validators[key] = index; // patch key on the array

        validators.push(validator);
      }
    });
    return validators;
  }
};

var mergeValidators = function mergeValidators(meta, newValdiators) {
  var originValidators = (0, _tsFns.isObject)(meta.validators) ? createValidators(meta.validators) : meta.validators || []; // concat array

  if ((0, _tsFns.isArray)(newValdiators)) {
    return [].concat((0, _toConsumableArray2.default)(originValidators), (0, _toConsumableArray2.default)(newValdiators.map(createValidator).filter(Boolean)));
  } // merge mapping


  if ((0, _tsFns.isObject)(newValdiators)) {
    var validators = [];
    Object.assign(validators, originValidators); // originValidators may have keys more than indexes

    var keys = Object.keys(newValdiators);
    keys.forEach(function (key) {
      var item = newValdiators[key];
      var validator = createValidator(item);
      var keyIndex = originValidators[key]; // add or replace validator

      if (validator) {
        // replace
        if (!(0, _tsFns.isUndefined)(keyIndex)) {
          validators[keyIndex] = validator;
        } // add
        else {
          var index = validators.length;
          validators[key] = index; // patch key on the array

          validators.push(validator);
        }
      } // remove the previous validator
      else {
        if (!(0, _tsFns.isUndefined)(keyIndex)) {
          validators[keyIndex] = null;
        }
      }
    });
    return validators.filter(Boolean);
  } // no change


  return originValidators;
};

function useAttr(meta, key, descriptor) {
  if (key === 'validators') {
    var value = descriptor.value;
    meta.validators = createValidators(value);
    return;
  }

  (0, _tsFns.define)(meta, key, descriptor);
}

var ensureAttrs = function ensureAttrs(attrs) {
  if (!attrs || (0, _typeof2.default)(attrs) !== 'object') {
    return {};
  }

  return (0, _tsFns.filter)(attrs, function (value) {
    if ((0, _tsFns.isUndefined)(value)) {
      return false;
    }

    return true;
  });
};

var notifyAttrs = function notifyAttrs(notifiers, attrs, message) {
  var viewGetters = {};
  var viewAttrs = {};
  (0, _tsFns.each)(attrs, function (value, key) {
    if ((0, _tsFns.isUndefined)(value)) {
      return;
    }

    if ((0, _tsFns.inObject)(key, _configs.RESERVED_ATTRIBUTES)) {
      return;
    }

    if ((0, _tsFns.isFunction)(value)) {
      viewGetters[key] = value;
    } else {
      viewAttrs[key] = value;
    }
  });
  notifiers.forEach(function (_ref) {
    var model = _ref.model,
        key = _ref.key;

    if (!model.$inited) {
      return;
    }

    model.use(key, function (view) {
      model.$store.runSilent(function () {
        Object.assign(view, viewAttrs);
      });
      (0, _tsFns.each)(viewGetters, function (getter, attr) {
        (0, _tsFns.define)(view, attr, {
          get: function get() {
            var data = model.getData(key);
            return getter.call(model, data, key);
          },
          enumerable: true,
          configurable: true
        });
      });
      model.$store.forceDispatch("!".concat(key), message);
    });
  });
};

var Meta = /*#__PURE__*/function () {
  function Meta() {
    var attrs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    (0, _classCallCheck2.default)(this, Meta);
    var descriptors = {}; // from inherit chain

    var properties = (0, _utils.ofChain)(this, Meta, ['Scene']);
    (0, _tsFns.each)(properties, function (descriptor, key) {
      if ((0, _tsFns.inObject)(key, attrs, true)) {
        return;
      }

      descriptors[key] = properties[key];
    }, true); // from prototype

    var Constructor = (0, _tsFns.getConstructorOf)(this);
    var prototype = Constructor.prototype;
    (0, _tsFns.each)(prototype, function (descriptor, key) {
      if (key[0] === '_') {
        return;
      }

      if (['constructor', 'extend', 'fetchAsyncAttrs', 'defineScenes', 'switchScene', 'Scene'].includes(key)) {
        return;
      }

      if ((0, _tsFns.inObject)(key, attrs, true)) {
        return;
      }

      if ((0, _tsFns.inObject)(key, descriptors)) {
        return;
      }

      descriptors[key] = prototype[key];
    }, true);

    this.__init(descriptors, attrs);
  }

  (0, _createClass2.default)(Meta, [{
    key: "__init",
    value: function __init(descriptors, attrs) {
      this.__useAttrs(descriptors, attrs);
    }
  }, {
    key: "__useAttrs",
    value: function __useAttrs() {
      var _this = this;

      for (var _len = arguments.length, attrsets = new Array(_len), _key = 0; _key < _len; _key++) {
        attrsets[_key] = arguments[_key];
      }

      var attrs = attrsets.length > 1 ? attrsets.reduce(function (attrs, item) {
        Object.assign(attrs, item);
        return attrs;
      }, {}) : attrsets[0];
      (0, _tsFns.each)(attrs, function (descriptor, key) {
        useAttr(_this, key, descriptor);
      }, true);
    }
  }, {
    key: "__restoreAttrs",
    value: function __restoreAttrs(allAttrs) {
      var _this2 = this;

      var attrs = _objectSpread({}, allAttrs);

      if (this[_constants.ComputeSymbol] && attrs.compute) {
        this[_constants.ComputeSymbol] = attrs.compute;
        delete attrs.compute;
      }

      this.__useAttrs(attrs);

      (0, _tsFns.each)(this, function (_, key) {
        if (!(key in attrs)) {
          delete _this2[key];
        }
      });
    }
  }, {
    key: "extend",
    value: function extend() {
      var attrs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var Constructor = (0, _tsFns.getConstructorOf)(this);

      var NewMeta = /*#__PURE__*/function (_Constructor) {
        (0, _inherits2.default)(NewMeta, _Constructor);

        var _super = _createSuper(NewMeta);

        function NewMeta() {
          (0, _classCallCheck2.default)(this, NewMeta);
          return _super.apply(this, arguments);
        }

        return (0, _createClass2.default)(NewMeta);
      }(Constructor);

      var defaultAttrs = _objectSpread({}, this);

      if (this[AsyncMetaSymbol]) {
        defaultAttrs = this[AsyncMetaSymbol].default;
      } else if (this[SceneMetaSymbol]) {
        defaultAttrs = this[SceneMetaSymbol].default;
      }

      var attrset = _objectSpread(_objectSpread({}, defaultAttrs), attrs);

      if (attrs.validators) {
        attrset.validators = mergeValidators(this, attrs.validators);
      } // pass attrs so that it will be used as passed attrs in scene meta


      var meta = new NewMeta(attrset);
      Object.setPrototypeOf(meta, this); // make it impossible to use meta

      return meta;
    }
  }], [{
    key: "extend",
    value: function extend(attrs) {
      if (attrs !== null && attrs !== void 0 && attrs.validators) {
        var validators = mergeValidators(this, attrs && attrs.validators);

        var _Constructor2 = (0, _tsFns.inherit)(this, null, _objectSpread(_objectSpread({}, attrs), {}, {
          validators: validators
        }));

        return _Constructor2;
      }

      var Constructor = (0, _tsFns.inherit)(this, null, attrs);
      return Constructor;
    }
  }]);
  return Meta;
}();

exports.Meta = Meta;

var AsyncMeta = /*#__PURE__*/function (_Meta) {
  (0, _inherits2.default)(AsyncMeta, _Meta);

  var _super2 = _createSuper(AsyncMeta);

  function AsyncMeta() {
    (0, _classCallCheck2.default)(this, AsyncMeta);
    return _super2.apply(this, arguments);
  }

  (0, _createClass2.default)(AsyncMeta, [{
    key: "__init",
    value: function __init(descriptors, attrs) {
      (0, _get2.default)((0, _getPrototypeOf2.default)(AsyncMeta.prototype), "__init", this).call(this, descriptors, attrs);
      this[AsyncMetaSymbol] = {
        descriptors: descriptors,
        default: attrs,
        notifiers: [],
        // -1 not begin fetching
        // 0 fetching
        // 1 fetched
        status: -1
      };
    }
  }, {
    key: "fetchAsyncAttrs",
    value: function fetchAsyncAttrs() {
      return Promise.resolve({});
    }
  }, {
    key: "_awaitMeta",
    value: function _awaitMeta(model, key) {
      var _this3 = this;

      var _this$AsyncMetaSymbol = this[AsyncMetaSymbol],
          descriptors = _this$AsyncMetaSymbol.descriptors,
          attrs = _this$AsyncMetaSymbol.default,
          notifiers = _this$AsyncMetaSymbol.notifiers,
          status = _this$AsyncMetaSymbol.status;

      if (status === 1) {
        return;
      }

      if (status === 0) {
        notifiers.push({
          model: model,
          key: key
        });
        return;
      }

      this[AsyncMetaSymbol].status = 0;
      this.fetchAsyncAttrs().then(function (data) {
        var next = ensureAttrs(_objectSpread(_objectSpread(_objectSpread({}, descriptors), attrs), data));

        _this3.__restoreAttrs(next);

        notifyAttrs(notifiers, next, 'async meta');
        notifiers.length = 0;
        _this3[AsyncMetaSymbol].status = 1;
      });
    }
  }]);
  return AsyncMeta;
}(Meta);

exports.AsyncMeta = AsyncMeta;

var SceneMeta = /*#__PURE__*/function (_Meta2) {
  (0, _inherits2.default)(SceneMeta, _Meta2);

  var _super3 = _createSuper(SceneMeta);

  function SceneMeta() {
    (0, _classCallCheck2.default)(this, SceneMeta);
    return _super3.apply(this, arguments);
  }

  (0, _createClass2.default)(SceneMeta, [{
    key: "__init",
    value: function __init(descriptors, attrs) {
      (0, _get2.default)((0, _getPrototypeOf2.default)(SceneMeta.prototype), "__init", this).call(this, descriptors, attrs);
      this[SceneMetaSymbol] = {
        descriptors: descriptors,
        default: attrs,
        notifiers: [],
        codes: []
      };

      this._initSceneCode();
    }
  }, {
    key: "_ensureAttrs",
    value: function _ensureAttrs(attrs) {
      return ensureAttrs(attrs);
    }
  }, {
    key: "defineScenes",
    value: function defineScenes() {
      return {};
    }
  }, {
    key: "switchScene",
    value: function switchScene(sceneCode) {
      var _this4 = this;

      var _this$SceneMetaSymbol = this[SceneMetaSymbol],
          descriptors = _this$SceneMetaSymbol.descriptors,
          defaultAttrs = _this$SceneMetaSymbol.default,
          notifiers = _this$SceneMetaSymbol.notifiers;
      var sceneCodes = (0, _tsFns.isArray)(sceneCode) ? sceneCode : [sceneCode];
      var scenes = this.defineScenes();

      var use = function use(sceneCode) {
        var scene = scenes[sceneCode];

        if (scene && typeof scene === 'function') {
          var res = scene();
          return res;
        } else if (scene) {
          return scene;
        } // do nothing if not exist
        // else {
        //   return Promise.reject(new Error(`[TySheMo]: Scene ${sceneCode} is not defined on Meta ${JSON.stringify(this)}`))
        // }

      };

      var restore = function restore(scenes) {
        var attrs = {};
        scenes.forEach(function (scene) {
          Object.assign(attrs, _this4._ensureAttrs(scene));
        });

        var next = _this4._ensureAttrs(_objectSpread(_objectSpread(_objectSpread({}, descriptors), defaultAttrs), attrs));

        _this4.__restoreAttrs(next);

        return next;
      };

      var finish = function finish() {
        notifiers.length = 0;
      };

      var results = [];
      sceneCodes.forEach(function (code) {
        var scene = use(code);

        if (!scene) {
          return;
        }

        results.push(scene);
      });
      this[SceneMetaSymbol].codes = sceneCodes; // set new attributes

      if (results.some(function (item) {
        return item instanceof Promise;
      })) {
        // restore some scenes before async
        var preloadScenes = results.filter(function (item) {
          return !(item instanceof Promise);
        });

        if (preloadScenes.length) {
          var preloadAttrs = restore(preloadScenes);
          notifyAttrs(notifiers, preloadAttrs, 'scene meta'); // dont finish it
        }

        return Promise.all(results.map(function (item) {
          return item instanceof Promise ? item : Promise.resolve(item);
        })).then(function (scenes) {
          return restore(scenes);
        }).then(function (attrs) {
          notifyAttrs(notifiers, attrs, 'scene meta');
        }).finally(function () {
          finish();
        });
      } else if (results.length) {
        var attrs = restore(results);
        notifyAttrs(notifiers, attrs, 'scene meta');
        finish();
      } else {
        finish();
      }
    }
  }, {
    key: "_awaitMeta",
    value: function _awaitMeta(model, key) {
      var notifiers = this[SceneMetaSymbol].notifiers;
      notifiers.push({
        model: model,
        key: key
      });
    }
  }, {
    key: "_initSceneCode",
    value: function _initSceneCode() {}
  }]);
  return SceneMeta;
}(Meta);
/**
 * special meta which treated as state, the following attributes not working: default, drop, to, map, state
 * should must pass `value`
 */


exports.SceneMeta = SceneMeta;

var StateMeta = /*#__PURE__*/function (_Meta3) {
  (0, _inherits2.default)(StateMeta, _Meta3);

  var _super4 = _createSuper(StateMeta);

  function StateMeta() {
    (0, _classCallCheck2.default)(this, StateMeta);
    return _super4.apply(this, arguments);
  }

  (0, _createClass2.default)(StateMeta, [{
    key: "__init",
    value: function __init(descriptors, attrs) {
      var value = attrs.value,
          _attrs$default = attrs.default,
          defaultValue = _attrs$default === void 0 ? value : _attrs$default,
          others = (0, _objectWithoutProperties2.default)(attrs, _excluded);
      delete others.drop;
      delete others.state;

      var desc = _objectSpread({}, descriptors);

      delete desc.drop;
      delete desc.state;
      desc.default = (0, _tsFns.isUndefined)(defaultValue) ? descriptors.value : defaultValue;
      (0, _get2.default)((0, _getPrototypeOf2.default)(StateMeta.prototype), "__init", this).call(this, desc, others); // force make drop true, can not be changed

      (0, _tsFns.define)(this, 'drop', {
        value: true,
        writable: false,
        enumerable: true,
        configurable: false
      });
    }
  }]);
  return StateMeta;
}(Meta);

exports.StateMeta = StateMeta;

var SceneStateMeta = /*#__PURE__*/function (_SceneMeta) {
  (0, _inherits2.default)(SceneStateMeta, _SceneMeta);

  var _super5 = _createSuper(SceneStateMeta);

  function SceneStateMeta() {
    (0, _classCallCheck2.default)(this, SceneStateMeta);
    return _super5.apply(this, arguments);
  }

  (0, _createClass2.default)(SceneStateMeta, [{
    key: "__init",
    value: function __init(descriptors, attrs) {
      (0, _get2.default)((0, _getPrototypeOf2.default)(SceneStateMeta.prototype), "__init", this).call(this, descriptors, attrs);

      StateMeta.prototype.__init.call(this, descriptors, attrs);
    }
  }, {
    key: "_ensureAttrs",
    value: function _ensureAttrs(attrs) {
      var value = attrs.value,
          others = (0, _objectWithoutProperties2.default)(attrs, _excluded2);
      delete others.drop;
      delete others.state;
      delete others.default;

      if (!(0, _tsFns.isUndefined)(value)) {
        others.default = value;
      }

      return (0, _get2.default)((0, _getPrototypeOf2.default)(SceneStateMeta.prototype), "_ensureAttrs", this).call(this, others);
    }
  }]);
  return SceneStateMeta;
}(SceneMeta);

exports.SceneStateMeta = SceneStateMeta;