import { br as PrecisionDate, p as AbstractEngine, bs as IsDocumentAvailable, E as EngineStore } from './index-BvJBnIUK.esm.js';

/**
 * Performance monitor tracks rolling average frame-time and frame-time variance over a user defined sliding-window
 */
class PerformanceMonitor {
    /**
     * constructor
     * @param frameSampleSize The number of samples required to saturate the sliding window
     */
    constructor(frameSampleSize = 30) {
        this._enabled = true;
        this._rollingFrameTime = new RollingAverage(frameSampleSize);
    }
    /**
     * Samples current frame
     * @param timeMs A timestamp in milliseconds of the current frame to compare with other frames
     */
    sampleFrame(timeMs = PrecisionDate.Now) {
        if (!this._enabled) {
            return;
        }
        if (this._lastFrameTimeMs != null) {
            const dt = timeMs - this._lastFrameTimeMs;
            this._rollingFrameTime.add(dt);
        }
        this._lastFrameTimeMs = timeMs;
    }
    /**
     * Returns the average frame time in milliseconds over the sliding window (or the subset of frames sampled so far)
     */
    get averageFrameTime() {
        return this._rollingFrameTime.average;
    }
    /**
     * Returns the variance frame time in milliseconds over the sliding window (or the subset of frames sampled so far)
     */
    get averageFrameTimeVariance() {
        return this._rollingFrameTime.variance;
    }
    /**
     * Returns the frame time of the most recent frame
     */
    get instantaneousFrameTime() {
        return this._rollingFrameTime.history(0);
    }
    /**
     * Returns the average framerate in frames per second over the sliding window (or the subset of frames sampled so far)
     */
    get averageFPS() {
        return 1000.0 / this._rollingFrameTime.average;
    }
    /**
     * Returns the average framerate in frames per second using the most recent frame time
     */
    get instantaneousFPS() {
        const history = this._rollingFrameTime.history(0);
        if (history === 0) {
            return 0;
        }
        return 1000.0 / history;
    }
    /**
     * Returns true if enough samples have been taken to completely fill the sliding window
     */
    get isSaturated() {
        return this._rollingFrameTime.isSaturated();
    }
    /**
     * Enables contributions to the sliding window sample set
     */
    enable() {
        this._enabled = true;
    }
    /**
     * Disables contributions to the sliding window sample set
     * Samples will not be interpolated over the disabled period
     */
    disable() {
        this._enabled = false;
        //clear last sample to avoid interpolating over the disabled period when next enabled
        this._lastFrameTimeMs = null;
    }
    /**
     * Returns true if sampling is enabled
     */
    get isEnabled() {
        return this._enabled;
    }
    /**
     * Resets performance monitor
     */
    reset() {
        //clear last sample to avoid interpolating over the disabled period when next enabled
        this._lastFrameTimeMs = null;
        //wipe record
        this._rollingFrameTime.reset();
    }
}
/**
 * RollingAverage
 *
 * Utility to efficiently compute the rolling average and variance over a sliding window of samples
 */
class RollingAverage {
    /**
     * constructor
     * @param length The number of samples required to saturate the sliding window
     */
    constructor(length) {
        this._samples = new Array(length);
        this.reset();
    }
    /**
     * Adds a sample to the sample set
     * @param v The sample value
     */
    add(v) {
        //http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
        let delta;
        //we need to check if we've already wrapped round
        if (this.isSaturated()) {
            //remove bottom of stack from mean
            const bottomValue = this._samples[this._pos];
            delta = bottomValue - this.average;
            this.average -= delta / (this._sampleCount - 1);
            this._m2 -= delta * (bottomValue - this.average);
        }
        else {
            this._sampleCount++;
        }
        //add new value to mean
        delta = v - this.average;
        this.average += delta / this._sampleCount;
        this._m2 += delta * (v - this.average);
        //set the new variance
        this.variance = this._m2 / (this._sampleCount - 1);
        this._samples[this._pos] = v;
        this._pos++;
        this._pos %= this._samples.length; //positive wrap around
    }
    /**
     * Returns previously added values or null if outside of history or outside the sliding window domain
     * @param i Index in history. For example, pass 0 for the most recent value and 1 for the value before that
     * @returns Value previously recorded with add() or null if outside of range
     */
    history(i) {
        if (i >= this._sampleCount || i >= this._samples.length) {
            return 0;
        }
        const i0 = this._wrapPosition(this._pos - 1.0);
        return this._samples[this._wrapPosition(i0 - i)];
    }
    /**
     * Returns true if enough samples have been taken to completely fill the sliding window
     * @returns true if sample-set saturated
     */
    isSaturated() {
        return this._sampleCount >= this._samples.length;
    }
    /**
     * Resets the rolling average (equivalent to 0 samples taken so far)
     */
    reset() {
        this.average = 0;
        this.variance = 0;
        this._sampleCount = 0;
        this._pos = 0;
        this._m2 = 0;
    }
    /**
     * Wraps a value around the sample range boundaries
     * @param i Position in sample range, for example if the sample length is 5, and i is -3, then 2 will be returned.
     * @returns Wrapped position in sample range
     */
    _wrapPosition(i) {
        const max = this._samples.length;
        return ((i % max) + max) % max;
    }
}

