/*
 * 1DS JS SDK POST plugin, 4.0.6
 * Copyright (c) Microsoft and contributors. All rights reserved.
 * (Microsoft Internal Only)
 */
import { __extendsFn as __extends } from "@microsoft/applicationinsights-shims";
/**
* PostManager.ts
* @author Abhilash Panwar (abpanwar); Hector Hernandez (hectorh); Nev Wylie (newylie)
* @copyright Microsoft 2018-2020
*/
import dynamicProto from "@microsoft/dynamicproto-js";
import { BaseTelemetryPlugin, EventsDiscardedReason, _throwInternal, addPageHideEventListener, addPageShowEventListener, addPageUnloadEventListener, arrForEach, createProcessTelemetryContext, createUniqueNamespace, doPerf, getWindow, isChromium, isGreaterThanZero, isNumber, mergeEvtNamespace, objForEachKey, onConfigChange, optimizeObject, proxyFunctions, removePageHideEventListener, removePageShowEventListener, removePageUnloadEventListener, setProcessTelemetryTimings } from "@microsoft/1ds-core-js";
import { createPromise } from "@nevware21/ts-async";
import { objDeepFreeze } from "@nevware21/ts-utils";
import { BE_PROFILE, NRT_PROFILE, RT_PROFILE } from "./DataModels";
import { EventBatch } from "./EventBatch";
import { HttpManager } from "./HttpManager";
import { STR_MSA_DEVICE_TICKET, STR_TRACE, STR_USER } from "./InternalConstants";
import { retryPolicyGetMillisToBackoffForRetry } from "./RetryPolicy";
import { createTimeoutWrapper } from "./TimeoutOverrideWrapper";
import { _DYN_AUTO_FLUSH_EVENTS_LI13, _DYN_BASE_DATA, _DYN_BATCHES, _DYN_CAN_SEND_REQUEST, _DYN_CLEAR_TIMEOUT_OVERRI3, _DYN_CONCAT, _DYN_COUNT, _DYN_DATA, _DYN_DISABLE_AUTO_BATCH_F14, _DYN_DISABLE_OPTIMIZE_OBJ, _DYN_DISABLE_TELEMETRY, _DYN_EVENTS, _DYN_EVENTS_LIMIT_IN_MEM, _DYN_GET_WPARAM, _DYN_IDENTIFIER, _DYN_IGNORE_MC1_MS0_COOKI12, _DYN_INITIALIZE, _DYN_IS_COMPLETELY_IDLE, _DYN_I_KEY, _DYN_LATENCY, _DYN_LENGTH, _DYN_OVERRIDE_INSTRUMENTA15, _DYN_PUSH, _DYN_SEND_ATTEMPT, _DYN_SEND_POST, _DYN_SEND_QUEUED_REQUESTS, _DYN_SEND_SYNCHRONOUS_BAT9, _DYN_SET_TIMEOUT_OVERRIDE, _DYN_SET_UNLOADING, _DYN_SPLICE, _DYN_SPLIT, _DYN_SYNC, _DYN__BACK_OFF_TRANSMISSI11 } from "./__DynamicConstants";
var FlushCheckTimer = 0.250; // This needs to be in seconds, so this is 250ms
var MaxNumberEventPerBatch = 500;
var EventsDroppedAtOneTime = 20;
var MaxSendAttempts = 6;
var MaxSyncUnloadSendAttempts = 2; // Assuming 2 based on beforeunload and unload
var MaxBackoffCount = 4;
var MaxConnections = 2;
var MaxRequestRetriesBeforeBackoff = 1;
var MaxEventsLimitInMem = 10000;
var strEventsDiscarded = "eventsDiscarded";
var undefValue = undefined;
/**
 * The default settings for the config.
 * WE MUST include all defaults here to ensure that the config is created with all of the properties
 * defined as dynamic.
 */
var defaultPostChannelConfig = objDeepFreeze({
    eventsLimitInMem: { isVal: isGreaterThanZero, v: MaxEventsLimitInMem },
    immediateEventLimit: { isVal: isGreaterThanZero, v: 500 },
    autoFlushEventsLimit: { isVal: isGreaterThanZero, v: 0 },
    disableAutoBatchFlushLimit: false,
    httpXHROverride: { isVal: isOverrideFn, v: undefValue },
    overrideInstrumentationKey: undefValue,
    overrideEndpointUrl: undefValue,
    disableTelemetry: false,
    ignoreMc1Ms0CookieProcessing: false,
    setTimeoutOverride: undefValue,
    clearTimeoutOverride: undefValue,
    payloadPreprocessor: undefValue,
    payloadListener: undefValue,
    disableEventTimings: undefValue,
    valueSanitizer: undefValue,
    stringifyObjects: undefValue,
    enableCompoundKey: undefValue,
    disableOptimizeObj: false,
    // disableCacheHeader: undefValue, // See Task #7178858 - Collector requires a change to support this
    transports: undefValue,
    unloadTransports: undefValue,
    useSendBeacon: undefValue,
    disableFetchKeepAlive: undefValue,
    avoidOptions: false,
    xhrTimeout: undefValue,
    disableXhrSync: undefValue,
    alwaysUseXhrOverride: false,
    maxEventRetryAttempts: { isVal: isNumber, v: MaxSendAttempts },
    maxUnloadEventRetryAttempts: { isVal: isNumber, v: MaxSyncUnloadSendAttempts },
    addNoResponse: undefValue
});
function isOverrideFn(httpXHROverride) {
    return httpXHROverride && httpXHROverride[_DYN_SEND_POST /* @min:%2esendPOST */];
}
/**
 * Class that manages adding events to inbound queues and batching of events
 * into requests.
 * @group Classes
 * @group Entrypoint
 */
