import { DEFAULT_THEMES, HEADER_METADATA_SLOT_ID, UNSAFE_CSS_ATTRIBUTE } from "./constants.js";
import { getLineAnnotationName } from "./utils/getLineAnnotationName.js";
import { FileRenderer } from "./FileRenderer.js";
import { LineSelectionManager, pluckLineSelectionOptions } from "./LineSelectionManager.js";
import { MouseEventManager, pluckMouseEventOptions } from "./MouseEventManager.js";
import { ResizeManager } from "./ResizeManager.js";
import { PJSContainerLoaded } from "./custom-components/Container.js";
import { SVGSpriteSheet } from "./sprite.js";
import { createAnnotationWrapperNode } from "./utils/createAnnotationWrapperNode.js";
import { createCodeNode } from "./utils/createCodeNode.js";
import { createHoverContentNode } from "./utils/createHoverContentNode.js";
import { createUnsafeCSSStyleNode } from "./utils/createUnsafeCSSStyleNode.js";
import { wrapUnsafeCSS } from "./utils/cssWrappers.js";
import { prerenderHTMLIfNecessary } from "./utils/prerenderHTMLIfNecessary.js";
import { setPreNodeProperties } from "./utils/setWrapperNodeProps.js";
import deepEqual from "fast-deep-equal";
import { toHtml } from "hast-util-to-html";