/** @internal */
function DisableTouchAction(canvas) {
    if (!canvas || !canvas.setAttribute) {
        return;
    }
    canvas.setAttribute("touch-action", "none");
    canvas.style.touchAction = "none";
    canvas.style.webkitTapHighlightColor = "transparent";
}
/** @internal */
function _CommonInit(commonEngine, canvas, creationOptions) {
    commonEngine._onCanvasFocus = () => {
        commonEngine.onCanvasFocusObservable.notifyObservers(commonEngine);
    };
    commonEngine._onCanvasBlur = () => {
        commonEngine.onCanvasBlurObservable.notifyObservers(commonEngine);
    };
    commonEngine._onCanvasContextMenu = (evt) => {
        if (commonEngine.disableContextMenu) {
            evt.preventDefault();
        }
    };
    canvas.addEventListener("focus", commonEngine._onCanvasFocus);
    canvas.addEventListener("blur", commonEngine._onCanvasBlur);
    canvas.addEventListener("contextmenu", commonEngine._onCanvasContextMenu);
    commonEngine._onBlur = () => {
        if (commonEngine.disablePerformanceMonitorInBackground) {
            commonEngine.performanceMonitor.disable();
        }
        commonEngine._windowIsBackground = true;
    };
    commonEngine._onFocus = () => {
        if (commonEngine.disablePerformanceMonitorInBackground) {
            commonEngine.performanceMonitor.enable();
        }
        commonEngine._windowIsBackground = false;
    };
    commonEngine._onCanvasPointerOut = (ev) => {
        // Check that the element at the point of the pointer out isn't the canvas and if it isn't, notify observers
        // Note: This is a workaround for a bug with Safari
        if (document.elementFromPoint(ev.clientX, ev.clientY) !== canvas) {
            commonEngine.onCanvasPointerOutObservable.notifyObservers(ev);
        }
    };
    const hostWindow = commonEngine.getHostWindow(); // it calls IsWindowObjectExist()
    if (hostWindow && typeof hostWindow.addEventListener === "function") {
        hostWindow.addEventListener("blur", commonEngine._onBlur);
        hostWindow.addEventListener("focus", commonEngine._onFocus);
    }
    canvas.addEventListener("pointerout", commonEngine._onCanvasPointerOut);
    if (!creationOptions.doNotHandleTouchAction) {
        DisableTouchAction(canvas);
    }
    // Create Audio Engine if needed.
    if (!AbstractEngine.audioEngine && creationOptions.audioEngine && AbstractEngine.AudioEngineFactory) {
        AbstractEngine.audioEngine = AbstractEngine.AudioEngineFactory(commonEngine.getRenderingCanvas(), commonEngine.getAudioContext(), commonEngine.getAudioDestination());
    }
    if (IsDocumentAvailable()) {
        // Fullscreen
        commonEngine._onFullscreenChange = () => {
            commonEngine.isFullscreen = !!document.fullscreenElement;
            // Pointer lock
            if (commonEngine.isFullscreen && commonEngine._pointerLockRequested && canvas) {
                RequestPointerlock(canvas);
            }
        };
        document.addEventListener("fullscreenchange", commonEngine._onFullscreenChange, false);
        document.addEventListener("webkitfullscreenchange", commonEngine._onFullscreenChange, false);
        // Pointer lock
        commonEngine._onPointerLockChange = () => {
            commonEngine.isPointerLock = document.pointerLockElement === canvas;
        };
        document.addEventListener("pointerlockchange", commonEngine._onPointerLockChange, false);
        document.addEventListener("webkitpointerlockchange", commonEngine._onPointerLockChange, false);
    }
    commonEngine.enableOfflineSupport = AbstractEngine.OfflineProviderFactory !== undefined;
    commonEngine._deterministicLockstep = !!creationOptions.deterministicLockstep;
    commonEngine._lockstepMaxSteps = creationOptions.lockstepMaxSteps || 0;
    commonEngine._timeStep = creationOptions.timeStep || 1 / 60;
}
/** @internal */
function _CommonDispose(commonEngine, canvas) {
    // Release audio engine
    if (EngineStore.Instances.length === 1 && AbstractEngine.audioEngine) {
        AbstractEngine.audioEngine.dispose();
        AbstractEngine.audioEngine = null;
    }
    // Events
    const hostWindow = commonEngine.getHostWindow(); // it calls IsWindowObjectExist()
    if (hostWindow && typeof hostWindow.removeEventListener === "function") {
        hostWindow.removeEventListener("blur", commonEngine._onBlur);
        hostWindow.removeEventListener("focus", commonEngine._onFocus);
    }
    if (canvas) {
        canvas.removeEventListener("focus", commonEngine._onCanvasFocus);
        canvas.removeEventListener("blur", commonEngine._onCanvasBlur);
        canvas.removeEventListener("pointerout", commonEngine._onCanvasPointerOut);
        canvas.removeEventListener("contextmenu", commonEngine._onCanvasContextMenu);
    }
    if (IsDocumentAvailable()) {
        document.removeEventListener("fullscreenchange", commonEngine._onFullscreenChange);
        document.removeEventListener("mozfullscreenchange", commonEngine._onFullscreenChange);
        document.removeEventListener("webkitfullscreenchange", commonEngine._onFullscreenChange);
        document.removeEventListener("msfullscreenchange", commonEngine._onFullscreenChange);
        document.removeEventListener("pointerlockchange", commonEngine._onPointerLockChange);
        document.removeEventListener("mspointerlockchange", commonEngine._onPointerLockChange);
        document.removeEventListener("mozpointerlockchange", commonEngine._onPointerLockChange);
        document.removeEventListener("webkitpointerlockchange", commonEngine._onPointerLockChange);
    }
}
/**
 * Get Font size information
 * @param font font name
 * @returns an object containing ascent, height and descent
 */
