import { DEFAULT_VIRTUAL_FILE_METRICS } from "../constants.js";
import { iterateOverFile } from "../utils/iterateOverFile.js";
import { File } from "./File.js";

//#region src/components/VirtualizedFile.ts
let instanceId = -1;
var VirtualizedFile = class extends File {
	__id = `virtualized-file:${++instanceId}`;
	top;
	height = 0;
	heightCache = /* @__PURE__ */ new Map();
	isVisible = false;
	constructor(options, virtualizer, metrics = DEFAULT_VIRTUAL_FILE_METRICS, workerManager, isContainerManaged = false) {
		super(options, workerManager, isContainerManaged);
		this.virtualizer = virtualizer;
		this.metrics = metrics;
	}
	getLineHeight(lineIndex, hasMetadataLine = false) {
		const cached = this.heightCache.get(lineIndex);
		if (cached != null) return cached;
		const multiplier = hasMetadataLine ? 2 : 1;
		return this.metrics.lineHeight * multiplier;
	}
	setOptions(options) {
		if (options == null) return;
		const previousOverflow = this.options.overflow;
		super.setOptions(options);
		if (previousOverflow !== this.options.overflow) {
			this.heightCache.clear();
			this.computeApproximateSize();
			this.renderRange = void 0;
		}
		this.virtualizer.instanceChanged(this);
	}
	reconcileHeights() {
		if (this.fileContainer == null || this.file == null) {
			this.height = 0;
			return;
		}
		const { overflow = "scroll" } = this.options;
		this.top = this.virtualizer.getOffsetInScrollContainer(this.fileContainer);
		if (overflow === "scroll" && this.lineAnnotations.length === 0 && !this.virtualizer.config.resizeDebugging) return;
		let hasLineHeightChange = false;
		if (this.code == null) return;
		const content = this.code.children[1];
		if (!(content instanceof HTMLElement)) return;
		for (const line of content.children) {
			if (!(line instanceof HTMLElement)) continue;
			const lineIndexAttr = line.dataset.lineIndex;
			if (lineIndexAttr == null) continue;
			const lineIndex = Number(lineIndexAttr);
			let measuredHeight = line.getBoundingClientRect().height;
			let hasMetadata = false;
			if (line.nextElementSibling instanceof HTMLElement && ("lineAnnotation" in line.nextElementSibling.dataset || "noNewline" in line.nextElementSibling.dataset)) {
				if ("noNewline" in line.nextElementSibling.dataset) hasMetadata = true;
				measuredHeight += line.nextElementSibling.getBoundingClientRect().height;
			}
			const expectedHeight = this.getLineHeight(lineIndex, hasMetadata);
			if (measuredHeight === expectedHeight) continue;
			hasLineHeightChange = true;
			if (measuredHeight === this.metrics.lineHeight * (hasMetadata ? 2 : 1)) this.heightCache.delete(lineIndex);
			else this.heightCache.set(lineIndex, measuredHeight);
		}
		if (hasLineHeightChange || this.virtualizer.config.resizeDebugging) this.computeApproximateSize();
	}
	onRender = (dirty) => {
		if (this.fileContainer == null || this.file == null) return false;
		if (dirty) this.top = this.virtualizer.getOffsetInScrollContainer(this.fileContainer);
		return this.render({ file: this.file });
	};
	cleanUp() {
		if (this.fileContainer != null) this.virtualizer.disconnect(this.fileContainer);
		super.cleanUp();
	}
	computeApproximateSize() {
		const isFirstCompute = this.height === 0;
		this.height = 0;
		if (this.file == null) return;
		const { disableFileHeader = false, overflow = "scroll" } = this.options;
		const { diffHeaderHeight, fileGap, lineHeight } = this.metrics;
		const lines = this.getOrCreateLineCache(this.file);
		if (!disableFileHeader) this.height += diffHeaderHeight;
		else this.height += fileGap;
		if (overflow === "scroll" && this.lineAnnotations.length === 0) this.height += this.getOrCreateLineCache(this.file).length * lineHeight;
		else iterateOverFile({
			lines,
			callback: ({ lineIndex }) => {
				this.height += this.getLineHeight(lineIndex, false);
			}
		});
		if (lines.length > 0) this.height += fileGap;
		if (this.fileContainer != null && this.virtualizer.config.resizeDebugging && !isFirstCompute) {
			const rect = this.fileContainer.getBoundingClientRect();
			if (rect.height !== this.height) console.log("VirtualizedFile.computeApproximateSize: computed height doesnt match", {
				name: this.file.name,
				elementHeight: rect.height,
				computedHeight: this.height
			});
			else console.log("VirtualizedFile.computeApproximateSize: computed height IS CORRECT");
		}
	}
	setVisibility(visible) {
		if (this.fileContainer == null) return;
		if (visible && !this.isVisible) {
			this.top = this.virtualizer.getOffsetInScrollContainer(this.fileContainer);
			this.isVisible = true;
		} else if (!visible && this.isVisible) {
			this.isVisible = false;
			this.rerender();
		}
	}
	render({ fileContainer, file,...props }) {
		const isFirstRender = this.fileContainer == null;
		this.file ??= file;
		fileContainer = this.getOrCreateFileContainerNode(fileContainer);
		if (this.file == null) {
			console.error("VirtualizedFile.render: attempting to virtually render when we dont have file");
			return false;
		}
		if (isFirstRender) {
			this.computeApproximateSize();
			this.virtualizer.connect(fileContainer, this);
			this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);
			this.isVisible = this.virtualizer.isInstanceVisible(this.top, this.height);
		} else this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);
		if (!this.isVisible) return this.renderPlaceholder(this.height);
		const windowSpecs = this.virtualizer.getWindowSpecs();
		const renderRange = this.computeRenderRangeFromWindow(this.file, this.top, windowSpecs);
		return super.render({
			file: this.file,
			fileContainer,
			renderRange,
			...props
		});
	}
	computeRenderRangeFromWindow(file, fileTop, { top, bottom }) {
		const { disableFileHeader = false, overflow = "scroll" } = this.options;
		const { diffHeaderHeight, fileGap, hunkLineCount, lineHeight } = this.metrics;
		const lines = this.getOrCreateLineCache(file);
		const lineCount = lines.length;
		const fileHeight = this.height;
		const headerRegion = disableFileHeader ? fileGap : diffHeaderHeight;
		if (fileTop < top - fileHeight || fileTop > bottom) return {
			startingLine: 0,
			totalLines: 0,
			bufferBefore: 0,
			bufferAfter: fileHeight - headerRegion - fileGap
		};
		if (lineCount <= hunkLineCount) return {
			startingLine: 0,
			totalLines: hunkLineCount,
			bufferBefore: 0,
			bufferAfter: 0
		};
		const estimatedTargetLines = Math.ceil(Math.max(bottom - top, 0) / lineHeight);
		const totalLines = Math.ceil(estimatedTargetLines / hunkLineCount) * hunkLineCount + hunkLineCount * 2;
		const totalHunks = totalLines / hunkLineCount;
		const viewportCenter = (top + bottom) / 2;
		if (overflow === "scroll" && this.lineAnnotations.length === 0) {
			const centerLine = Math.floor((viewportCenter - (fileTop + headerRegion)) / lineHeight);
			const idealStartHunk$1 = Math.floor(centerLine / hunkLineCount) - Math.floor(totalHunks / 2);
			const totalHunksInFile = Math.ceil(lineCount / hunkLineCount);
			const startingLine$1 = Math.max(0, Math.min(idealStartHunk$1, totalHunksInFile)) * hunkLineCount;
			const clampedTotalLines$1 = idealStartHunk$1 < 0 ? totalLines + idealStartHunk$1 * hunkLineCount : totalLines;
			const bufferBefore$1 = startingLine$1 * lineHeight;
			const renderedLines = Math.min(clampedTotalLines$1, lineCount - startingLine$1);
			return {
				startingLine: startingLine$1,
				totalLines: clampedTotalLines$1,
				bufferBefore: bufferBefore$1,
				bufferAfter: Math.max(0, (lineCount - startingLine$1 - renderedLines) * lineHeight)
			};
		}
		const overflowHunks = totalHunks;
		const hunkOffsets = [];
		let absoluteLineTop = fileTop + headerRegion;
		let currentLine = 0;
		let firstVisibleHunk;
		let centerHunk;
		let overflowCounter;
		iterateOverFile({
			lines,
			callback: ({ lineIndex }) => {
				const isAtHunkBoundary = currentLine % hunkLineCount === 0;
				if (isAtHunkBoundary) {
					hunkOffsets.push(absoluteLineTop - (fileTop + headerRegion));
					if (overflowCounter != null) {
						if (overflowCounter <= 0) return true;
						overflowCounter--;
					}
				}
				const lineHeight$1 = this.getLineHeight(lineIndex, false);
				const currentHunk = Math.floor(currentLine / hunkLineCount);
				if (absoluteLineTop > top - lineHeight$1 && absoluteLineTop < bottom) firstVisibleHunk ??= currentHunk;
				if (absoluteLineTop + lineHeight$1 > viewportCenter) centerHunk ??= currentHunk;
				if (overflowCounter == null && absoluteLineTop >= bottom && isAtHunkBoundary) overflowCounter = overflowHunks;
				currentLine++;
				absoluteLineTop += lineHeight$1;
				return false;
			}
		});
		if (firstVisibleHunk == null) return {
			startingLine: 0,
			totalLines: 0,
			bufferBefore: 0,
			bufferAfter: fileHeight - headerRegion - fileGap
		};
		const collectedHunks = hunkOffsets.length;
		centerHunk ??= firstVisibleHunk;
		const idealStartHunk = Math.round(centerHunk - totalHunks / 2);
		const maxStartHunk = Math.max(0, collectedHunks - totalHunks);
		const startHunk = Math.max(0, Math.min(idealStartHunk, maxStartHunk));
		const startingLine = startHunk * hunkLineCount;
		const clampedTotalLines = idealStartHunk < 0 ? totalLines + idealStartHunk * hunkLineCount : totalLines;
		const bufferBefore = hunkOffsets[startHunk] ?? 0;
		const finalHunkIndex = startHunk + clampedTotalLines / hunkLineCount;
		return {
			startingLine,
			totalLines: clampedTotalLines,
			bufferBefore,
			bufferAfter: finalHunkIndex < hunkOffsets.length ? fileHeight - headerRegion - hunkOffsets[finalHunkIndex] - fileGap : fileHeight - (absoluteLineTop - fileTop) - fileGap
		};
	}
};

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