(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "../core/TransientDisposable", "../core/types", "./Inject", "./ServiceCollection", "./TypeKey"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.ServiceProvider = void 0;
    var TransientDisposable_1 = require("../core/TransientDisposable");
    var types_1 = require("../core/types");
    var Inject_1 = require("./Inject");
    var ServiceCollection_1 = require("./ServiceCollection");
    var TypeKey_1 = require("./TypeKey");
    var ServiceProvider = /** @class */ (function () {
        function ServiceProvider(parent) {
            this.parent = parent;
            this.instances = {};
            if (parent === null) {
                ServiceCollection_1.ServiceCollection.instance.registerScoped(ServiceProvider);
            }
            var sd = ServiceCollection_1.ServiceCollection.instance.get(ServiceProvider);
            this.instances[sd.id] = this;
        }
        Object.defineProperty(ServiceProvider.prototype, "global", {
            get: function () {
                return this.parent === null ? this : this.parent.global;
            },
            enumerable: false,
            configurable: true
        });
        ServiceProvider.prototype.get = function (key) {
            return this.resolve(key, true);
        };
        ServiceProvider.prototype.put = function (key, value) {
            var sd = ServiceCollection_1.ServiceCollection.instance.get(key);
            if (!sd) {
                sd = ServiceCollection_1.ServiceCollection.instance.register(key, function () { return value; }, ServiceCollection_1.Scope.Global);
            }
            this.instances[sd.id] = value;
        };
        ServiceProvider.prototype.resolve = function (key, create, defValue) {
            if (create === void 0) { create = false; }
            var sd = ServiceCollection_1.ServiceCollection.instance.get(key);
            if (!sd) {
                if (!create) {
                    if (defValue !== undefined) {
                        return defValue;
                    }
                    throw new Error("No service registered for type " + key);
                }
                return this.create(key);
            }
            if (sd.type === ServiceProvider) {
                return this;
            }
            if (sd.scope === ServiceCollection_1.Scope.Global) {
                return this.global.getValue(sd);
            }
            if (sd.scope === ServiceCollection_1.Scope.Scoped) {
                if (this.parent === null) {
                    throw new Error("Scoped dependency cannot be created on global");
                }
            }
            return this.getValue(sd);
        };
        ServiceProvider.prototype.getValue = function (sd) {
            if (sd.scope === ServiceCollection_1.Scope.Transient) {
                return sd.factory(this);
            }
            var v = this.instances[sd.id];
            if (!v) {
                this.instances[sd.id] = v = sd.factory(this);
            }
            return v;
        };
        ServiceProvider.prototype.newScope = function () {
            return new ServiceProvider(this);
        };
        ServiceProvider.prototype.dispose = function () {
            for (var key in this.instances) {
                if (this.instances.hasOwnProperty(key)) {
                    var element = this.instances[key];
                    if (element === this) {
                        continue;
                    }
                    var d = element;
                    if (d.dispose) {
                        d.dispose();
                    }
                }
            }
        };
        ServiceProvider.prototype.create = function (key) {
            var _this = this;
            var originalKey = key;
            var originalTypeKey = TypeKey_1.TypeKey.get(originalKey);
            if (types_1.DI.resolveType) {
                var mappedType = ServiceProvider.mappedTypes[originalTypeKey] || (ServiceProvider.mappedTypes[originalTypeKey] = types_1.DI.resolveType(originalKey));
                key = mappedType;
            }
            var typeKey1 = TypeKey_1.TypeKey.get(key);
            var plist = Inject_1.InjectedTypes.getParamList(key, typeKey1);
            var value = null;
            if (plist) {
                var pv = plist.map(function (x) { return x ? _this.resolve(x) : (void 0); });
                pv.unshift(null);
                value = new (key.bind.apply(key, pv))();
                for (var _i = 0, pv_1 = pv; _i < pv_1.length; _i++) {
                    var iterator = pv_1[_i];
                    if (iterator && iterator instanceof TransientDisposable_1.default) {
                        iterator.registerIn(value);
                    }
                }
            }
            else {
                value = new (key)();
            }
            var propList = Inject_1.InjectedTypes.getPropertyList(key, typeKey1);
            if (propList) {
                for (var key1 in propList) {
                    if (propList.hasOwnProperty(key1)) {
                        var element = propList[key1];
                        var d = this.resolve(element);
                        value[key1] = d;
                        if (d && d instanceof TransientDisposable_1.default) {
                            d.registerIn(value);
                        }
                    }
                }
            }
            return value;
        };
        ServiceProvider.mappedTypes = {};
        return ServiceProvider;
    }());
    exports.ServiceProvider = ServiceProvider;
});
//# sourceMappingURL=ServiceProvider.js.map