/*
 * Application Insights JavaScript SDK - Core, 2.8.15
 * Copyright (c) Microsoft and contributors. All rights reserved.
 */


import dynamicProto from "@microsoft/dynamicproto-js";
import { _DYN_COMPLETE, _DYN_GET_CTX, _DYN_IS_ASYNC, _DYN_IS_CHILD_EVT, _DYN_LENGTH, _DYN_NAME, _DYN_PUSH, _DYN_SET_CTX, _DYN_TIME } from "../__DynamicConstants";
import { dateNow, isArray, isFunction, objDefineAccessors } from "./HelperFuncs";
import { STR_GET_PERF_MGR, STR_PERF_EVENT } from "./InternalConstants";
var strExecutionContextKey = "ctx";
var strParentContextKey = "ParentContextKey";
var strChildrenContextKey = "ChildrenContextKey";
var _defaultPerfManager = null;
var PerfEvent = /** @class */ (function () {
    function PerfEvent(name, payloadDetails, isAsync) {
        var _self = this;
        var accessorDefined = false;
        _self.start = dateNow();
        _self[_DYN_NAME /* @min:%2ename */] = name;
        _self[_DYN_IS_ASYNC /* @min:%2eisAsync */] = isAsync;
        _self[_DYN_IS_CHILD_EVT /* @min:%2eisChildEvt */] = function () { return false; };
        if (isFunction(payloadDetails)) {
            // Create an accessor to minimize the potential performance impact of executing the payloadDetails callback
            var theDetails_1;
            accessorDefined = objDefineAccessors(_self, "payload", function () {
                // Delay the execution of the payloadDetails until needed
                if (!theDetails_1 && isFunction(payloadDetails)) {
                    theDetails_1 = payloadDetails();
                    // clear it out now so the referenced objects can be garbage collected
                    payloadDetails = null;
                }
                return theDetails_1;
            });
        }
        _self[_DYN_GET_CTX /* @min:%2egetCtx */] = function (key) {
            if (key) {
                // The parent and child links are located directly on the object (for better viewing in the DebugPlugin)
                if (key === PerfEvent[strParentContextKey] || key === PerfEvent[strChildrenContextKey]) {
                    return _self[key];
                }
                return (_self[strExecutionContextKey] || {})[key];
            }
            return null;
        };
        _self[_DYN_SET_CTX /* @min:%2esetCtx */] = function (key, value) {
            if (key) {
                // Put the parent and child links directly on the object (for better viewing in the DebugPlugin)
                if (key === PerfEvent[strParentContextKey]) {
                    // Simple assumption, if we are setting a parent then we must be a child
                    if (!_self[key]) {
                        _self[_DYN_IS_CHILD_EVT /* @min:%2eisChildEvt */] = function () { return true; };
                    }
                    _self[key] = value;
                }
                else if (key === PerfEvent[strChildrenContextKey]) {
                    _self[key] = value;
                }
                else {
                    var ctx = _self[strExecutionContextKey] = _self[strExecutionContextKey] || {};
                    ctx[key] = value;
                }
            }
        };
        _self[_DYN_COMPLETE /* @min:%2ecomplete */] = function () {
            var childTime = 0;
            var childEvts = _self[_DYN_GET_CTX /* @min:%2egetCtx */](PerfEvent[strChildrenContextKey]);
            if (isArray(childEvts)) {
                for (var lp = 0; lp < childEvts[_DYN_LENGTH /* @min:%2elength */]; lp++) {
                    var childEvt = childEvts[lp];
                    if (childEvt) {
                        childTime += childEvt[_DYN_TIME /* @min:%2etime */];
                    }
                }
            }
            _self[_DYN_TIME /* @min:%2etime */] = dateNow() - _self.start;
            _self.exTime = _self[_DYN_TIME /* @min:%2etime */] - childTime;
            _self[_DYN_COMPLETE /* @min:%2ecomplete */] = function () { };
            if (!accessorDefined && isFunction(payloadDetails)) {
                // If we couldn't define the property set during complete -- to minimize the perf impact until after the time
                _self.payload = payloadDetails();
            }
        };
    }
    PerfEvent.ParentContextKey = "parent";
    PerfEvent.ChildrenContextKey = "childEvts";
    return PerfEvent;
}());
export { PerfEvent };
var PerfManager = /** @class */ (function () {
    function PerfManager(manager) {
        /**
         * General bucket used for execution context set and retrieved via setCtx() and getCtx.
         * Defined as private so it can be visualized via the DebugPlugin
         */
        this.ctx = {};
        dynamicProto(PerfManager, this, function (_self) {
            _self.create = function (src, payloadDetails, isAsync) {
                // TODO (@MSNev): at some point we will want to add additional configuration to "select" which events to instrument
                // for now this is just a simple do everything.
                return new PerfEvent(src, payloadDetails, isAsync);
            };
            _self.fire = function (perfEvent) {
                if (perfEvent) {
                    perfEvent[_DYN_COMPLETE /* @min:%2ecomplete */]();
                    if (manager && isFunction(manager[STR_PERF_EVENT /* @min:%2eperfEvent */])) {
                        manager[STR_PERF_EVENT /* @min:%2eperfEvent */](perfEvent);
                    }
                }
            };
            _self[_DYN_SET_CTX /* @min:%2esetCtx */] = function (key, value) {
                if (key) {
                    var ctx = _self[strExecutionContextKey] = _self[strExecutionContextKey] || {};
                    ctx[key] = value;
                }
            };
            _self[_DYN_GET_CTX /* @min:%2egetCtx */] = function (key) {
                return (_self[strExecutionContextKey] || {})[key];
            };
        });
    }
// Removed Stub for PerfManager.prototype.create.
// Removed Stub for PerfManager.prototype.fire.
// Removed Stub for PerfManager.prototype.setCtx.
// Removed Stub for PerfManager.prototype.getCtx.
    // This is a workaround for an IE8 bug when using dynamicProto() with classes that don't have any
    // non-dynamic functions or static properties/functions when using uglify-js to minify the resulting code.
    // this will be removed when ES3 support is dropped.
    PerfManager.__ieDyn=1;

    return PerfManager;
}());
export { PerfManager };
var doPerfActiveKey = "CoreUtils.doPerf";
/**
 * Helper function to wrap a function with a perf event
 * @param mgrSource - The Performance Manager or a Performance provider source (may be null)
 * @param getSource - The callback to create the source name for the event (if perf monitoring is enabled)
 * @param func - The function to call and measure
 * @param details - A function to return the payload details
 * @param isAsync - Is the event / function being call asynchronously or synchronously
 */
