"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.eject = eject;
exports.inject = inject;
exports.meta = meta;
exports.state = state;
exports.type = type;

var _meta3 = require("./meta.js");

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

var _model = require("./model.js");

var _factory = require("./factory.js");

var _ty = require("./ty/ty.js");

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

var createDecorator = function createDecorator(name, fn, force) {
  return function (protos, key, descriptor) {
    (0, _utils.onlySupportLegacy)(protos); // only works for Model

    if (force && !(protos instanceof _model.Model)) {
      throw new Error("TySheMo: @".concat(name, " only works on class which extends Model."));
    }

    if (!key) {
      throw new Error("TySheMo: @".concat(name, " only works on a class property."));
    }
    /**
     * should must decorate properties which has no value
     * class Some extends Model {
     *    @state({ value }) a; // without initializer
     * }
     */


    if (descriptor) {
      var oop = function oop() {
        throw new Error("TySheMo: @".concat(name, " only works on a class property which has no initializer."));
      };

      if ('initializer' in descriptor) {
        if (descriptor.initializer) {
          oop();
        }
      } else {
        oop();
      }
    } // if without descriptor -> in typescript


    if (fn) {
      fn(protos, key);
    } // as previous determine, only babel legacy will work here
    // this make this non-value-given property use the value from super class

    /**
     * class A {
     *   id = 1;
     * }
     *
     * class B {
     *   id;
     * }
     *
     * =>
     *
     * class B {
     *   constructor() {
     *     super()
     *     // without id assigned void
     *   }
     * }
     */


    if (descriptor) {
      return {
        writable: true,
        configurable: true
      };
    }
  };
};

var define = function define(target, key, value) {
  Object.defineProperty(target, key, {
    value: value,
    writable: true,
    enumerable: true,
    configurable: true
  });
};

function meta(entry, options, methods) {
  return createDecorator('meta', function (protos, key) {
    var CurrentModel = protos.constructor;

    if ((0, _tsFns.isInheritedOf)(entry, _meta3.Meta) || (0, _tsFns.isInstanceOf)(entry, _meta3.Meta)) {
      define(CurrentModel, key, entry);
    } else if ((0, _tsFns.isInheritedOf)(entry, _model.Model) || (0, _tsFns.isArray)(entry) && !entry.some(function (item) {
      return !(0, _tsFns.isInheritedOf)(item, _model.Model);
    })) {
      var _meta = _factory.Factory.createMeta(entry, options, methods);

      define(CurrentModel, key, _meta);
    } else {
      if ((0, _tsFns.isObject)(entry)) {
        var _meta2 = new _meta3.Meta(entry);

        define(CurrentModel, key, _meta2);
      }
    }
  }, true);
}

function state(options) {
  if (options && ((0, _tsFns.isInstanceOf)(options, _meta3.StateMeta) || (0, _tsFns.isInheritedOf)(options, _meta3.StateMeta))) {
    return meta(options);
  }

  if (options && ((0, _tsFns.isInstanceOf)(options, _meta3.SceneStateMeta) || (0, _tsFns.isInheritedOf)(options, _meta3.SceneStateMeta))) {
    return meta(options);
  }

  return createDecorator('state', function (protos, key) {
    if (!('value' in options) && !options.get && !options.set) {
      throw new Error("TySheMo: @state params should not contain either value or get/set.");
    }

    if ('value' in options && (options.get || options.set)) {
      throw new Error("TySheMo: @state params should not contain value and get/set together.");
    }

    var CurrentModel = protos.constructor;
    var state = new _model.State(options);
    define(CurrentModel, key, state);
  }, true);
}

function type() {
  var _Ty$decorate;

  return (_Ty$decorate = _ty.Ty.decorate).with.apply(_Ty$decorate, arguments);
}
/**
 * Move the given property to the Class static property
 * @param {*} source
 * @returns
 */


function inject(source) {
  return createDecorator('inject', function (protos, key) {
    var CurrentModel = protos.constructor;
    define(CurrentModel, key, source);
  });
}
/**
 * invalidate properties which have no initializer
 * @returns
 * @example
 * class A {
 *   @eject()
 *   some: string;
 * }
 * @param {boolean} [force] whether to override model field to null
 */


function eject(force) {
  return createDecorator('eject', function (protos, key) {
    if (force) {
      var CurrentModel = protos.constructor;
      define(CurrentModel, key, null);
    }
  }, force);
}