import { DEFAULT_THEMES } from "./constants.js";
import { getSharedHighlighter } from "./highlighter/shared_highlighter.js";
import { getHighlighterOptions } from "./utils/getHighlighterOptions.js";
import { formatCSSVariablePrefix } from "./utils/formatCSSVariablePrefix.js";
import { getHighlighterThemeStyles } from "./utils/getHighlighterThemeStyles.js";
import { createCodeNode } from "./utils/createCodeNode.js";
import { setPreNodeProperties } from "./utils/setWrapperNodeProps.js";
import { queueRender } from "./UniversalRenderer.js";
import { CodeToTokenTransformStream } from "./shiki-stream/stream.js";
import { createRowNodes } from "./utils/createRowNodes.js";
import { createSpanFromToken } from "./utils/createSpanNodeFromToken.js";

//#region src/FileStream.ts
var FileStream = class {
	highlighter;
	stream;
	abortController;
	fileContainer;
	pre;
	code;
	constructor(options = { theme: DEFAULT_THEMES }) {
		this.options = options;
		this.currentLineIndex = this.options.startingLineIndex ?? 1;
	}
	cleanUp() {
		this.abortController?.abort();
		this.abortController = void 0;
	}
	setThemeType(themeType) {
		if ((this.options.themeType ?? "system") === themeType) return;
		this.options = {
			...this.options,
			themeType
		};
		if (this.pre != null) switch (themeType) {
			case "system":
				delete this.pre.dataset.themeType;
				break;
			case "light":
			case "dark":
				this.pre.dataset.themeType = themeType;
				break;
		}
	}
	async initializeHighlighter() {
		this.highlighter = await getSharedHighlighter(getHighlighterOptions(this.options.lang, this.options));
		return this.highlighter;
	}
	queuedSetupArgs;
	async setup(_source, _wrapper) {
		const isSettingUp = this.queuedSetupArgs != null;
		this.queuedSetupArgs = [_source, _wrapper];
		if (isSettingUp) return;
		this.highlighter ??= await this.initializeHighlighter();
		const [source, wrapper] = this.queuedSetupArgs;
		this.queuedSetupArgs = void 0;
		const stream = source;
		this.setupStream(stream, wrapper, this.highlighter);
	}
	setupStream(stream, wrapper, highlighter) {
		const { disableLineNumbers = false, overflow = "scroll", theme = DEFAULT_THEMES, themeType = "system" } = this.options;
		const fileContainer = this.getOrCreateFileContainer();
		if (fileContainer.parentElement == null) wrapper.appendChild(fileContainer);
		this.pre ??= document.createElement("pre");
		if (this.pre.parentElement == null) fileContainer.shadowRoot?.appendChild(this.pre);
		const themeStyles = getHighlighterThemeStyles({
			theme,
			highlighter
		});
		const baseThemeType = typeof theme === "string" ? highlighter.getTheme(theme).type : void 0;
		const pre = setPreNodeProperties({
			diffIndicators: "none",
			disableBackground: true,
			disableLineNumbers,
			overflow,
			pre: this.pre,
			split: false,
			themeType: baseThemeType ?? themeType,
			themeStyles,
			totalLines: 0
		});
		pre.innerHTML = "";
		this.pre = pre;
		this.code = createCodeNode({ pre });
		this.abortController?.abort();
		this.abortController = new AbortController();
		const { onStreamStart, onStreamClose, onStreamAbort } = this.options;
		this.stream = stream;
		this.stream.pipeThrough(typeof theme === "string" ? new CodeToTokenTransformStream({
			...this.options,
			theme,
			highlighter,
			allowRecalls: true,
			defaultColor: false,
			cssVariablePrefix: formatCSSVariablePrefix()
		}) : new CodeToTokenTransformStream({
			...this.options,
			themes: theme,
			highlighter,
			allowRecalls: true,
			defaultColor: false,
			cssVariablePrefix: formatCSSVariablePrefix()
		})).pipeTo(new WritableStream({
			start(controller) {
				onStreamStart?.(controller);
			},
			close() {
				onStreamClose?.();
			},
			abort(reason) {
				onStreamAbort?.(reason);
			},
			write: this.handleWrite
		}), { signal: this.abortController.signal }).catch((error) => {
			if (error.name !== "AbortError") console.error("FileStream pipe error:", error);
		});
	}
	queuedTokens = [];
	handleWrite = (token) => {
		if ("recall" in token && this.queuedTokens.length >= token.recall) this.queuedTokens.length = this.queuedTokens.length - token.recall;
		else this.queuedTokens.push(token);
		queueRender(this.render);
		this.options.onStreamWrite?.(token);
	};
	currentLineIndex;
	currentLineElement;
	render = () => {
		this.options.onPreRender?.(this);
		const linesToAppend = [];
		for (const token of this.queuedTokens) if ("recall" in token) {
			if (this.currentLineElement == null) throw new Error("FileStream.render: no current line element, shouldnt be possible to get here");
			if (token.recall > this.currentLineElement.childNodes.length) throw new Error(`FileStream.render: Token recall exceed the current line, there's probably a bug...`);
			for (let i = 0; i < token.recall; i++) this.currentLineElement.lastChild?.remove();
		} else {
			const span = createSpanFromToken(token);
			if (this.currentLineElement == null) linesToAppend.push(this.createLine());
			this.currentLineElement?.appendChild(span);
			if (token.content === "\n") {
				this.currentLineIndex++;
				linesToAppend.push(this.createLine());
			}
		}
		for (const line of linesToAppend) this.code?.appendChild(line);
		this.queuedTokens.length = 0;
		this.options.onPostRender?.(this);
	};
	createLine() {
		const { row, content } = createRowNodes(this.currentLineIndex);
		this.currentLineElement = content;
		return row;
	}
	getOrCreateFileContainer(fileContainer) {
		if (fileContainer != null && fileContainer === this.fileContainer || fileContainer == null && this.fileContainer != null) return this.fileContainer;
		this.fileContainer = fileContainer ?? document.createElement("file-diff");
		return this.fileContainer;
	}
};

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