export function doPerf(mgrSource, getSource, func, details, isAsync) {
    if (mgrSource) {
        var perfMgr = mgrSource;
        if (perfMgr[STR_GET_PERF_MGR]) {
            // Looks like a perf manager provider object
            perfMgr = perfMgr[STR_GET_PERF_MGR]();
        }
        if (perfMgr) {
            var perfEvt = void 0;
            var currentActive = perfMgr[_DYN_GET_CTX /* @min:%2egetCtx */](doPerfActiveKey);
            try {
                perfEvt = perfMgr.create(getSource(), details, isAsync);
                if (perfEvt) {
                    if (currentActive && perfEvt[_DYN_SET_CTX /* @min:%2esetCtx */]) {
                        perfEvt[_DYN_SET_CTX /* @min:%2esetCtx */](PerfEvent[strParentContextKey], currentActive);
                        if (currentActive[_DYN_GET_CTX /* @min:%2egetCtx */] && currentActive[_DYN_SET_CTX /* @min:%2esetCtx */]) {
                            var children = currentActive[_DYN_GET_CTX /* @min:%2egetCtx */](PerfEvent[strChildrenContextKey]);
                            if (!children) {
                                children = [];
                                currentActive[_DYN_SET_CTX /* @min:%2esetCtx */](PerfEvent[strChildrenContextKey], children);
                            }
                            children[_DYN_PUSH /* @min:%2epush */](perfEvt);
                        }
                    }
                    // Set this event as the active event now
                    perfMgr[_DYN_SET_CTX /* @min:%2esetCtx */](doPerfActiveKey, perfEvt);
                    return func(perfEvt);
                }
            }
            catch (ex) {
                if (perfEvt && perfEvt[_DYN_SET_CTX /* @min:%2esetCtx */]) {
                    perfEvt[_DYN_SET_CTX /* @min:%2esetCtx */]("exception", ex);
                }
            }
            finally {
                // fire the perf event
                if (perfEvt) {
                    perfMgr.fire(perfEvt);
                }
                // Reset the active event to the previous value
                perfMgr[_DYN_SET_CTX /* @min:%2esetCtx */](doPerfActiveKey, currentActive);
            }
        }
    }
    return func();
}
/**
 * Set the global performance manager to use when there is no core instance or it has not been initialized yet.
 * @param perfManager - The IPerfManager instance to use when no performance manager is supplied.
 */
export function setGblPerfMgr(perfManager) {
    _defaultPerfManager = perfManager;
}
/**
 * Get the current global performance manager that will be used with no performance manager is supplied.
 * @returns - The current default manager
 */
export function getGblPerfMgr() {
    return _defaultPerfManager;
}
