define(["require", "exports", "../constants/error_msgs", "../constants/literal_types", "../utils/exceptions", "../utils/serialization", "./instantiation"], function (require, exports, ERROR_MSGS, literal_types_1, exceptions_1, serialization_1, instantiation_1) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    var invokeFactory = function (factoryType, serviceIdentifier, fn) {
        try {
            return fn();
        }
        catch (error) {
            if (exceptions_1.isStackOverflowExeption(error)) {
                throw new Error(ERROR_MSGS.CIRCULAR_DEPENDENCY_IN_FACTORY(factoryType, serviceIdentifier.toString()));
            }
            else {
                throw error;
            }
        }
    };
    var _resolveRequest = function (requestScope) {
        return function (request) {
            request.parentContext.setCurrentRequest(request);
            var bindings = request.bindings;
            var childRequests = request.childRequests;
            var targetIsAnArray = request.target && request.target.isArray();
            var targetParentIsNotAnArray = !request.parentRequest ||
                !request.parentRequest.target ||
                !request.target ||
                !request.parentRequest.target.matchesArray(request.target.serviceIdentifier);
            if (targetIsAnArray && targetParentIsNotAnArray) {
                return childRequests.map(function (childRequest) {
                    var _f = _resolveRequest(requestScope);
                    return _f(childRequest);
                });
            }
            else {
                var result = null;
                if (request.target.isOptional() && bindings.length === 0) {
                    return undefined;
                }
                var binding_1 = bindings[0];
                var isSingleton = binding_1.scope === literal_types_1.BindingScopeEnum.Singleton;
                var isRequestSingleton = binding_1.scope === literal_types_1.BindingScopeEnum.Request;
                if (isSingleton && binding_1.activated) {
                    return binding_1.cache;
                }
                if (isRequestSingleton &&
                    requestScope !== null &&
                    requestScope.has(binding_1.id)) {
                    return requestScope.get(binding_1.id);
                }
                if (binding_1.type === literal_types_1.BindingTypeEnum.ConstantValue) {
                    result = binding_1.cache;
                }
                else if (binding_1.type === literal_types_1.BindingTypeEnum.Function) {
                    result = binding_1.cache;
                }
                else if (binding_1.type === literal_types_1.BindingTypeEnum.Constructor) {
                    result = binding_1.implementationType;
                }
                else if (binding_1.type === literal_types_1.BindingTypeEnum.DynamicValue && binding_1.dynamicValue !== null) {
                    result = invokeFactory("toDynamicValue", binding_1.serviceIdentifier, function () { return binding_1.dynamicValue(request.parentContext); });
                }
                else if (binding_1.type === literal_types_1.BindingTypeEnum.Factory && binding_1.factory !== null) {
                    result = invokeFactory("toFactory", binding_1.serviceIdentifier, function () { return binding_1.factory(request.parentContext); });
                }
                else if (binding_1.type === literal_types_1.BindingTypeEnum.Provider && binding_1.provider !== null) {
                    result = invokeFactory("toProvider", binding_1.serviceIdentifier, function () { return binding_1.provider(request.parentContext); });
                }
                else if (binding_1.type === literal_types_1.BindingTypeEnum.Instance && binding_1.implementationType !== null) {
                    result = instantiation_1.resolveInstance(binding_1.implementationType, childRequests, _resolveRequest(requestScope));
                }
                else {
                    var serviceIdentifier = serialization_1.getServiceIdentifierAsString(request.serviceIdentifier);
                    throw new Error(ERROR_MSGS.INVALID_BINDING_TYPE + " " + serviceIdentifier);
                }
                if (typeof binding_1.onActivation === "function") {
                    result = binding_1.onActivation(request.parentContext, result);
                }
                if (isSingleton) {
                    binding_1.cache = result;
                    binding_1.activated = true;
                }
                if (isRequestSingleton &&
                    requestScope !== null &&
                    !requestScope.has(binding_1.id)) {
                    requestScope.set(binding_1.id, result);
                }
                return result;
            }
        };
    };
    function resolve(context) {
        var _f = _resolveRequest(context.plan.rootRequest.requestScope);
        return _f(context.plan.rootRequest);
    }
    exports.resolve = resolve;
});
