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


import { ObjAssign, ObjClass } from "@microsoft/applicationinsights-shims";
import { arrForEach, asString as asString21, isArray, isBoolean, isError, isFunction, isNullOrUndefined, isObject, isPlainObject, isString, isUndefined, objDeepFreeze, objDefine, objForEachKey, objHasOwn, strIndexOf } from "@nevware21/ts-utils";
import { _DYN_APPLY, _DYN_LENGTH, _DYN_NAME, _DYN_REPLACE } from "../__DynamicConstants";
import { STR_EMPTY } from "./InternalConstants";
// RESTRICT and AVOID circular dependencies you should not import other contained modules or export the contents of this file directly
// Added to help with minification
var strGetPrototypeOf = "getPrototypeOf";
var rCamelCase = /-([a-z])/g;
var rNormalizeInvalid = /([^\w\d_$])/g;
var rLeadingNumeric = /^(\d+[\w\d_$])/;
export var _getObjProto = Object[strGetPrototypeOf];
export function isNotUndefined(value) {
    return !isUndefined(value);
}
export function isNotNullOrUndefined(value) {
    return !isNullOrUndefined(value);
}
/**
 * Validates that the string name conforms to the JS IdentifierName specification and if not
 * normalizes the name so that it would. This method does not identify or change any keywords
 * meaning that if you pass in a known keyword the same value will be returned.
 * This is a simplified version
 * @param name - The name to validate
 */
export function normalizeJsName(name) {
    var value = name;
    if (value && isString(value)) {
        // CamelCase everything after the "-" and remove the dash
        value = value[_DYN_REPLACE /* @min:%2ereplace */](rCamelCase, function (_all, letter) {
            return letter.toUpperCase();
        });
        value = value[_DYN_REPLACE /* @min:%2ereplace */](rNormalizeInvalid, "_");
        value = value[_DYN_REPLACE /* @min:%2ereplace */](rLeadingNumeric, function (_all, match) {
            return "_" + match;
        });
    }
    return value;
}
/**
 * A simple wrapper (for minification support) to check if the value contains the search string.
 * @param value - The string value to check for the existence of the search value
 * @param search - The value search within the value
 */
export function strContains(value, search) {
    if (value && search) {
        return strIndexOf(value, search) !== -1;
    }
    return false;
}
/**
 * Convert a date to I.S.O. format in IE8
 */
export function toISOString(date) {
    return date && date.toISOString() || "";
}
export var deepFreeze = objDeepFreeze;
/**
 * Returns the name of object if it's an Error. Otherwise, returns empty string.
 */
export function getExceptionName(object) {
    if (isError(object)) {
        return object[_DYN_NAME /* @min:%2ename */];
    }
    return STR_EMPTY;
}
/**
 * Sets the provided value on the target instance using the field name when the provided chk function returns true, the chk
 * function will only be called if the new value is no equal to the original value.
 * @param target - The target object
 * @param field - The key of the target
 * @param value - The value to set
 * @param valChk - [Optional] Callback to check the value that if supplied will be called check if the new value can be set
 * @param srcChk - [Optional] Callback to check to original value that if supplied will be called if the new value should be set (if allowed)
 * @returns The existing or new value, depending what was set
 */
export function setValue(target, field, value, valChk, srcChk) {
    var theValue = value;
    if (target) {
        theValue = target[field];
        if (theValue !== value && (!srcChk || srcChk(theValue)) && (!valChk || valChk(value))) {
            theValue = value;
            target[field] = theValue;
        }
    }
    return theValue;
}
/**
 * Returns the current value from the target object if not null or undefined otherwise sets the new value and returns it
 * @param target - The target object to return or set the default value
 * @param field - The key for the field to set on the target
 * @param defValue - [Optional] The value to set if not already present, when not provided a empty object will be added
 */
