// @ts-ignore apps resolve this at runtime with path alias in project bundlers
import appConfig from '~/package.json';
export function uptime() {
    return global.android ? org.nativescript.Process.getUpTime() : global.__tns_uptime();
}
export function log(message, ...optionalParams) {
    if (global.__nslog) {
        global.__nslog('CONSOLE LOG: ' + message);
    }
    console.log(message, ...optionalParams);
}
// Use object instead of map as it is a bit faster
const timers = {};
const anyGlobal = global;
const profileNames = [];
export const time = (global.__time || Date.now);
export function start(name) {
    let info = timers[name];
    if (info) {
        info.currentStart = time();
        info.runCount++;
    }
    else {
        info = {
            totalTime: 0,
            count: 0,
            currentStart: time(),
            runCount: 1,
        };
        timers[name] = info;
        profileNames.push(name);
    }
}
export function stop(name) {
    const info = timers[name];
    if (!info) {
        throw new Error(`No timer started: ${name}`);
    }
    if (info.runCount) {
        info.runCount--;
        if (info.runCount) {
            info.count++;
        }
        else {
            info.lastTime = time() - info.currentStart;
            info.totalTime += info.lastTime;
            info.count++;
            info.currentStart = 0;
        }
    }
    else {
        throw new Error(`Timer ${name} paused more times than started.`);
    }
    return info;
}
export function timer(name) {
    return timers[name];
}
export function print(name) {
    const info = timers[name];
    if (!info) {
        throw new Error(`No timer started: ${name}`);
    }
    console.log(`---- [${name}] STOP total: ${info.totalTime} count:${info.count}`);
    return info;
}
export function isRunning(name) {
    const info = timers[name];
    return !!(info && info.runCount);
}
function countersProfileFunctionFactory(fn, name, type = 1 /* MemberType.Instance */) {
    profileNames.push(name);
    return function () {
        start(name);
        try {
            return fn.apply(this, arguments);
        }
        finally {
            stop(name);
        }
    };
}
function timelineProfileFunctionFactory(fn, name, type = 1 /* MemberType.Instance */) {
    return type === 1 /* MemberType.Instance */ ? function () {
        const start = time();
        try {
            return fn.apply(this, arguments);
        }
        finally {
            const end = time();
            console.log(`Timeline: Modules: ${name} ${this}  (${start}ms. - ${end}ms.)`);
        }
    } : function () {
        const start = time();
        try {
            return fn.apply(this, arguments);
        }
        finally {
            const end = time();
            console.log(`Timeline: Modules: ${name}  (${start}ms. - ${end}ms.)`);
        }
    };
}
export var Level;
(function (Level) {
    Level[Level["none"] = 0] = "none";
    Level[Level["lifecycle"] = 1] = "lifecycle";
    Level[Level["timeline"] = 2] = "timeline";
})(Level || (Level = {}));
let tracingLevel = Level.none;
let profileFunctionFactory;
export function enable(mode = 'counters') {
    profileFunctionFactory =
        mode &&
            {
                counters: countersProfileFunctionFactory,
                timeline: timelineProfileFunctionFactory,
            }[mode];
    tracingLevel =
        {
            lifecycle: Level.lifecycle,
            timeline: Level.timeline,
        }[mode] || Level.none;
}
try {
    if (appConfig && appConfig.profiling) {
        enable(appConfig.profiling);
    }
}
catch (e1) {
    try {
        console.log('Profiling startup failed to figure out defaults from package.json, error: ' + e1);
    }
    catch (e2) {
        // We can get here if an exception is thrown in the mksnapshot as there is no console there.
    }
}
export function disable() {
    profileFunctionFactory = undefined;
}
function profileFunction(fn, customName) {
    return profileFunctionFactory(fn, customName || fn.name);
}
const profileMethodUnnamed = (target, key, descriptor) => {
    // save a reference to the original method this way we keep the values currently in the
    // descriptor and don't overwrite what another decorator might have done to the descriptor.
    if (descriptor === undefined) {
        descriptor = Object.getOwnPropertyDescriptor(target, key);
    }
    const originalMethod = descriptor.value;
    let className = '';
    if (target && target.constructor && target.constructor.name) {
        className = target.constructor.name + '.';
    }
    const name = className + key?.toString();
    //editing the descriptor/value parameter
    descriptor.value = profileFunctionFactory(originalMethod, name, 1 /* MemberType.Instance */);
    // return edited descriptor as opposed to overwriting the descriptor
    return descriptor;
};
const profileStaticMethodUnnamed = (ctor, key, descriptor) => {
    // save a reference to the original method this way we keep the values currently in the
    // descriptor and don't overwrite what another decorator might have done to the descriptor.
    if (descriptor === undefined) {
        descriptor = Object.getOwnPropertyDescriptor(ctor, key);
    }
    const originalMethod = descriptor.value;
    let className = '';
    if (ctor && ctor.name) {
        className = ctor.name + '.';
    }
    const name = className + key?.toString();
    //editing the descriptor/value parameter
    descriptor.value = profileFunctionFactory(originalMethod, name, 0 /* MemberType.Static */);
    // return edited descriptor as opposed to overwriting the descriptor
    return descriptor;
};
function profileMethodNamed(name) {
    return (target, key, descriptor) => {
        // save a reference to the original method this way we keep the values currently in the
        // descriptor and don't overwrite what another decorator might have done to the descriptor.
        if (descriptor === undefined) {
            descriptor = Object.getOwnPropertyDescriptor(target, key);
        }
        const originalMethod = descriptor.value;
        //editing the descriptor/value parameter
        descriptor.value = profileFunctionFactory(originalMethod, name);
        // return edited descriptor as opposed to overwriting the descriptor
        return descriptor;
    };
}
const voidMethodDecorator = () => {
    // no op
};
export function profile(nameFnOrTarget, fnOrKey, descriptor, attrs) {
    if (typeof nameFnOrTarget === 'object' && (typeof fnOrKey === 'string' || typeof fnOrKey === 'symbol')) {
        if (!profileFunctionFactory) {
            return;
        }
        return profileMethodUnnamed(nameFnOrTarget, fnOrKey, descriptor);
    }
    else if (typeof nameFnOrTarget === 'function' && (typeof fnOrKey === 'string' || typeof fnOrKey === 'symbol')) {
        if (!profileFunctionFactory) {
            return;
        }
        return profileStaticMethodUnnamed(nameFnOrTarget, fnOrKey, descriptor);
    }
    else if (typeof nameFnOrTarget === 'string' && typeof fnOrKey === 'function') {
        if (!profileFunctionFactory) {
            return fnOrKey;
        }
        return profileFunction(fnOrKey, nameFnOrTarget);
    }
    else if (typeof nameFnOrTarget === 'function') {
        if (!profileFunctionFactory) {
            return nameFnOrTarget;
        }
        return profileFunction(nameFnOrTarget);
    }
    else if (typeof nameFnOrTarget === 'string') {
        if (!profileFunctionFactory) {
            return voidMethodDecorator;
        }
        return profileMethodNamed(nameFnOrTarget);
    }
    else {
        if (!profileFunctionFactory) {
            return voidMethodDecorator;
        }
        return profileMethodUnnamed;
    }
}
export function dumpProfiles() {
    profileNames.forEach(function (name) {
        const info = timers[name];
        if (info) {
            console.log('---- [' + name + '] STOP total: ' + info.totalTime + ' count:' + info.count);
        }
        else {
            console.log('---- [' + name + '] Never called');
        }
    });
}
export function resetProfiles() {
    profileNames.forEach(function (name) {
        const info = timers[name];
        if (info) {
            if (info.runCount) {
                console.log('---- timer with name [' + name + "] is currently running and won't be reset");
            }
            else {
                timers[name] = undefined;
            }
        }
    });
}
export function startCPUProfile(name) {
    if (anyGlobal.android) {
        __startCPUProfiler(name);
    }
}
export function stopCPUProfile(name) {
    if (anyGlobal.android) {
        __stopCPUProfiler(name);
    }
}
export function level() {
    return tracingLevel;
}
export function trace(message, start, end) {
    log(`Timeline: Modules: ${message}  (${start}ms. - ${end}ms.)`);
}
//# sourceMappingURL=index.js.map