//#region src/File.ts
let instanceId = -1;
var File = class {
	static LoadedCustomComponent = PJSContainerLoaded;
	__id = ++instanceId;
	fileContainer;
	spriteSVG;
	pre;
	code;
	unsafeCSSStyle;
	hoverContent;
	errorWrapper;
	headerElement;
	headerMetadata;
	fileRenderer;
	resizeManager;
	mouseEventManager;
	lineSelectionManager;
	annotationElements = [];
	lineAnnotations = [];
	file;
	constructor(options = { theme: DEFAULT_THEMES }, workerManager, isContainerManaged = false) {
		this.options = options;
		this.workerManager = workerManager;
		this.isContainerManaged = isContainerManaged;
		this.fileRenderer = new FileRenderer(options, this.handleHighlightRender, this.workerManager);
		this.resizeManager = new ResizeManager();
		this.mouseEventManager = new MouseEventManager("file", pluckMouseEventOptions(options));
		this.lineSelectionManager = new LineSelectionManager(pluckLineSelectionOptions(options));
		this.workerManager?.subscribeToThemeChanges(this);
	}
	handleHighlightRender = () => {
		this.rerender();
	};
	rerender() {
		if (this.file == null) return;
		this.render({
			file: this.file,
			forceRender: true
		});
	}
	setOptions(options) {
		if (options == null) return;
		this.options = options;
		this.mouseEventManager.setOptions(pluckMouseEventOptions(options));
		this.lineSelectionManager.setOptions(pluckLineSelectionOptions(options));
	}
	mergeOptions(options) {
		this.options = {
			...this.options,
			...options
		};
	}
	setThemeType(themeType) {
		if ((this.options.themeType ?? "system") === themeType) return;
		this.mergeOptions({ themeType });
		this.fileRenderer.setThemeType(themeType);
		if (this.headerElement != null) if (themeType === "system") delete this.headerElement.dataset.themeType;
		else this.headerElement.dataset.themeType = 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;
		}
	}
	getHoveredLine = () => {
		return this.mouseEventManager.getHoveredLine();
	};
	setLineAnnotations(lineAnnotations) {
		this.lineAnnotations = lineAnnotations;
	}
	setSelectedLines(range) {
		this.lineSelectionManager.setSelection(range);
	}
	cleanUp() {
		this.fileRenderer.cleanUp();
		this.resizeManager.cleanUp();
		this.mouseEventManager.cleanUp();
		this.lineSelectionManager.cleanUp();
		this.workerManager?.unsubscribeToThemeChanges(this);
		this.workerManager = void 0;
		this.file = void 0;
		if (!this.isContainerManaged) this.fileContainer?.parentNode?.removeChild(this.fileContainer);
		if (this.fileContainer?.shadowRoot != null) this.fileContainer.shadowRoot.innerHTML = "";
		this.fileContainer = void 0;
		this.pre = void 0;
		this.headerElement = void 0;
		this.errorWrapper = void 0;
		this.unsafeCSSStyle = void 0;
	}
	hydrate(props) {
		const { fileContainer, prerenderedHTML } = props;
		prerenderHTMLIfNecessary(fileContainer, prerenderedHTML);
		for (const element of Array.from(fileContainer.shadowRoot?.children ?? [])) {
			if (element instanceof SVGElement) {
				this.spriteSVG = element;
				continue;
			}
			if (!(element instanceof HTMLElement)) continue;
			if (element instanceof HTMLPreElement) {
				this.pre = element;
				continue;
			}
			if (element instanceof HTMLStyleElement && element.hasAttribute(UNSAFE_CSS_ATTRIBUTE)) {
				this.unsafeCSSStyle = element;
				continue;
			}
			if ("pjsHeader" in element.dataset) {
				this.headerElement = element;
				continue;
			}
		}
		if (this.pre == null) this.render(props);
		else {
			const { file, lineAnnotations } = props;
			this.fileContainer = fileContainer;
			delete this.pre.dataset.dehydrated;
			this.lineAnnotations = lineAnnotations ?? this.lineAnnotations;
			this.file = file;
			this.fileRenderer.hydrate(file);
			this.renderAnnotations();
			this.renderHoverUtility();
			this.injectUnsafeCSS();
			this.mouseEventManager.setup(this.pre);
			this.lineSelectionManager.setup(this.pre);
			if ((this.options.overflow ?? "scroll") === "scroll") this.resizeManager.setup(this.pre);
		}
	}
	render({ file, fileContainer, forceRender = false, containerWrapper, lineAnnotations }) {
		const annotationsChanged = lineAnnotations != null && (lineAnnotations.length > 0 || this.lineAnnotations.length > 0) ? lineAnnotations !== this.lineAnnotations : false;
		if (!forceRender && deepEqual(this.file, file) && !annotationsChanged) return;
		this.file = file;
		this.fileRenderer.setOptions(this.options);
		if (lineAnnotations != null) this.setLineAnnotations(lineAnnotations);
		this.fileRenderer.setLineAnnotations(this.lineAnnotations);
		const { disableFileHeader = false } = this.options;
		if (disableFileHeader) {
			if (this.headerElement != null) {
				this.headerElement.parentNode?.removeChild(this.headerElement);
				this.headerElement = void 0;
			}
		}
		fileContainer = this.getOrCreateFileContainerNode(fileContainer, containerWrapper);
		try {
			const fileResult = this.fileRenderer.renderFile(file);
			if (fileResult == null) {
				if (this.workerManager != null && !this.workerManager.isInitialized()) this.workerManager.initialize().then(() => this.rerender());
				return;
			}
			if (fileResult.headerAST != null) this.applyHeaderToDOM(fileResult.headerAST, fileContainer);
			const pre = this.getOrCreatePreNode(fileContainer);
			this.applyHunksToDOM(fileResult, pre);
			this.renderAnnotations();
			this.renderHoverUtility();
		} catch (error) {
			if (error instanceof Error) this.applyErrorToDOM(error, fileContainer);
		}
	}
	renderAnnotations() {
		if (this.isContainerManaged || this.fileContainer == null) return;
		for (const element of this.annotationElements) element.parentNode?.removeChild(element);
		this.annotationElements.length = 0;
		const { renderAnnotation } = this.options;
		if (renderAnnotation != null && this.lineAnnotations.length > 0) for (const annotation of this.lineAnnotations) {
			const content = renderAnnotation(annotation);
			if (content == null) continue;
			const el = createAnnotationWrapperNode(getLineAnnotationName(annotation));
			el.appendChild(content);
			this.annotationElements.push(el);
			this.fileContainer.appendChild(el);
		}
	}
	renderHoverUtility() {
		const { renderHoverUtility } = this.options;
		if (this.fileContainer == null || renderHoverUtility == null) return;
		if (this.hoverContent == null) {
			this.hoverContent = createHoverContentNode();
			this.fileContainer.appendChild(this.hoverContent);
		}
		const element = renderHoverUtility(this.mouseEventManager.getHoveredLine);
		this.hoverContent.innerHTML = "";
		if (element != null) this.hoverContent.appendChild(element);
	}
	injectUnsafeCSS() {
		if (this.fileContainer?.shadowRoot == null) return;
		const { unsafeCSS } = this.options;
		if (unsafeCSS == null || unsafeCSS === "") {
			if (this.unsafeCSSStyle != null) {
				this.unsafeCSSStyle.parentNode?.removeChild(this.unsafeCSSStyle);
				this.unsafeCSSStyle = void 0;
			}
			return;
		}
		if (this.unsafeCSSStyle == null) {
			this.unsafeCSSStyle = createUnsafeCSSStyleNode();
			this.fileContainer.shadowRoot.appendChild(this.unsafeCSSStyle);
		}
		this.unsafeCSSStyle.innerText = wrapUnsafeCSS(unsafeCSS);
	}
	applyHunksToDOM(result, pre) {
		this.cleanupErrorWrapper();
		this.applyPreNodeAttributes(pre, result);
		pre.innerHTML = "";
		this.code = createCodeNode();
		this.code.innerHTML = this.fileRenderer.renderPartialHTML(result.codeAST);
		pre.appendChild(this.code);
		this.injectUnsafeCSS();
		this.mouseEventManager.setup(pre);
		this.lineSelectionManager.setup(pre);
		this.lineSelectionManager.setDirty();
		if ((this.options.overflow ?? "scroll") === "scroll") this.resizeManager.setup(pre);
		else this.resizeManager.cleanUp();
	}
	applyHeaderToDOM(headerAST, container) {
		const { file } = this;
		if (file == null) return;
		this.cleanupErrorWrapper();
		const tempDiv = document.createElement("div");
		tempDiv.innerHTML = toHtml(headerAST);
		const newHeader = tempDiv.firstElementChild;
		if (!(newHeader instanceof HTMLElement)) return;
		if (this.headerElement != null) container.shadowRoot?.replaceChild(newHeader, this.headerElement);
		else container.shadowRoot?.prepend(newHeader);
		this.headerElement = newHeader;
		if (this.isContainerManaged) return;
		const { renderCustomMetadata } = this.options;
		if (this.headerMetadata != null) this.headerMetadata.parentNode?.removeChild(this.headerMetadata);
		const content = renderCustomMetadata?.(file) ?? void 0;
		if (content != null) {
			this.headerMetadata = document.createElement("div");
			this.headerMetadata.slot = HEADER_METADATA_SLOT_ID;
			if (content instanceof Element) this.headerMetadata.appendChild(content);
			else this.headerMetadata.innerText = `${content}`;
			container.appendChild(this.headerMetadata);
		}
	}
	getOrCreateFileContainerNode(fileContainer, parentNode) {
		this.fileContainer = fileContainer ?? this.fileContainer ?? document.createElement("file-diff");
		if (parentNode != null && this.fileContainer.parentNode !== parentNode) parentNode.appendChild(this.fileContainer);
		if (this.spriteSVG == null) {
			const fragment = document.createElement("div");
			fragment.innerHTML = SVGSpriteSheet;
			const firstChild = fragment.firstChild;
			if (firstChild instanceof SVGElement) {
				this.spriteSVG = firstChild;
				this.fileContainer.shadowRoot?.appendChild(this.spriteSVG);
			}
		}
		return this.fileContainer;
	}
	getOrCreatePreNode(container) {
		if (this.pre == null) {
			this.pre = document.createElement("pre");
			container.shadowRoot?.appendChild(this.pre);
		} else if (this.pre.parentNode !== container) container.shadowRoot?.appendChild(this.pre);
		return this.pre;
	}
	applyPreNodeAttributes(pre, { totalLines, themeStyles, baseThemeType }) {
		const { overflow = "scroll", themeType = "system", disableLineNumbers = false } = this.options;
		setPreNodeProperties({
			pre,
			split: false,
			themeStyles,
			overflow,
			disableLineNumbers,
			themeType: baseThemeType ?? themeType,
			diffIndicators: "none",
			disableBackground: true,
			totalLines
		});
	}
	applyErrorToDOM(error, container) {
		this.cleanupErrorWrapper();
		const pre = this.getOrCreatePreNode(container);
		pre.innerHTML = "";
		pre.parentNode?.removeChild(pre);
		this.pre = void 0;
		const shadowRoot = container.shadowRoot ?? container.attachShadow({ mode: "open" });
		this.errorWrapper ??= document.createElement("div");
		this.errorWrapper.dataset.errorWrapper = "";
		this.errorWrapper.innerHTML = "";
		shadowRoot.appendChild(this.errorWrapper);
		const errorMessage = document.createElement("div");
		errorMessage.dataset.errorMessage = "";
		errorMessage.innerText = error.message;
		this.errorWrapper.appendChild(errorMessage);
		const errorStack = document.createElement("pre");
		errorStack.dataset.errorStack = "";
		errorStack.innerText = error.stack ?? "No Error Stack";
		this.errorWrapper.appendChild(errorStack);
	}
	cleanupErrorWrapper() {
		this.errorWrapper?.parentNode?.removeChild(this.errorWrapper);
		this.errorWrapper = void 0;
	}
};

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