export function getSetValue(target, field, defValue) {
    var theValue;
    if (target) {
        theValue = target[field];
        if (!theValue && isNullOrUndefined(theValue)) {
            // Supports having the default as null
            theValue = !isUndefined(defValue) ? defValue : {};
            target[field] = theValue;
        }
    }
    else {
        // Expanded for performance so we only check defValue if required
        theValue = !isUndefined(defValue) ? defValue : {};
    }
    return theValue;
}
function _createProxyFunction(source, funcName) {
    var srcFunc = null;
    var src = null;
    if (isFunction(source)) {
        srcFunc = source;
    }
    else {
        src = source;
    }
    return function () {
        // Capture the original arguments passed to the method
        var originalArguments = arguments;
        if (srcFunc) {
            src = srcFunc();
        }
        if (src) {
            return src[funcName][_DYN_APPLY /* @min:%2eapply */](src, originalArguments);
        }
    };
}
/**
 * Effectively assigns all enumerable properties (not just own properties) and functions (including inherited prototype) from
 * the source object to the target, it attempts to use proxy getters / setters (if possible) and proxy functions to avoid potential
 * implementation issues by assigning prototype functions as instance ones
 *
 * This method is the primary method used to "update" the snippet proxy with the ultimate implementations.
 *
 * Special ES3 Notes:
 * Updates (setting) of direct property values on the target or indirectly on the source object WILL NOT WORK PROPERLY, updates to the
 * properties of "referenced" object will work (target.context.newValue = 10 => will be reflected in the source.context as it's the
 * same object). ES3 Failures: assigning target.myProp = 3 -> Won't change source.myProp = 3, likewise the reverse would also fail.
 * @param target - The target object to be assigned with the source properties and functions
 * @param source - The source object which will be assigned / called by setting / calling the targets proxies
 * @param chkSet - An optional callback to determine whether a specific property/function should be proxied
 */
export function proxyAssign(target, source, chkSet) {
    if (target && source && isObject(target) && isObject(source)) {
        var _loop_1 = function (field) {
            if (isString(field)) {
                var value = source[field];
                if (isFunction(value)) {
                    if (!chkSet || chkSet(field, true, source, target)) {
                        // Create a proxy function rather than just copying the (possible) prototype to the new object as an instance function
                        target[field] = _createProxyFunction(source, field);
                    }
                }
                else if (!chkSet || chkSet(field, false, source, target)) {
                    if (objHasOwn(target, field)) {
                        // Remove any previous instance property
                        delete target[field];
                    }
                    objDefine(target, field, {
                        g: function () {
                            return source[field];
                        },
                        s: function (theValue) {
                            source[field] = theValue;
                        }
                    });
                }
            }
        };
        // effectively apply/proxy full source to the target instance
        for (var field in source) {
            _loop_1(field);
        }
    }
    return target;
}
/**
 * Creates a proxy function on the target which internally will call the source version with all arguments passed to the target method.
 *
 * @param target - The target object to be assigned with the source properties and functions
 * @param name - The function name that will be added on the target
 * @param source - The source object which will be assigned / called by setting / calling the targets proxies
 * @param theFunc - The function name on the source that will be proxied on the target
 * @param overwriteTarget - If `false` this will not replace any pre-existing name otherwise (the default) it will overwrite any existing name
 */
export function proxyFunctionAs(target, name, source, theFunc, overwriteTarget) {
    if (target && name && source) {
        if (overwriteTarget !== false || isUndefined(target[name])) {
            target[name] = _createProxyFunction(source, theFunc);
        }
    }
}
/**
 * Creates proxy functions on the target which internally will call the source version with all arguments passed to the target method.
 *
 * @param target - The target object to be assigned with the source properties and functions
 * @param source - The source object which will be assigned / called by setting / calling the targets proxies
 * @param functionsToProxy - An array of function names that will be proxied on the target
 * @param overwriteTarget - If false this will not replace any pre-existing name otherwise (the default) it will overwrite any existing name
 */
