import { DEFAULT_RENDER_RANGE, DEFAULT_THEMES } from "../constants.js";
import { areLanguagesAttached } from "../highlighter/languages/areLanguagesAttached.js";
import { getHighlighterIfLoaded, getSharedHighlighter } from "../highlighter/shared_highlighter.js";
import { getThemes } from "../utils/getThemes.js";
import { areThemesAttached } from "../highlighter/themes/areThemesAttached.js";
import { hasResolvedThemes } from "../highlighter/themes/hasResolvedThemes.js";
import { areRenderRangesEqual } from "../utils/areRenderRangesEqual.js";
import { areThemesEqual } from "../utils/areThemesEqual.js";
import { createGutterGap, createGutterItem, createGutterWrapper, createHastElement } from "../utils/hast_utils.js";
import { createAnnotationElement } from "../utils/createAnnotationElement.js";
import { createContentColumn } from "../utils/createContentColumn.js";
import { createFileHeaderElement } from "../utils/createFileHeaderElement.js";
import { createPreElement } from "../utils/createPreElement.js";
import { getFiletypeFromFileName } from "../utils/getFiletypeFromFileName.js";
import { getHighlighterOptions } from "../utils/getHighlighterOptions.js";
import { getLineAnnotationName } from "../utils/getLineAnnotationName.js";
import { iterateOverFile } from "../utils/iterateOverFile.js";
import { splitFileContents } from "../utils/splitFileContents.js";
import { renderFileWithHighlighter } from "../utils/renderFileWithHighlighter.js";
import { toHtml } from "hast-util-to-html";