function GetFontOffset(font) {
    const text = document.createElement("span");
    text.textContent = "Hg";
    text.style.font = font;
    const block = document.createElement("div");
    block.style.display = "inline-block";
    block.style.width = "1px";
    block.style.height = "0px";
    block.style.verticalAlign = "bottom";
    const div = document.createElement("div");
    div.style.whiteSpace = "nowrap";
    div.appendChild(text);
    div.appendChild(block);
    document.body.appendChild(div);
    let fontAscent;
    let fontHeight;
    try {
        fontHeight = block.getBoundingClientRect().top - text.getBoundingClientRect().top;
        block.style.verticalAlign = "baseline";
        fontAscent = block.getBoundingClientRect().top - text.getBoundingClientRect().top;
    }
    finally {
        document.body.removeChild(div);
    }
    return { ascent: fontAscent, height: fontHeight, descent: fontHeight - fontAscent };
}
/** @internal */
async function CreateImageBitmapFromSource(engine, imageSource, options) {
    return await new Promise((resolve, reject) => {
        const image = new Image();
        image.onload = () => {
            // eslint-disable-next-line @typescript-eslint/no-floating-promises, github/no-then
            image.decode().then(() => {
                // eslint-disable-next-line @typescript-eslint/no-floating-promises, github/no-then
                engine.createImageBitmap(image, options).then((imageBitmap) => {
                    resolve(imageBitmap);
                });
            });
        };
        image.onerror = () => {
            // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
            reject(`Error loading image ${image.src}`);
        };
        image.src = imageSource;
    });
}
/** @internal */
function ResizeImageBitmap(engine, image, bufferWidth, bufferHeight) {
    const canvas = engine.createCanvas(bufferWidth, bufferHeight);
    const context = canvas.getContext("2d");
    if (!context) {
        throw new Error("Unable to get 2d context for resizeImageBitmap");
    }
    context.drawImage(image, 0, 0);
    // Create VertexData from map data
    // Cast is due to wrong definition in lib.d.ts from ts 1.3 - https://github.com/Microsoft/TypeScript/issues/949
    const buffer = context.getImageData(0, 0, bufferWidth, bufferHeight).data;
    return buffer;
}
/**
 * Ask the browser to promote the current element to fullscreen rendering mode
 * @param element defines the DOM element to promote
 */