export function proxyFunctions(target, source, functionsToProxy, overwriteTarget) {
    if (target && source && isObject(target) && isArray(functionsToProxy)) {
        arrForEach(functionsToProxy, function (theFuncName) {
            if (isString(theFuncName)) {
                proxyFunctionAs(target, theFuncName, source, theFuncName, overwriteTarget);
            }
        });
    }
    return target;
}
/**
 * Simpler helper to create a dynamic class that implements the interface and populates the values with the defaults.
 * Only instance properties (hasOwnProperty) values are copied from the defaults to the new instance
 * @param defaults - Simple helper
 */
export function createClassFromInterface(defaults) {
    return /** @class */ (function () {
        function class_1() {
            var _this = this;
            if (defaults) {
                objForEachKey(defaults, function (field, value) {
                    _this[field] = value;
                });
            }
        }
        return class_1;
    }());
}
/**
 * A helper function to assist with JIT performance for objects that have properties added / removed dynamically
 * this is primarily for chromium based browsers and has limited effects on Firefox and none of IE. Only call this
 * function after you have finished "updating" the object, calling this within loops reduces or defeats the benefits.
 * This helps when iterating using for..in, objKeys() and objForEach()
 * @param theObject - The object to be optimized if possible
 */
export function optimizeObject(theObject) {
    // V8 Optimization to cause the JIT compiler to create a new optimized object for looking up the own properties
    // primarily for object with <= 19 properties for >= 20 the effect is reduced or non-existent
    if (theObject && ObjAssign) {
        theObject = ObjClass(ObjAssign({}, theObject));
    }
    return theObject;
}
export function objExtend(obj1, obj2, obj3, obj4, obj5, obj6) {
    // Variables
    var theArgs = arguments;
    var extended = theArgs[0] || {};
    var argLen = theArgs[_DYN_LENGTH /* @min:%2elength */];
    var deep = false;
    var idx = 1;
    // Check for "Deep" flag
    if (argLen > 0 && isBoolean(extended)) {
        deep = extended;
        extended = theArgs[idx] || {};
        idx++;
    }
    // Handle case when target is a string or something (possible in deep copy)
    if (!isObject(extended)) {
        extended = {};
    }
    // Loop through each remaining object and conduct a merge
    for (; idx < argLen; idx++) {
        var arg = theArgs[idx];
        var isArgArray = isArray(arg);
        var isArgObj = isObject(arg);
        for (var prop in arg) {
            var propOk = (isArgArray && (prop in arg)) || (isArgObj && objHasOwn(arg, prop));
            if (!propOk) {
                continue;
            }
            var newValue = arg[prop];
            var isNewArray = void 0;
            // If deep merge and property is an object, merge properties
            if (deep && newValue && ((isNewArray = isArray(newValue)) || isPlainObject(newValue))) {
                // Grab the current value of the extended object
                var clone = extended[prop];
                if (isNewArray) {
                    if (!isArray(clone)) {
                        // We can't "merge" an array with a non-array so overwrite the original
                        clone = [];
                    }
                }
                else if (!isPlainObject(clone)) {
                    // We can't "merge" an object with a non-object
                    clone = {};
                }
                // Never move the original objects always clone them
                newValue = objExtend(deep, clone, newValue);
            }
            // Assign the new (or previous) value (unless undefined)
            if (newValue !== undefined) {
                extended[prop] = newValue;
            }
        }
    }
    return extended;
}
export var asString = asString21;
export function isFeatureEnabled(feature, cfg) {
    var rlt = false;
    var ft = cfg && cfg.featureOptIn && cfg.featureOptIn[feature];
    if (feature && ft) {
        var mode = ft.mode;
        // NOTE: None will be considered as true
        rlt = (mode == 3 /* FeatureOptInMode.enable */) || (mode == 1 /* FeatureOptInMode.none */);
    }
    return rlt;
}
//# sourceMappingURL=HelperFuncs.js.map