//#region src/renderers/FileRenderer.ts
let instanceId = -1;
var FileRenderer = class {
	__id = `file-renderer:${++instanceId}`;
	highlighter;
	renderCache;
	computedLang = "text";
	lineAnnotations = {};
	lineCache;
	constructor(options = { theme: DEFAULT_THEMES }, onRenderUpdate, workerManager) {
		this.options = options;
		this.onRenderUpdate = onRenderUpdate;
		this.workerManager = workerManager;
		if (workerManager?.isWorkingPool() !== true) this.highlighter = areThemesAttached(options.theme ?? DEFAULT_THEMES) ? getHighlighterIfLoaded() : void 0;
	}
	setOptions(options) {
		this.options = options;
	}
	mergeOptions(options) {
		this.options = {
			...this.options,
			...options
		};
	}
	setThemeType(themeType) {
		if ((this.options.themeType ?? "system") === themeType) return;
		this.mergeOptions({ themeType });
	}
	setLineAnnotations(lineAnnotations) {
		this.lineAnnotations = {};
		for (const annotation of lineAnnotations) {
			const arr = this.lineAnnotations[annotation.lineNumber] ?? [];
			this.lineAnnotations[annotation.lineNumber] = arr;
			arr.push(annotation);
		}
	}
	cleanUp() {
		this.renderCache = void 0;
		this.highlighter = void 0;
		this.workerManager = void 0;
		this.onRenderUpdate = void 0;
		this.lineCache = void 0;
	}
	hydrate(file) {
		const { options } = this.getRenderOptions(file);
		let cache = this.workerManager?.getFileResultCache(file);
		if (cache != null && !areRenderOptionsEqual(options, cache.options)) cache = void 0;
		this.renderCache ??= {
			file,
			options,
			highlighted: true,
			result: cache?.result,
			renderRange: void 0
		};
		if (this.workerManager?.isWorkingPool() === true && this.renderCache.result == null) this.workerManager.highlightFileAST(this, file);
		else this.asyncHighlight(file).then(({ result, options: options$1 }) => {
			this.onHighlightSuccess(file, result, options$1);
		});
	}
	getRenderOptions(file) {
		const options = (() => {
			if (this.workerManager?.isWorkingPool() === true) return this.workerManager.getFileRenderOptions();
			const { theme = DEFAULT_THEMES, tokenizeMaxLineLength = 1e3 } = this.options;
			return {
				theme,
				tokenizeMaxLineLength
			};
		})();
		const { renderCache } = this;
		if (renderCache?.result == null) return {
			options,
			forceRender: true
		};
		if (file !== renderCache.file || !areRenderOptionsEqual(options, renderCache.options)) return {
			options,
			forceRender: true
		};
		return {
			options,
			forceRender: false
		};
	}
	getOrCreateLineCache(file) {
		if (file.cacheKey == null) {
			this.lineCache = void 0;
			return splitFileContents(file.contents);
		}
		let { lineCache } = this;
		if (lineCache == null || lineCache.cacheKey !== file.cacheKey) lineCache = {
			cacheKey: file.cacheKey,
			lines: splitFileContents(file.contents)
		};
		this.lineCache = lineCache;
		return lineCache.lines;
	}
	renderFile(file = this.renderCache?.file, renderRange = DEFAULT_RENDER_RANGE) {
		if (file == null) return;
		const cache = this.workerManager?.getFileResultCache(file);
		if (cache != null && this.renderCache == null) this.renderCache = {
			file,
			highlighted: true,
			renderRange: void 0,
			...cache
		};
		const { options, forceRender } = this.getRenderOptions(file);
		this.renderCache ??= {
			file,
			highlighted: false,
			options,
			result: void 0,
			renderRange: void 0
		};
		if (this.workerManager?.isWorkingPool() === true) {
			if (this.renderCache.result == null || !this.renderCache.highlighted && !areRenderRangesEqual(this.renderCache.renderRange, renderRange)) {
				this.renderCache.result = this.workerManager.getPlainFileAST(file, renderRange.startingLine, renderRange.totalLines, this.getOrCreateLineCache(file));
				this.renderCache.renderRange = renderRange;
			}
			if (renderRange.totalLines > 0 && (!this.renderCache.highlighted || forceRender)) this.workerManager.highlightFileAST(this, file);
		} else {
			this.computedLang = file.lang ?? getFiletypeFromFileName(file.name);
			const hasThemes = this.highlighter != null && areThemesAttached(options.theme);
			const hasLangs = this.highlighter != null && areLanguagesAttached(this.computedLang);
			if (this.highlighter != null && hasThemes && (forceRender || !this.renderCache.highlighted && hasLangs || this.renderCache.result == null)) {
				const { result, options: options$1 } = this.renderFileWithHighlighter(file, this.highlighter, !hasLangs);
				this.renderCache = {
					file,
					options: options$1,
					highlighted: hasLangs,
					result,
					renderRange: void 0
				};
			}
			if (!hasThemes || !hasLangs) this.asyncHighlight(file).then(({ result, options: options$1 }) => {
				this.onHighlightSuccess(file, result, options$1);
			});
		}
		return this.renderCache.result != null ? this.processFileResult(this.renderCache.file, renderRange, this.renderCache.result) : void 0;
	}
	async asyncRender(file, renderRange = DEFAULT_RENDER_RANGE) {
		const { result } = await this.asyncHighlight(file);
		return this.processFileResult(file, renderRange, result);
	}
	async asyncHighlight(file) {
		this.computedLang = file.lang ?? getFiletypeFromFileName(file.name);
		const hasThemes = this.highlighter != null && hasResolvedThemes(getThemes(this.options.theme));
		const hasLangs = this.highlighter != null && areLanguagesAttached(this.computedLang);
		if (this.highlighter == null || !hasThemes || !hasLangs) this.highlighter = await this.initializeHighlighter();
		return this.renderFileWithHighlighter(file, this.highlighter);
	}
	renderFileWithHighlighter(file, highlighter, forcePlainText = false) {
		const { options } = this.getRenderOptions(file);
		return {
			result: renderFileWithHighlighter(file, highlighter, options, { forcePlainText }),
			options
		};
	}
	processFileResult(file, renderRange, { code, themeStyles, baseThemeType }) {
		const { disableFileHeader = false } = this.options;
		const contentArray = [];
		const gutter = createGutterWrapper();
		const lines = this.getOrCreateLineCache(file);
		let rowCount = 0;
		iterateOverFile({
			lines,
			startingLine: renderRange.startingLine,
			totalLines: renderRange.totalLines,
			callback: ({ lineIndex, lineNumber }) => {
				const line = code[lineIndex];
				if (line == null) {
					const message = "FileRenderer.processFileResult: Line doesnt exist";
					console.error(message, {
						name: file.name,
						lineIndex,
						lineNumber,
						lines
					});
					throw new Error(message);
				}
				if (line != null) {
					gutter.children.push(createGutterItem("context", lineNumber, `${lineIndex}`));
					contentArray.push(line);
					rowCount++;
					const annotations = this.lineAnnotations[lineNumber];
					if (annotations != null) {
						gutter.children.push(createGutterGap("context", "annotation", 1));
						contentArray.push(createAnnotationElement({
							type: "annotation",
							hunkIndex: 0,
							lineIndex: lineNumber,
							annotations: annotations.map((annotation) => getLineAnnotationName(annotation))
						}));
						rowCount++;
					}
				}
			}
		});
		gutter.properties.style = `grid-row: span ${rowCount}`;
		return {
			gutterAST: gutter.children ?? [],
			contentAST: contentArray,
			preAST: this.createPreElement(lines.length, themeStyles, baseThemeType),
			headerAST: !disableFileHeader ? this.renderHeader(file, themeStyles, baseThemeType) : void 0,
			totalLines: lines.length,
			rowCount,
			themeStyles,
			baseThemeType,
			bufferBefore: renderRange.bufferBefore,
			bufferAfter: renderRange.bufferAfter,
			css: ""
		};
	}
	renderHeader(file, themeStyles, baseThemeType) {
		const { themeType = "system" } = this.options;
		return createFileHeaderElement({
			fileOrDiff: file,
			themeStyles,
			themeType: baseThemeType ?? themeType
		});
	}
	renderFullHTML(result) {
		return toHtml(this.renderFullAST(result));
	}
	renderFullAST(result, children = []) {
		children.push(createHastElement({
			tagName: "code",
			children: this.renderCodeAST(result),
			properties: { "data-code": "" }
		}));
		return {
			...result.preAST,
			children
		};
	}
	renderCodeAST(result) {
		const gutter = createGutterWrapper();
		gutter.children = result.gutterAST;
		gutter.properties.style = `grid-row: span ${result.rowCount}`;
		return [gutter, createContentColumn(result.contentAST, result.rowCount)];
	}
	renderPartialHTML(children, includeCodeNode = false) {
		if (!includeCodeNode) return toHtml(children);
		return toHtml(createHastElement({
			tagName: "code",
			children,
			properties: { "data-code": "" }
		}));
	}
	async initializeHighlighter() {
		this.highlighter = await getSharedHighlighter(getHighlighterOptions(this.computedLang, this.options));
		return this.highlighter;
	}
	onHighlightSuccess(file, result, options) {
		if (this.renderCache == null) return;
		const triggerRenderUpdate = this.renderCache.file !== file || !this.renderCache.highlighted || !areRenderOptionsEqual(options, this.renderCache.options);
		this.renderCache = {
			file,
			options,
			highlighted: true,
			result,
			renderRange: void 0
		};
		if (triggerRenderUpdate) this.onRenderUpdate?.();
	}
	onHighlightError(error) {
		console.error(error);
	}
	createPreElement(totalLines, themeStyles, baseThemeType) {
		const { disableLineNumbers = false, overflow = "scroll", themeType = "system" } = this.options;
		return createPreElement({
			type: "file",
			diffIndicators: "none",
			disableBackground: true,
			disableLineNumbers,
			overflow,
			themeStyles,
			themeType: baseThemeType ?? themeType,
			split: false,
			totalLines
		});
	}
};
function areRenderOptionsEqual(optionsA, optionsB) {
	return areThemesEqual(optionsA.theme, optionsB.theme) && optionsA.tokenizeMaxLineLength === optionsB.tokenizeMaxLineLength;
}

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