var PostChannel = /** @class */ (function (_super) {
    __extends(PostChannel, _super);
    function PostChannel() {
        var _this = _super.call(this) || this;
        _this.identifier = "PostChannel";
        _this.priority = 1011;
        _this.version = '4.0.6';
        var _postConfig;
        var _isTeardownCalled = false;
        var _flushCallbackQueue = [];
        var _flushCallbackTimer;
        var _paused = false;
        var _immediateQueueSize = 0;
        var _immediateQueueSizeLimit;
        var _queueSize = 0;
        var _queueSizeLimit;
        var _profiles = {};
        var _currentProfile = RT_PROFILE;
        var _scheduledTimer;
        var _immediateTimer;
        var _currentBackoffCount;
        var _timerCount;
        var _httpManager;
        var _batchQueues;
        var _autoFlushEventsLimit;
        // either MaxBatchSize * (1+ Max Connections) or _queueLimit / 6 (where 3 latency Queues [normal, realtime, cost deferred] * 2 [allow half full -- allow for retry])
        var _autoFlushBatchLimit;
        var _delayedBatchSendLatency;
        var _delayedBatchReason;
        var _optimizeObject;
        var _isPageUnloadTriggered;
        var _maxEventSendAttempts;
        var _maxUnloadEventSendAttempts;
        var _evtNamespace;
        var _timeoutWrapper;
        var _ignoreMc1Ms0CookieProcessing;
        var _disableAutoBatchFlushLimit;
        var _notificationManager;
        var _unloadHandlersAdded;
        var _overrideInstrumentationKey;
        var _disableTelemetry;
        dynamicProto(PostChannel, _this, function (_self, _base) {
            _initDefaults();
            // Special internal method to allow the DebugPlugin to hook embedded objects
            _self["_getDbgPlgTargets"] = function () {
                return [_httpManager, _postConfig];
            };
            _self[_DYN_INITIALIZE /* @min:%2einitialize */] = function (theConfig, core, extensions) {
                doPerf(core, function () { return "PostChannel:initialize"; }, function () {
                    _base[_DYN_INITIALIZE /* @min:%2einitialize */](theConfig, core, extensions);
                    _notificationManager = core.getNotifyMgr();
                    try {
                        _evtNamespace = mergeEvtNamespace(createUniqueNamespace(_self[_DYN_IDENTIFIER /* @min:%2eidentifier */]), core.evtNamespace && core.evtNamespace());
                        _self._addHook(onConfigChange(theConfig, function (details) {
                            var coreConfig = details.cfg;
                            var ctx = createProcessTelemetryContext(null, coreConfig, core);
                            _postConfig = ctx.getExtCfg(_self[_DYN_IDENTIFIER /* @min:%2eidentifier */], defaultPostChannelConfig);
                            _timeoutWrapper = createTimeoutWrapper(_postConfig[_DYN_SET_TIMEOUT_OVERRIDE /* @min:%2esetTimeoutOverride */], _postConfig[_DYN_CLEAR_TIMEOUT_OVERRI3 /* @min:%2eclearTimeoutOverride */]);
                            // Only try and use the optimizeObject() if this appears to be a chromium based browser and it has not been explicitly disabled
                            _optimizeObject = !_postConfig[_DYN_DISABLE_OPTIMIZE_OBJ /* @min:%2edisableOptimizeObj */] && isChromium();
                            _ignoreMc1Ms0CookieProcessing = _postConfig[_DYN_IGNORE_MC1_MS0_COOKI12 /* @min:%2eignoreMc1Ms0CookieProcessing */];
                            _hookWParam(core); // _hookWParam uses _ignoreMc1Ms0CookieProcessing
                            _queueSizeLimit = _postConfig[_DYN_EVENTS_LIMIT_IN_MEM /* @min:%2eeventsLimitInMem */];
                            _immediateQueueSizeLimit = _postConfig.immediateEventLimit;
                            _autoFlushEventsLimit = _postConfig[_DYN_AUTO_FLUSH_EVENTS_LI13 /* @min:%2eautoFlushEventsLimit */];
                            _maxEventSendAttempts = _postConfig.maxEventRetryAttempts;
                            _maxUnloadEventSendAttempts = _postConfig.maxUnloadEventRetryAttempts;
                            _disableAutoBatchFlushLimit = _postConfig[_DYN_DISABLE_AUTO_BATCH_F14 /* @min:%2edisableAutoBatchFlushLimit */];
                            _setAutoLimits();
                            // Override iKey if provided in Post config if provided for during initialization
                            _overrideInstrumentationKey = _postConfig[_DYN_OVERRIDE_INSTRUMENTA15 /* @min:%2eoverrideInstrumentationKey */];
                            // DisableTelemetry was defined in the config provided during initialization
                            _disableTelemetry = !!_postConfig[_DYN_DISABLE_TELEMETRY /* @min:%2edisableTelemetry */];
                            if (_unloadHandlersAdded) {
                                _removeUnloadHandlers();
                            }
                            var excludePageUnloadEvents = coreConfig.disablePageUnloadEvents || [];
                            // When running in Web browsers try to send all telemetry if page is unloaded
                            _unloadHandlersAdded = addPageUnloadEventListener(_handleUnloadEvents, excludePageUnloadEvents, _evtNamespace);
                            _unloadHandlersAdded = addPageHideEventListener(_handleUnloadEvents, excludePageUnloadEvents, _evtNamespace) || _unloadHandlersAdded;
                            _unloadHandlersAdded = addPageShowEventListener(_handleShowEvents, coreConfig.disablePageShowEvents, _evtNamespace) || _unloadHandlersAdded;
                        }));
                        // only initialize the manager once
                        _httpManager[_DYN_INITIALIZE /* @min:%2einitialize */](theConfig, _self.core, _self);
                    }
                    catch (e) {
                        // resetting the initialized state because of failure
                        _self.setInitialized(false);
                        throw e;
                    }
                }, function () { return ({ theConfig: theConfig, core: core, extensions: extensions }); });
            };
            _self.processTelemetry = function (ev, itemCtx) {
                setProcessTelemetryTimings(ev, _self[_DYN_IDENTIFIER /* @min:%2eidentifier */]);
                itemCtx = itemCtx || _self._getTelCtx(itemCtx);
                var event = ev;
                if (!_disableTelemetry && !_isTeardownCalled) {
                    // Override iKey if provided in Post config if provided for during initialization
                    if (_overrideInstrumentationKey) {
                        event[_DYN_I_KEY /* @min:%2eiKey */] = _overrideInstrumentationKey;
                    }
                    _addEventToQueues(event, true);
                    if (_isPageUnloadTriggered) {
                        // Unload event has been received so we need to try and flush new events
                        _releaseAllQueues(2 /* EventSendType.SendBeacon */, 2 /* SendRequestReason.Unload */);
                    }
                    else {
                        _scheduleTimer();
                    }
                }
                _self.processNext(event, itemCtx);
            };
            _self._doTeardown = function (unloadCtx, unloadState) {
                _releaseAllQueues(2 /* EventSendType.SendBeacon */, 2 /* SendRequestReason.Unload */);
                _isTeardownCalled = true;
                _httpManager.teardown();
                _removeUnloadHandlers();
                // Just register to remove all events associated with this namespace
                _initDefaults();
            };
            function _removeUnloadHandlers() {
                removePageUnloadEventListener(null, _evtNamespace);
                removePageHideEventListener(null, _evtNamespace);
                removePageShowEventListener(null, _evtNamespace);
            }
            function _hookWParam(core) {
                var existingGetWParamMethod = core[_DYN_GET_WPARAM /* @min:%2egetWParam */];
                core[_DYN_GET_WPARAM /* @min:%2egetWParam */] = function () {
                    var wparam = 0;
                    if (_ignoreMc1Ms0CookieProcessing) {
                        wparam = wparam | 2;
                    }
                    return wparam | existingGetWParamMethod.call(core);
                };
            }
            // Moving event handlers out from the initialize closure so that any local variables can be garbage collected
            function _handleUnloadEvents(evt) {
                var theEvt = evt || getWindow().event; // IE 8 does not pass the event
                if (theEvt.type !== "beforeunload") {
                    // Only set the unload trigger if not beforeunload event as beforeunload can be cancelled while the other events can't
                    _isPageUnloadTriggered = true;
                    _httpManager[_DYN_SET_UNLOADING /* @min:%2esetUnloading */](_isPageUnloadTriggered);
                }
                _releaseAllQueues(2 /* EventSendType.SendBeacon */, 2 /* SendRequestReason.Unload */);
            }
            function _handleShowEvents(evt) {
                // Handle the page becoming visible again
                _isPageUnloadTriggered = false;
                _httpManager[_DYN_SET_UNLOADING /* @min:%2esetUnloading */](_isPageUnloadTriggered);
            }
            function _addEventToQueues(event, append) {
                // If send attempt field is undefined we should set it to 0.
                if (!event[_DYN_SEND_ATTEMPT /* @min:%2esendAttempt */]) {
                    event[_DYN_SEND_ATTEMPT /* @min:%2esendAttempt */] = 0;
                }
                // Add default latency
                if (!event[_DYN_LATENCY /* @min:%2elatency */]) {
                    event[_DYN_LATENCY /* @min:%2elatency */] = 1 /* EventLatencyValue.Normal */;
                }
                // Remove extra AI properties if present
                if (event.ext && event.ext[STR_TRACE]) {
                    delete (event.ext[STR_TRACE]);
                }
                if (event.ext && event.ext[STR_USER] && event.ext[STR_USER]["id"]) {
                    delete (event.ext[STR_USER]["id"]);
                }
                // v8 performance optimization for iterating over the keys
                if (_optimizeObject) {
                    event.ext = optimizeObject(event.ext);
                    if (event[_DYN_BASE_DATA /* @min:%2ebaseData */]) {
                        event[_DYN_BASE_DATA /* @min:%2ebaseData */] = optimizeObject(event[_DYN_BASE_DATA /* @min:%2ebaseData */]);
                    }
                    if (event[_DYN_DATA /* @min:%2edata */]) {
                        event[_DYN_DATA /* @min:%2edata */] = optimizeObject(event[_DYN_DATA /* @min:%2edata */]);
                    }
                }
                if (event[_DYN_SYNC /* @min:%2esync */]) {
                    // If the transmission is backed off then do not send synchronous events.
                    // We will convert these events to Real time latency instead.
                    if (_currentBackoffCount || _paused) {
                        event[_DYN_LATENCY /* @min:%2elatency */] = 3 /* EventLatencyValue.RealTime */;
                        event[_DYN_SYNC /* @min:%2esync */] = false;
                    }
                    else {
                        // Log the event synchronously
                        if (_httpManager) {
                            // v8 performance optimization for iterating over the keys
                            if (_optimizeObject) {
                                event = optimizeObject(event);
                            }
                            _httpManager[_DYN_SEND_SYNCHRONOUS_BAT9 /* @min:%2esendSynchronousBatch */](EventBatch.create(event[_DYN_I_KEY /* @min:%2eiKey */], [event]), event[_DYN_SYNC /* @min:%2esync */] === true ? 1 /* EventSendType.Synchronous */ : event[_DYN_SYNC /* @min:%2esync */], 3 /* SendRequestReason.SyncEvent */);
                            return;
                        }
                    }
                }
                var evtLatency = event[_DYN_LATENCY /* @min:%2elatency */];
                var queueSize = _queueSize;
                var queueLimit = _queueSizeLimit;
                if (evtLatency === 4 /* EventLatencyValue.Immediate */) {
                    queueSize = _immediateQueueSize;
                    queueLimit = _immediateQueueSizeLimit;
                }
                var eventDropped = false;
                // Only add the event if the queue isn't full or it's a direct event (which don't add to the queue sizes)
                if (queueSize < queueLimit) {
                    eventDropped = !_addEventToProperQueue(event, append);
                }
                else {
                    var dropLatency = 1 /* EventLatencyValue.Normal */;
                    var dropNumber = EventsDroppedAtOneTime;
                    if (evtLatency === 4 /* EventLatencyValue.Immediate */) {
                        // Only drop other immediate events as they are not technically sharing the general queue
                        dropLatency = 4 /* EventLatencyValue.Immediate */;
                        dropNumber = 1;
                    }
                    // Drop old event from lower or equal latency
                    eventDropped = true;
                    if (_dropEventWithLatencyOrLess(event[_DYN_I_KEY /* @min:%2eiKey */], event[_DYN_LATENCY /* @min:%2elatency */], dropLatency, dropNumber)) {
                        eventDropped = !_addEventToProperQueue(event, append);
                    }
                }
                if (eventDropped) {
                    // Can't drop events from current queues because the all the slots are taken by queues that are being flushed.
                    _notifyEvents(strEventsDiscarded, [event], EventsDiscardedReason.QueueFull);
                }
            }
            _self.setEventQueueLimits = function (eventLimit, autoFlushLimit) {
                _postConfig[_DYN_EVENTS_LIMIT_IN_MEM /* @min:%2eeventsLimitInMem */] = _queueSizeLimit = isGreaterThanZero(eventLimit) ? eventLimit : MaxEventsLimitInMem;
                _postConfig[_DYN_AUTO_FLUSH_EVENTS_LI13 /* @min:%2eautoFlushEventsLimit */] = _autoFlushEventsLimit = isGreaterThanZero(autoFlushLimit) ? autoFlushLimit : 0;
                _setAutoLimits();
                // We only do this check here as during normal event addition if the queue is > then events start getting dropped
                var doFlush = _queueSize > eventLimit;
                if (!doFlush && _autoFlushBatchLimit > 0) {
                    // Check the auto flush max batch size
                    for (var latency = 1 /* EventLatencyValue.Normal */; !doFlush && latency <= 3 /* EventLatencyValue.RealTime */; latency++) {
                        var batchQueue = _batchQueues[latency];
                        if (batchQueue && batchQueue[_DYN_BATCHES /* @min:%2ebatches */]) {
                            arrForEach(batchQueue[_DYN_BATCHES /* @min:%2ebatches */], function (theBatch) {
                                if (theBatch && theBatch[_DYN_COUNT /* @min:%2ecount */]() >= _autoFlushBatchLimit) {
                                    // If any 1 batch is > than the limit then trigger an auto flush
                                    doFlush = true;
                                }
                            });
                        }
                    }
                }
                _performAutoFlush(true, doFlush);
            };
            _self.pause = function () {
                _clearScheduledTimer();
                _paused = true;
                _httpManager.pause();
            };
            _self.resume = function () {
                _paused = false;
                _httpManager.resume();
                _scheduleTimer();
            };
            _self._loadTransmitProfiles = function (profiles) {
                _resetTransmitProfiles();
                objForEachKey(profiles, function (profileName, profileValue) {
                    var profLen = profileValue[_DYN_LENGTH /* @min:%2elength */];
                    if (profLen >= 2) {
                        var directValue = (profLen > 2 ? profileValue[2] : 0);
                        profileValue[_DYN_SPLICE /* @min:%2esplice */](0, profLen - 2);
                        // Make sure if a higher latency is set to not send then don't send lower latency
                        if (profileValue[1] < 0) {
                            profileValue[0] = -1;
                        }
                        // Make sure each latency is multiple of the latency higher then it. If not a multiple
                        // we round up so that it becomes a multiple.
                        if (profileValue[1] > 0 && profileValue[0] > 0) {
                            var timerMultiplier = profileValue[0] / profileValue[1];
                            profileValue[0] = Math.ceil(timerMultiplier) * profileValue[1];
                        }
                        // Add back the direct profile timeout
                        if (directValue >= 0 && profileValue[1] >= 0 && directValue > profileValue[1]) {
                            // Make sure if it's not disabled (< 0) then make sure it's not larger than RealTime
                            directValue = profileValue[1];
                        }
                        profileValue[_DYN_PUSH /* @min:%2epush */](directValue);
                        _profiles[profileName] = profileValue;
                    }
                });
            };
            _self.flush = function (async, callback, sendReason) {
                if (async === void 0) { async = true; }
                var result;
                if (!_paused) {
                    sendReason = sendReason || 1 /* SendRequestReason.ManualFlush */;
                    if (async) {
                        if (!callback) {
                            result = createPromise(function (resolve) {
                                // Set the callback to the promise resolve callback
                                callback = resolve;
                            });
                        }
                        if (_flushCallbackTimer == null) {
                            // Clear the normal schedule timer as we are going to try and flush ASAP
                            _clearScheduledTimer();
                            // Move all queued events to the HttpManager so that we don't discard new events (Auto flush scenario)
                            _queueBatches(1 /* EventLatencyValue.Normal */, 0 /* EventSendType.Batched */, sendReason);
                            _flushCallbackTimer = _createTimer(function () {
                                _flushCallbackTimer = null;
                                _flushImpl(callback, sendReason);
                            }, 0);
                        }
                        else {
                            // Even if null (no callback) this will ensure after the flushImpl finishes waiting
                            // for a completely idle connection it will attempt to re-flush any queued events on the next cycle
                            _flushCallbackQueue[_DYN_PUSH /* @min:%2epush */](callback);
                        }
                    }
                    else {
                        // Clear the normal schedule timer as we are going to try and flush ASAP
                        var cleared = _clearScheduledTimer();
                        // Now cause all queued events to be sent synchronously
                        _sendEventsForLatencyAndAbove(1 /* EventLatencyValue.Normal */, 1 /* EventSendType.Synchronous */, sendReason);
                        callback && callback();
                        if (cleared) {
                            // restart the normal event timer if it was cleared
                            _scheduleTimer();
                        }
                    }
                }
                return result;
            };
            _self.setMsaAuthTicket = function (ticket) {
                _httpManager.addHeader(STR_MSA_DEVICE_TICKET, ticket);
            };
            _self.hasEvents = _hasEvents;
            _self._setTransmitProfile = function (profileName) {
                if (_currentProfile !== profileName && _profiles[profileName] !== undefined) {
                    _clearScheduledTimer();
                    _currentProfile = profileName;
                    _scheduleTimer();
                }
            };
            proxyFunctions(_self, function () { return _httpManager; }, ["addResponseHandler"]);
            /**
             * Batch and send events currently in the queue for the given latency.
             * @param latency - Latency for which to send events.
             */
            function _sendEventsForLatencyAndAbove(latency, sendType, sendReason) {
                var queued = _queueBatches(latency, sendType, sendReason);
                // Always trigger the request as while the post channel may not have queued additional events, the httpManager may already have waiting events
                _httpManager[_DYN_SEND_QUEUED_REQUESTS /* @min:%2esendQueuedRequests */](sendType, sendReason);
                return queued;
            }
            function _hasEvents() {
                return _queueSize > 0;
            }
            /**
             * Try to schedule the timer after which events will be sent. If there are
             * no events to be sent, or there is already a timer scheduled, or the
             * http manager doesn't have any idle connections this method is no-op.
             */
            function _scheduleTimer() {
                // If we had previously attempted to send requests, but the http manager didn't have any idle connections then the requests where delayed
                // so try and requeue then again now
                if (_delayedBatchSendLatency >= 0 && _queueBatches(_delayedBatchSendLatency, 0 /* EventSendType.Batched */, _delayedBatchReason)) {
                    _httpManager[_DYN_SEND_QUEUED_REQUESTS /* @min:%2esendQueuedRequests */](0 /* EventSendType.Batched */, _delayedBatchReason);
                }
                if (_immediateQueueSize > 0 && !_immediateTimer && !_paused) {
                    // During initialization _profiles enforce that the direct [2] is less than real time [1] timer value
                    // If the immediateTimeout is disabled the immediate events will be sent with Real Time events
                    var immediateTimeOut = _profiles[_currentProfile][2];
                    if (immediateTimeOut >= 0) {
                        _immediateTimer = _createTimer(function () {
                            _immediateTimer = null;
                            // Only try to send direct events
                            _sendEventsForLatencyAndAbove(4 /* EventLatencyValue.Immediate */, 0 /* EventSendType.Batched */, 1 /* SendRequestReason.NormalSchedule */);
                            _scheduleTimer();
                        }, immediateTimeOut);
                    }
                }
                // During initialization the _profiles enforce that the normal [0] is a multiple of the real time [1] timer value
                var timeOut = _profiles[_currentProfile][1];
                if (!_scheduledTimer && !_flushCallbackTimer && timeOut >= 0 && !_paused) {
                    if (_hasEvents()) {
                        _scheduledTimer = _createTimer(function () {
                            _scheduledTimer = null;
                            _sendEventsForLatencyAndAbove(_timerCount === 0 ? 3 /* EventLatencyValue.RealTime */ : 1 /* EventLatencyValue.Normal */, 0 /* EventSendType.Batched */, 1 /* SendRequestReason.NormalSchedule */);
                            // Increment the count for next cycle
                            _timerCount++;
                            _timerCount %= 2;
                            _scheduleTimer();
                        }, timeOut);
                    }
                    else {
                        _timerCount = 0;
                    }
                }
            }
            _self[_DYN__BACK_OFF_TRANSMISSI11 /* @min:%2e_backOffTransmission */] = function () {
                if (_currentBackoffCount < MaxBackoffCount) {
                    _currentBackoffCount++;
                    _clearScheduledTimer();
                    _scheduleTimer();
                }
            };
            _self._clearBackOff = function () {
                if (_currentBackoffCount) {
                    _currentBackoffCount = 0;
                    _clearScheduledTimer();
                    _scheduleTimer();
                }
            };
            function _initDefaults() {
                _postConfig = null;
                _isTeardownCalled = false;
                _flushCallbackQueue = [];
                _flushCallbackTimer = null;
                _paused = false;
                _immediateQueueSize = 0;
                _immediateQueueSizeLimit = 500;
                _queueSize = 0;
                _queueSizeLimit = MaxEventsLimitInMem;
                _profiles = {};
                _currentProfile = RT_PROFILE;
                _scheduledTimer = null;
                _immediateTimer = null;
                _currentBackoffCount = 0;
                _timerCount = 0;
                _batchQueues = {};
                _autoFlushEventsLimit = 0;
                _unloadHandlersAdded = false;
                // either MaxBatchSize * (1+ Max Connections) or _queueLimit / 6 (where 3 latency Queues [normal, realtime, cost deferred] * 2 [allow half full -- allow for retry])
                _autoFlushBatchLimit = 0;
                _delayedBatchSendLatency = -1;
                _delayedBatchReason = null;
                _optimizeObject = true;
                _isPageUnloadTriggered = false;
                _maxEventSendAttempts = MaxSendAttempts;
                _maxUnloadEventSendAttempts = MaxSyncUnloadSendAttempts;
                _evtNamespace = null;
                _overrideInstrumentationKey = null;
                _disableTelemetry = false;
                _timeoutWrapper = createTimeoutWrapper();
                _httpManager = new HttpManager(MaxNumberEventPerBatch, MaxConnections, MaxRequestRetriesBeforeBackoff, {
                    requeue: _requeueEvents,
                    send: _sendingEvent,
                    sent: _eventsSentEvent,
                    drop: _eventsDropped,
                    rspFail: _eventsResponseFail,
                    oth: _otherEvent
                });
                _initializeProfiles();
                _clearQueues();
                _setAutoLimits();
            }
            function _createTimer(theTimerFunc, timeOut) {
                // If the transmission is backed off make the timer at least 1 sec to allow for back off.
                if (timeOut === 0 && _currentBackoffCount) {
                    timeOut = 1;
                }
                var timerMultiplier = 1000;
                if (_currentBackoffCount) {
                    timerMultiplier = retryPolicyGetMillisToBackoffForRetry(_currentBackoffCount - 1);
                }
                return _timeoutWrapper.set(theTimerFunc, timeOut * timerMultiplier);
            }
            function _clearScheduledTimer() {
                if (_scheduledTimer !== null) {
                    _scheduledTimer.cancel();
                    _scheduledTimer = null;
                    _timerCount = 0;
                    return true;
                }
                return false;
            }
            // Try to send all queued events using beacons if available
            function _releaseAllQueues(sendType, sendReason) {
                _clearScheduledTimer();
                // Cancel all flush callbacks
                if (_flushCallbackTimer) {
                    _flushCallbackTimer.cancel();
                    _flushCallbackTimer = null;
                }
                if (!_paused) {
                    // Queue all the remaining requests to be sent. The requests will be sent using HTML5 Beacons if they are available.
                    _sendEventsForLatencyAndAbove(1 /* EventLatencyValue.Normal */, sendType, sendReason);
                }
            }
            /**
             * Add empty queues for all latencies in the inbound queues map. This is called
             * when Transmission Manager is being flushed. This ensures that new events added
             * after flush are stored separately till we flush the current events.
             */
            function _clearQueues() {
                _batchQueues[4 /* EventLatencyValue.Immediate */] = {
                    batches: [],
                    iKeyMap: {}
                };
                _batchQueues[3 /* EventLatencyValue.RealTime */] = {
                    batches: [],
                    iKeyMap: {}
                };
                _batchQueues[2 /* EventLatencyValue.CostDeferred */] = {
                    batches: [],
                    iKeyMap: {}
                };
                _batchQueues[1 /* EventLatencyValue.Normal */] = {
                    batches: [],
                    iKeyMap: {}
                };
            }
            function _getEventBatch(iKey, latency, create) {
                var batchQueue = _batchQueues[latency];
                if (!batchQueue) {
                    latency = 1 /* EventLatencyValue.Normal */;
                    batchQueue = _batchQueues[latency];
                }
                var eventBatch = batchQueue.iKeyMap[iKey];
                if (!eventBatch && create) {
                    eventBatch = EventBatch.create(iKey);
                    batchQueue.batches[_DYN_PUSH /* @min:%2epush */](eventBatch);
                    batchQueue.iKeyMap[iKey] = eventBatch;
                }
                return eventBatch;
            }
            function _performAutoFlush(isAsync, doFlush) {
                // Only perform the auto flush check if the httpManager has an idle connection and we are not in a backoff situation
                if (_httpManager[_DYN_CAN_SEND_REQUEST /* @min:%2ecanSendRequest */]() && !_currentBackoffCount) {
                    if (_autoFlushEventsLimit > 0 && _queueSize > _autoFlushEventsLimit) {
                        // Force flushing
                        doFlush = true;
                    }
                    if (doFlush && _flushCallbackTimer == null) {
                        // Auto flush the queue, adding a callback to avoid the creation of a promise
                        _self.flush(isAsync, function () { }, 20 /* SendRequestReason.MaxQueuedEvents */);
                    }
                }
            }
            function _addEventToProperQueue(event, append) {
                // v8 performance optimization for iterating over the keys
                if (_optimizeObject) {
                    event = optimizeObject(event);
                }
                var latency = event[_DYN_LATENCY /* @min:%2elatency */];
                var eventBatch = _getEventBatch(event[_DYN_I_KEY /* @min:%2eiKey */], latency, true);
                if (eventBatch.addEvent(event)) {
                    if (latency !== 4 /* EventLatencyValue.Immediate */) {
                        _queueSize++;
                        // Check for auto flushing based on total events in the queue, but not for requeued or retry events
                        if (append && event[_DYN_SEND_ATTEMPT /* @min:%2esendAttempt */] === 0) {
                            // Force the flushing of the batch if the batch (specific iKey / latency combination) reaches it's auto flush limit
                            _performAutoFlush(!event.sync, _autoFlushBatchLimit > 0 && eventBatch[_DYN_COUNT /* @min:%2ecount */]() >= _autoFlushBatchLimit);
                        }
                    }
                    else {
                        // Direct events don't need auto flushing as they are scheduled (by default) for immediate delivery
                        _immediateQueueSize++;
                    }
                    return true;
                }
                return false;
            }
            function _dropEventWithLatencyOrLess(iKey, latency, currentLatency, dropNumber) {
                while (currentLatency <= latency) {
                    var eventBatch = _getEventBatch(iKey, latency, true);
                    if (eventBatch && eventBatch[_DYN_COUNT /* @min:%2ecount */]() > 0) {
                        // Dropped oldest events from lowest possible latency
                        var droppedEvents = eventBatch[_DYN_SPLIT /* @min:%2esplit */](0, dropNumber);
                        var droppedCount = droppedEvents[_DYN_COUNT /* @min:%2ecount */]();
                        if (droppedCount > 0) {
                            if (currentLatency === 4 /* EventLatencyValue.Immediate */) {
                                _immediateQueueSize -= droppedCount;
                            }
                            else {
                                _queueSize -= droppedCount;
                            }
                            _notifyBatchEvents(strEventsDiscarded, [droppedEvents], EventsDiscardedReason.QueueFull);
                            return true;
                        }
                    }
                    currentLatency++;
                }
                // Unable to drop any events -- lets just make sure the queue counts are correct to avoid exhaustion
                _resetQueueCounts();
                return false;
            }
            /**
             * Internal helper to reset the queue counts, used as a backstop to avoid future queue exhaustion errors
             * that might occur because of counting issues.
             */
            function _resetQueueCounts() {
                var immediateQueue = 0;
                var normalQueue = 0;
                var _loop_1 = function (latency) {
                    var batchQueue = _batchQueues[latency];
                    if (batchQueue && batchQueue[_DYN_BATCHES /* @min:%2ebatches */]) {
                        arrForEach(batchQueue[_DYN_BATCHES /* @min:%2ebatches */], function (theBatch) {
                            if (latency === 4 /* EventLatencyValue.Immediate */) {
                                immediateQueue += theBatch[_DYN_COUNT /* @min:%2ecount */]();
                            }
                            else {
                                normalQueue += theBatch[_DYN_COUNT /* @min:%2ecount */]();
                            }
                        });
                    }
                };
                for (var latency = 1 /* EventLatencyValue.Normal */; latency <= 4 /* EventLatencyValue.Immediate */; latency++) {
                    _loop_1(latency);
                }
                _queueSize = normalQueue;
                _immediateQueueSize = immediateQueue;
            }
            function _queueBatches(latency, sendType, sendReason) {
                var eventsQueued = false;
                var isAsync = sendType === 0 /* EventSendType.Batched */;
                // Only queue batches (to the HttpManager) if this is a sync request or the httpManager has an idle connection
                // Thus keeping the events within the PostChannel until the HttpManager has a connection available
                // This is so we can drop "old" events if the queue is getting full because we can't successfully send events
                if (!isAsync || _httpManager[_DYN_CAN_SEND_REQUEST /* @min:%2ecanSendRequest */]()) {
                    doPerf(_self.core, function () { return "PostChannel._queueBatches"; }, function () {
                        var droppedEvents = [];
                        var latencyToProcess = 4 /* EventLatencyValue.Immediate */;
                        while (latencyToProcess >= latency) {
                            var batchQueue = _batchQueues[latencyToProcess];
                            if (batchQueue && batchQueue.batches && batchQueue.batches[_DYN_LENGTH /* @min:%2elength */] > 0) {
                                arrForEach(batchQueue[_DYN_BATCHES /* @min:%2ebatches */], function (theBatch) {
                                    // Add the batch to the http manager to send the requests
                                    if (!_httpManager.addBatch(theBatch)) {
                                        // The events from this iKey are being dropped (killed)
                                        droppedEvents = droppedEvents[_DYN_CONCAT /* @min:%2econcat */](theBatch[_DYN_EVENTS /* @min:%2eevents */]());
                                    }
                                    else {
                                        eventsQueued = eventsQueued || (theBatch && theBatch[_DYN_COUNT /* @min:%2ecount */]() > 0);
                                    }
                                    if (latencyToProcess === 4 /* EventLatencyValue.Immediate */) {
                                        _immediateQueueSize -= theBatch[_DYN_COUNT /* @min:%2ecount */]();
                                    }
                                    else {
                                        _queueSize -= theBatch[_DYN_COUNT /* @min:%2ecount */]();
                                    }
                                });
                                // Remove all batches from this Queue
                                batchQueue[_DYN_BATCHES /* @min:%2ebatches */] = [];
                                batchQueue.iKeyMap = {};
                            }
                            latencyToProcess--;
                        }
                        if (droppedEvents[_DYN_LENGTH /* @min:%2elength */] > 0) {
                            _notifyEvents(strEventsDiscarded, droppedEvents, EventsDiscardedReason.KillSwitch);
                        }
                        if (eventsQueued && _delayedBatchSendLatency >= latency) {
                            // We have queued events at the same level as the delayed values so clear the setting
                            _delayedBatchSendLatency = -1;
                            _delayedBatchReason = 0 /* SendRequestReason.Undefined */;
                        }
                    }, function () { return ({ latency: latency, sendType: sendType, sendReason: sendReason }); }, !isAsync);
                }
                else {
                    // remember the min latency so that we can re-trigger later
                    _delayedBatchSendLatency = _delayedBatchSendLatency >= 0 ? Math.min(_delayedBatchSendLatency, latency) : latency;
                    _delayedBatchReason = Math.max(_delayedBatchReason, sendReason);
                }
                return eventsQueued;
            }
            /**
             * This is the callback method is called as part of the manual flushing process.
             * @param callback
             * @param sendReason
             */
            function _flushImpl(callback, sendReason) {
                // Add any additional queued events and cause all queued events to be sent asynchronously
                _sendEventsForLatencyAndAbove(1 /* EventLatencyValue.Normal */, 0 /* EventSendType.Batched */, sendReason);
                // All events (should) have been queue -- lets just make sure the queue counts are correct to avoid queue exhaustion (previous bug #9685112)
                _resetQueueCounts();
                _waitForIdleManager(function () {
                    // Only called AFTER the httpManager does not have any outstanding requests
                    if (callback) {
                        callback();
                    }
                    if (_flushCallbackQueue[_DYN_LENGTH /* @min:%2elength */] > 0) {
                        _flushCallbackTimer = _createTimer(function () {
                            _flushCallbackTimer = null;
                            _flushImpl(_flushCallbackQueue.shift(), sendReason);
                        }, 0);
                    }
                    else {
                        // No more flush requests
                        _flushCallbackTimer = null;
                        // Restart the normal timer schedule
                        _scheduleTimer();
                    }
                });
            }
            function _waitForIdleManager(callback) {
                if (_httpManager[_DYN_IS_COMPLETELY_IDLE /* @min:%2eisCompletelyIdle */]()) {
                    callback();
                }
                else {
                    _flushCallbackTimer = _createTimer(function () {
                        _flushCallbackTimer = null;
                        _waitForIdleManager(callback);
                    }, FlushCheckTimer);
                }
            }
            /**
             * Resets the transmit profiles to the default profiles of Real Time, Near Real Time
             * and Best Effort. This removes all the custom profiles that were loaded.
             */
            function _resetTransmitProfiles() {
                _clearScheduledTimer();
                _initializeProfiles();
                _currentProfile = RT_PROFILE;
                _scheduleTimer();
            }
            function _initializeProfiles() {
                _profiles = {};
                _profiles[RT_PROFILE] = [2, 1, 0];
                _profiles[NRT_PROFILE] = [6, 3, 0];
                _profiles[BE_PROFILE] = [18, 9, 0];
            }
            /**
             * The notification handler for requeue events
             * @ignore
             */
            function _requeueEvents(batches, reason) {
                var droppedEvents = [];
                var maxSendAttempts = _maxEventSendAttempts;
                if (_isPageUnloadTriggered) {
                    // If a page unlaod has been triggered reduce the number of times we try to "retry"
                    maxSendAttempts = _maxUnloadEventSendAttempts;
                }
                arrForEach(batches, function (theBatch) {
                    if (theBatch && theBatch[_DYN_COUNT /* @min:%2ecount */]() > 0) {
                        arrForEach(theBatch[_DYN_EVENTS /* @min:%2eevents */](), function (theEvent) {
                            if (theEvent) {
                                // Check if the request being added back is for a sync event in which case mark it no longer a sync event
                                if (theEvent[_DYN_SYNC /* @min:%2esync */]) {
                                    theEvent[_DYN_LATENCY /* @min:%2elatency */] = 4 /* EventLatencyValue.Immediate */;
                                    theEvent[_DYN_SYNC /* @min:%2esync */] = false;
                                }
                                if (theEvent[_DYN_SEND_ATTEMPT /* @min:%2esendAttempt */] < maxSendAttempts) {
                                    // Reset the event timings
                                    setProcessTelemetryTimings(theEvent, _self[_DYN_IDENTIFIER /* @min:%2eidentifier */]);
                                    _addEventToQueues(theEvent, false);
                                }
                                else {
                                    droppedEvents[_DYN_PUSH /* @min:%2epush */](theEvent);
                                }
                            }
                        });
                    }
                });
                if (droppedEvents[_DYN_LENGTH /* @min:%2elength */] > 0) {
                    _notifyEvents(strEventsDiscarded, droppedEvents, EventsDiscardedReason.NonRetryableStatus);
                }
                if (_isPageUnloadTriggered) {
                    // Unload event has been received so we need to try and flush new events
                    _releaseAllQueues(2 /* EventSendType.SendBeacon */, 2 /* SendRequestReason.Unload */);
                }
            }
            function _callNotification(evtName, theArgs) {
                var manager = (_notificationManager || {});
                var notifyFunc = manager[evtName];
                if (notifyFunc) {
                    try {
                        notifyFunc.apply(manager, theArgs);
                    }
                    catch (e) {
                        _throwInternal(_self.diagLog(), 1 /* eLoggingSeverity.CRITICAL */, 74 /* _eInternalMessageId.NotificationException */, evtName + " notification failed: " + e);
                    }
                }
            }
            function _notifyEvents(evtName, theEvents) {
                var extraArgs = [];
                for (var _i = 2; _i < arguments.length; _i++) {
                    extraArgs[_i - 2] = arguments[_i];
                }
                if (theEvents && theEvents[_DYN_LENGTH /* @min:%2elength */] > 0) {
                    _callNotification(evtName, [theEvents][_DYN_CONCAT /* @min:%2econcat */](extraArgs));
                }
            }
            function _notifyBatchEvents(evtName, batches) {
                var extraArgs = [];
                for (var _i = 2; _i < arguments.length; _i++) {
                    extraArgs[_i - 2] = arguments[_i];
                }
                if (batches && batches[_DYN_LENGTH /* @min:%2elength */] > 0) {
                    arrForEach(batches, function (theBatch) {
                        if (theBatch && theBatch[_DYN_COUNT /* @min:%2ecount */]() > 0) {
                            _callNotification(evtName, [theBatch.events()][_DYN_CONCAT /* @min:%2econcat */](extraArgs));
                        }
                    });
                }
            }
            /**
             * The notification handler for when batches are about to be sent
             * @ignore
             */
            function _sendingEvent(batches, reason, isSyncRequest) {
                if (batches && batches[_DYN_LENGTH /* @min:%2elength */] > 0) {
                    _callNotification("eventsSendRequest", [(reason >= 1000 /* EventBatchNotificationReason.SendingUndefined */ && reason <= 1999 /* EventBatchNotificationReason.SendingEventMax */ ?
                            reason - 1000 /* EventBatchNotificationReason.SendingUndefined */ :
                            0 /* SendRequestReason.Undefined */), isSyncRequest !== true]);
                }
            }
            /**
             * This event represents that a batch of events have been successfully sent and a response received
             * @param batches The notification handler for when the batches have been successfully sent
             * @param reason For this event the reason will always be EventBatchNotificationReason.Complete
             */
            function _eventsSentEvent(batches, reason) {
                _notifyBatchEvents("eventsSent", batches, reason);
                // Try and schedule the processing timer if we have events
                _scheduleTimer();
            }
            function _eventsDropped(batches, reason) {
                _notifyBatchEvents(strEventsDiscarded, batches, (reason >= 8000 /* EventBatchNotificationReason.EventsDropped */ && reason <= 8999 /* EventBatchNotificationReason.EventsDroppedMax */ ?
                    reason - 8000 /* EventBatchNotificationReason.EventsDropped */ :
                    EventsDiscardedReason.Unknown));
            }
            function _eventsResponseFail(batches) {
                _notifyBatchEvents(strEventsDiscarded, batches, EventsDiscardedReason.NonRetryableStatus);
                // Try and schedule the processing timer if we have events
                _scheduleTimer();
            }
            function _otherEvent(batches, reason) {
                _notifyBatchEvents(strEventsDiscarded, batches, EventsDiscardedReason.Unknown);
                // Try and schedule the processing timer if we have events
                _scheduleTimer();
            }
            function _setAutoLimits() {
                if (!_disableAutoBatchFlushLimit) {
                    _autoFlushBatchLimit = Math.max(MaxNumberEventPerBatch * (MaxConnections + 1), _queueSizeLimit / 6);
                }
                else {
                    _autoFlushBatchLimit = 0;
                }
            }
        });
        return _this;
    }
// Removed Stub for PostChannel.prototype.initialize.
// Removed Stub for PostChannel.prototype.processTelemetry.
// Removed Stub for PostChannel.prototype.setEventQueueLimits.
// Removed Stub for PostChannel.prototype.pause.
// Removed Stub for PostChannel.prototype.resume.
// Removed Stub for PostChannel.prototype.addResponseHandler.
// Removed Stub for PostChannel.prototype.flush.
// Removed Stub for PostChannel.prototype.setMsaAuthTicket.
// Removed Stub for PostChannel.prototype.hasEvents.
// Removed Stub for PostChannel.prototype._loadTransmitProfiles.
// Removed Stub for PostChannel.prototype._setTransmitProfile.
// Removed Stub for PostChannel.prototype._backOffTransmission.
// Removed Stub for PostChannel.prototype._clearBackOff.
    // This is a workaround for an IE 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.
    PostChannel.__ieDyn=1;

    return PostChannel;
}(BaseTelemetryPlugin));
export { PostChannel };
//# sourceMappingURL=PostChannel.js.map