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


"use strict";
import { _DYN_CALL, _DYN_GET_NEXT, _DYN_GET_PLUGIN, _DYN_INITIALIZE, _DYN_IS_INITIALIZED, _DYN_LENGTH, _DYN_NAME, _DYN_PUSH, _DYN_SET_NEXT_PLUGIN, _DYN_SPAN_ID, _DYN_TEARDOWN, _DYN_TRACE_FLAGS, _DYN_TRACE_ID, _DYN__DO_TEARDOWN } from "../__DynamicConstants";
import { createElmNodeData } from "./DataCacheHelper";
import { arrForEach, isFunction } from "./HelperFuncs";
import { STR_CORE, STR_PRIORITY, STR_PROCESS_TELEMETRY } from "./InternalConstants";
import { isValidSpanId, isValidTraceId } from "./W3cTraceParent";
var pluginStateData = createElmNodeData("plugin");
export function _getPluginState(plugin) {
    return pluginStateData.get(plugin, "state", {}, true);
}
/**
 * Initialize the queue of plugins
 * @param plugins - The array of plugins to initialize and setting of the next plugin
 * @param config The current config for the instance
 * @param core THe current core instance
 * @param extensions The extensions
 */
export function initializePlugins(processContext, extensions) {
    // Set the next plugin and identified the uninitialized plugins
    var initPlugins = [];
    var lastPlugin = null;
    var proxy = processContext[_DYN_GET_NEXT /* @min:%2egetNext */]();
    var pluginState;
    while (proxy) {
        var thePlugin = proxy[_DYN_GET_PLUGIN /* @min:%2egetPlugin */]();
        if (thePlugin) {
            if (lastPlugin &&
                isFunction(lastPlugin[_DYN_SET_NEXT_PLUGIN /* @min:%2esetNextPlugin */]) &&
                isFunction(thePlugin[STR_PROCESS_TELEMETRY /* @min:%2eprocessTelemetry */])) {
                // Set this plugin as the next for the previous one
                lastPlugin[_DYN_SET_NEXT_PLUGIN /* @min:%2esetNextPlugin */](thePlugin);
            }
            var isInitialized = false;
            if (isFunction(thePlugin[_DYN_IS_INITIALIZED /* @min:%2eisInitialized */])) {
                isInitialized = thePlugin[_DYN_IS_INITIALIZED /* @min:%2eisInitialized */]();
            }
            else {
                pluginState = _getPluginState(thePlugin);
                isInitialized = pluginState[_DYN_IS_INITIALIZED /* @min:%2eisInitialized */];
            }
            if (!isInitialized) {
                initPlugins[_DYN_PUSH /* @min:%2epush */](thePlugin);
            }
            lastPlugin = thePlugin;
            proxy = proxy[_DYN_GET_NEXT /* @min:%2egetNext */]();
        }
    }
    // Now initialize the plugins
    arrForEach(initPlugins, function (thePlugin) {
        var core = processContext[STR_CORE]();
        thePlugin[_DYN_INITIALIZE /* @min:%2einitialize */](processContext.getCfg(), core, extensions, processContext[_DYN_GET_NEXT /* @min:%2egetNext */]());
        pluginState = _getPluginState(thePlugin);
        // Only add the core to the state if the plugin didn't set it (doesn't extent from BaseTelemetryPlugin)
        if (!thePlugin[STR_CORE] && !pluginState[STR_CORE]) {
            pluginState[STR_CORE] = core;
        }
        pluginState[_DYN_IS_INITIALIZED /* @min:%2eisInitialized */] = true;
        delete pluginState[_DYN_TEARDOWN /* @min:%2eteardown */];
    });
}
export function sortPlugins(plugins) {
    // Sort by priority
    return plugins.sort(function (extA, extB) {
        var result = 0;
        if (extB) {
            var bHasProcess = isFunction(extB[STR_PROCESS_TELEMETRY]);
            if (isFunction(extA[STR_PROCESS_TELEMETRY])) {
                result = bHasProcess ? extA[STR_PRIORITY] - extB[STR_PRIORITY] : 1;
            }
            else if (bHasProcess) {
                result = -1;
            }
        }
        else {
            result = extA ? 1 : -1;
        }
        return result;
    });
    // sort complete
}
/**
 * Teardown / Unload helper to perform teardown/unloading operations for the provided components synchronously or asynchronously, this will call any
 * _doTeardown() or _doUnload() functions on the provided components to allow them to finish removal.
 * @param components - The components you want to unload
 * @param unloadCtx - This is the context that should be used during unloading.
 * @param unloadState - The details / state of the unload process, it holds details like whether it should be unloaded synchronously or asynchronously and the reason for the unload.
 * @param asyncCallback - An optional callback that the plugin must call if it returns true to inform the caller that it has completed any async unload/teardown operations.
 * @returns boolean - true if the plugin has or will call asyncCallback, this allows the plugin to perform any asynchronous operations.
 */
export function unloadComponents(components, unloadCtx, unloadState, asyncCallback) {
    var idx = 0;
    function _doUnload() {
        while (idx < components[_DYN_LENGTH /* @min:%2elength */]) {
            var component = components[idx++];
            if (component) {
                var func = component._doUnload || component[_DYN__DO_TEARDOWN /* @min:%2e_doTeardown */];
                if (isFunction(func)) {
                    if (func[_DYN_CALL /* @min:%2ecall */](component, unloadCtx, unloadState, _doUnload) === true) {
                        return true;
                    }
                }
            }
        }
    }
    return _doUnload();
}
/**
 * Creates a IDistributedTraceContext which optionally also "sets" the value on a parent
 * @param parentCtx - An optional parent distributed trace instance
 * @returns A new IDistributedTraceContext instance that uses an internal temporary object
 */
export function createDistributedTraceContext(parentCtx) {
    var trace = {};
    return {
        getName: function () {
            return trace[_DYN_NAME /* @min:%2ename */];
        },
        setName: function (newValue) {
            parentCtx && parentCtx.setName(newValue);
            trace[_DYN_NAME /* @min:%2ename */] = newValue;
        },
        getTraceId: function () {
            return trace[_DYN_TRACE_ID /* @min:%2etraceId */];
        },
        setTraceId: function (newValue) {
            parentCtx && parentCtx.setTraceId(newValue);
            if (isValidTraceId(newValue)) {
                trace[_DYN_TRACE_ID /* @min:%2etraceId */] = newValue;
            }
        },
        getSpanId: function () {
            return trace[_DYN_SPAN_ID /* @min:%2espanId */];
        },
        setSpanId: function (newValue) {
            parentCtx && parentCtx.setSpanId(newValue);
            if (isValidSpanId(newValue)) {
                trace[_DYN_SPAN_ID /* @min:%2espanId */] = newValue;
            }
        },
        getTraceFlags: function () {
            return trace[_DYN_TRACE_FLAGS /* @min:%2etraceFlags */];
        },
        setTraceFlags: function (newTraceFlags) {
            parentCtx && parentCtx.setTraceFlags(newTraceFlags);
            trace[_DYN_TRACE_FLAGS /* @min:%2etraceFlags */] = newTraceFlags;
        }
    };
}