function RequestFullscreen(element) {
    const requestFunction = element.requestFullscreen || element.webkitRequestFullscreen;
    if (!requestFunction) {
        return;
    }
    void requestFunction.call(element);
}
/**
 * Asks the browser to exit fullscreen mode
 */
function ExitFullscreen() {
    const anyDoc = document;
    if (document.exitFullscreen) {
        // eslint-disable-next-line @typescript-eslint/no-floating-promises
        document.exitFullscreen();
    }
    else if (anyDoc.webkitCancelFullScreen) {
        anyDoc.webkitCancelFullScreen();
    }
}
/**
 * Ask the browser to promote the current element to pointerlock mode
 * @param element defines the DOM element to promote
 */
function RequestPointerlock(element) {
    if (element.requestPointerLock) {
        // In some browsers, requestPointerLock returns a promise.
        // Handle possible rejections to avoid an unhandled top-level exception.
        const promise = element.requestPointerLock();
        if (promise instanceof Promise) {
            promise
                // eslint-disable-next-line github/no-then
                .then(() => {
                element.focus();
            })
                // eslint-disable-next-line github/no-then
                .catch(() => { });
        }
        else {
            element.focus();
        }
    }
}
/**
 * Asks the browser to exit pointerlock mode
 */
function ExitPointerlock() {
    if (document.exitPointerLock) {
        document.exitPointerLock();
    }
}

/** This file must only contain pure code and pure imports */
let _Registered = false;
/**
 * Register side effects for abstractEngineDom.
 * Safe to call multiple times; only the first call has an effect.
 */
function RegisterAbstractEngineDom() {
    if (_Registered) {
        return;
    }
    _Registered = true;
    AbstractEngine.prototype.getInputElement = function () {
        return this._renderingCanvas;
    };
    AbstractEngine.prototype.getRenderingCanvasClientRect = function () {
        if (!this._renderingCanvas) {
            return null;
        }
        return this._renderingCanvas.getBoundingClientRect();
    };
    AbstractEngine.prototype.getInputElementClientRect = function () {
        if (!this._renderingCanvas) {
            return null;
        }
        return this.getInputElement().getBoundingClientRect();
    };
    AbstractEngine.prototype.getAspectRatio = function (viewportOwner, useScreen = false) {
        const viewport = viewportOwner.viewport;
        return (this.getRenderWidth(useScreen) * viewport.width) / (this.getRenderHeight(useScreen) * viewport.height);
    };
    AbstractEngine.prototype.getScreenAspectRatio = function () {
        return this.getRenderWidth(true) / this.getRenderHeight(true);
    };
    AbstractEngine.prototype._verifyPointerLock = function () {
        this._onPointerLockChange?.();
    };
}

export { CreateImageBitmapFromSource as C, ExitFullscreen as E, GetFontOffset as G, PerformanceMonitor as P, RegisterAbstractEngineDom as R, _CommonInit as _, ResizeImageBitmap as a, RequestFullscreen as b, RequestPointerlock as c, ExitPointerlock as d, _CommonDispose as e };
//# sourceMappingURL=abstractEngine.dom.pure-Bni7lnbD.esm.js.map
