import { logger } from "./logger.js";

//#region src/preview/AdaptiveResolutionTracker.ts
/**
* Available resolution scale steps for adaptive scaling.
* Finer-grained steps (5% increments) for smoother adaptation.
* Ordered from highest to lowest quality.
*/
const SCALE_STEPS = [
	1,
	.95,
	.9,
	.85,
	.8,
	.75,
	.7,
	.65,
	.6,
	.55,
	.5,
	.45,
	.4,
	.35,
	.3,
	.25,
	.2,
	.15,
	.1
];
/**
* Timing thresholds
*
* Target: 30fps = 33.33ms per frame
* Tolerate down to 15fps (half target) before scaling down.
* Scale up when we have plenty of headroom.
*/
const TARGET_FRAME_TIME_MS = 33.33;
const SCALE_DOWN_THRESHOLD_MS = 66.67;
const SCALE_UP_THRESHOLD_MS = 25;
const ROLLING_WINDOW_SIZE = 30;
const MIN_SCALE_CHANGE_INTERVAL_MS = 2e3;
const SCALE_UP_STABILITY_SAMPLES = 60;
/** Size of the pressure history for histogram display */
const PRESSURE_HISTORY_SIZE = 60;
/**
* Tracks render time to recommend optimal preview resolution.
*/
var AdaptiveResolutionTracker = class {
	constructor(options) {
		this.renderTimes = [];
		this.currentScaleIndex = 0;
		this.lastScaleChangeTime = 0;
		this.samplesAtCurrentScale = 0;
		this.pressureObserver = null;
		this.pressureState = "nominal";
		this.pressureHistory = [];
		this.lastFrameTime = 0;
		this.frameIntervals = [];
		this.onScaleChange = options?.onScaleChange;
		this.initPressureObserver();
	}
	/**
	* Initialize Compute Pressure API observer if available.
	*/
	initPressureObserver() {
		if (!("PressureObserver" in globalThis)) return;
		try {
			this.pressureObserver = new PressureObserver((records) => {
				if (records.length > 0) {
					const latest = records[records.length - 1];
					this.pressureState = latest.state;
					this.pressureHistory.push(latest.state);
					if (this.pressureHistory.length > PRESSURE_HISTORY_SIZE) this.pressureHistory.shift();
				}
			});
			this.pressureObserver.observe("cpu", { sampleInterval: 500 }).catch(() => {});
		} catch (e) {
			logger.warn("[AdaptiveResolutionTracker] Failed to initialize PressureObserver:", e);
			this.pressureObserver = null;
		}
	}
	/**
	* Record a frame's render time.
	* Call this AFTER each render completes with how long the render took.
	*
	* @param renderTimeMs - How long the render() call took in milliseconds
	* @param timestamp - Optional rAF timestamp for frame interval tracking (display only)
	*/
	recordFrame(renderTimeMs, timestamp) {
		this.renderTimes.push(renderTimeMs);
		if (this.renderTimes.length > ROLLING_WINDOW_SIZE) this.renderTimes.shift();
		this.samplesAtCurrentScale++;
		if (timestamp !== void 0 && this.lastFrameTime > 0) {
			const interval = timestamp - this.lastFrameTime;
			this.frameIntervals.push(interval);
			if (this.frameIntervals.length > ROLLING_WINDOW_SIZE) this.frameIntervals.shift();
		}
		if (timestamp !== void 0) this.lastFrameTime = timestamp;
		this.checkForScaleAdjustment();
	}
	/**
	* Check if we should scale up or down based on render time trends.
	*/
	checkForScaleAdjustment() {
		if (this.renderTimes.length < 10) return;
		if (performance.now() - this.lastScaleChangeTime < MIN_SCALE_CHANGE_INTERVAL_MS) return;
		const avgRenderTime = this.renderTimes.reduce((a, b) => a + b, 0) / this.renderTimes.length;
		if (avgRenderTime > SCALE_DOWN_THRESHOLD_MS) {
			this.scaleDown("slow");
			return;
		}
		if (this.pressureState === "critical" || this.pressureState === "serious") {
			this.scaleDown("pressure");
			return;
		}
		const pressureOk = this.pressureState === "nominal" || this.pressureState === "fair";
		if (avgRenderTime < SCALE_UP_THRESHOLD_MS && this.samplesAtCurrentScale >= SCALE_UP_STABILITY_SAMPLES && pressureOk) this.scaleUp();
	}
	/**
	* Decrease resolution (increase scale index).
	*/
	scaleDown(reason) {
		if (this.currentScaleIndex < SCALE_STEPS.length - 1) {
			this.currentScaleIndex++;
			this.lastScaleChangeTime = performance.now();
			this.samplesAtCurrentScale = 0;
			this.renderTimes = [];
			const newScale = SCALE_STEPS[this.currentScaleIndex];
			logger.debug(`[AdaptiveResolutionTracker] Scaling DOWN to ${(newScale * 100).toFixed(0)}% (reason: ${reason})`);
			this.onScaleChange?.(newScale);
		}
	}
	/**
	* Increase resolution (decrease scale index).
	*/
	scaleUp() {
		if (this.currentScaleIndex > 0) {
			this.currentScaleIndex--;
			this.lastScaleChangeTime = performance.now();
			this.samplesAtCurrentScale = 0;
			this.renderTimes = [];
			const newScale = SCALE_STEPS[this.currentScaleIndex];
			logger.debug(`[AdaptiveResolutionTracker] Scaling UP to ${(newScale * 100).toFixed(0)}% (reason: stable performance)`);
			this.onScaleChange?.(newScale);
		}
	}
	/**
	* Get the current recommended scale factor.
	*/
	getRecommendedScale() {
		return SCALE_STEPS[this.currentScaleIndex];
	}
	/**
	* Get current statistics for display.
	*/
	getStats() {
		const avgRenderTime = this.renderTimes.length > 0 ? this.renderTimes.reduce((a, b) => a + b, 0) / this.renderTimes.length : 0;
		const avgFrameInterval = this.frameIntervals.length > 0 ? this.frameIntervals.reduce((a, b) => a + b, 0) / this.frameIntervals.length : 16.67;
		const fps = avgFrameInterval > 0 ? 1e3 / avgFrameInterval : 0;
		const canChange = performance.now() - this.lastScaleChangeTime >= MIN_SCALE_CHANGE_INTERVAL_MS;
		const pressureOk = this.pressureState === "nominal" || this.pressureState === "fair";
		const canScaleUp = canChange && this.currentScaleIndex > 0 && avgRenderTime < SCALE_UP_THRESHOLD_MS && this.samplesAtCurrentScale >= SCALE_UP_STABILITY_SAMPLES && pressureOk;
		const canScaleDown = canChange && this.currentScaleIndex < SCALE_STEPS.length - 1;
		const headroom = TARGET_FRAME_TIME_MS - avgRenderTime;
		return {
			currentScale: this.getRecommendedScale(),
			avgRenderTime,
			fps,
			pressureState: this.pressureState,
			pressureHistory: [...this.pressureHistory],
			samplesAtCurrentScale: this.samplesAtCurrentScale,
			canScaleUp,
			canScaleDown,
			headroom
		};
	}
	/**
	* Reset the tracker state.
	*/
	reset() {
		this.lastFrameTime = 0;
		this.frameIntervals = [];
		this.renderTimes = [];
		this.currentScaleIndex = 0;
		this.lastScaleChangeTime = 0;
		this.samplesAtCurrentScale = 0;
	}
	/**
	* Initialize the tracker to start at a specific scale.
	*/
	initializeAtScale(targetScale) {
		let bestIndex = 0;
		for (let i = 0; i < SCALE_STEPS.length; i++) if (SCALE_STEPS[i] <= targetScale) {
			bestIndex = i;
			break;
		}
		this.currentScaleIndex = bestIndex;
		this.lastFrameTime = 0;
		this.frameIntervals = [];
		this.renderTimes = [];
		this.lastScaleChangeTime = 0;
		this.samplesAtCurrentScale = 0;
		logger.debug(`[AdaptiveResolutionTracker] Initialized at scale ${(SCALE_STEPS[bestIndex] * 100).toFixed(0)}%`);
	}
	/**
	* Clean up resources.
	*/
	dispose() {
		if (this.pressureObserver) {
			try {
				this.pressureObserver.disconnect();
			} catch {}
			this.pressureObserver = null;
		}
	}
};

//#endregion
export { AdaptiveResolutionTracker };
//# sourceMappingURL=AdaptiveResolutionTracker.js.map