import { areSelectionsEqual } from "../utils/areSelectionsEqual.js";

//#region src/managers/LineSelectionManager.ts
/**
* Manages line selection state and interactions for code/diff viewers.
* Handles:
* - Click and drag selection
* - Shift-click to extend selection
* - DOM attribute updates (data-selected-line)
*/
var LineSelectionManager = class {
	pre;
	selectedRange = null;
	renderedSelectionRange;
	anchor;
	_queuedRender;
	constructor(options = {}) {
		this.options = options;
	}
	setOptions(options) {
		this.options = {
			...this.options,
			...options
		};
		this.removeEventListeners();
		if (this.options.enableLineSelection === true) this.attachEventListeners();
	}
	cleanUp() {
		this.removeEventListeners();
		if (this._queuedRender != null) {
			cancelAnimationFrame(this._queuedRender);
			this._queuedRender = void 0;
		}
		this.pre?.removeAttribute("data-interactive-line-numbers");
		this.pre = void 0;
	}
	setup(pre) {
		this.setDirty();
		if (this.pre !== pre) {
			this.cleanUp();
			this.pre = pre;
			const { enableLineSelection = false } = this.options;
			if (enableLineSelection) this.attachEventListeners();
			else this.removeEventListeners();
		}
		this.setSelection(this.selectedRange);
	}
	setDirty() {
		this.renderedSelectionRange = void 0;
	}
	isDirty() {
		return this.renderedSelectionRange === void 0;
	}
	setSelection(range) {
		const isRangeChange = !(range === this.selectedRange || areSelectionsEqual(range ?? void 0, this.selectedRange ?? void 0));
		if (!this.isDirty() && !isRangeChange) return;
		this.selectedRange = range;
		this.renderSelection();
		if (isRangeChange) this.notifySelectionChange();
	}
	getSelection() {
		return this.selectedRange;
	}
	getLineIndex(lineNumber, side) {
		const { getLineIndex } = this.options;
		return getLineIndex != null ? getLineIndex(lineNumber, side) : [lineNumber - 1, lineNumber - 1];
	}
	attachEventListeners() {
		if (this.pre == null) return;
		this.removeEventListeners();
		this.pre.setAttribute("data-interactive-line-numbers", "");
		this.pre.addEventListener("pointerdown", this.handleMouseDown);
	}
	removeEventListeners() {
		if (this.pre == null) return;
		this.pre.removeEventListener("pointerdown", this.handleMouseDown);
		document.removeEventListener("pointermove", this.handleMouseMove);
		document.removeEventListener("pointerup", this.handleMouseUp);
		this.pre.removeAttribute("data-interactive-line-numbers");
	}
	handleMouseDown = (event) => {
		const mouseEventData = event.button === 0 ? this.getMouseEventDataForPath(event.composedPath(), "click") : void 0;
		if (mouseEventData == null || this.pre == null) return;
		event.preventDefault();
		const { lineNumber, eventSide, lineIndex } = mouseEventData;
		if (event.shiftKey && this.selectedRange != null) {
			const range = this.getIndexesFromSelection(this.selectedRange, this.pre.getAttribute("data-diff-type") === "split");
			if (range == null) return;
			const useStart = range.start <= range.end ? lineIndex >= range.start : lineIndex <= range.end;
			this.anchor = {
				line: useStart ? this.selectedRange.start : this.selectedRange.end,
				side: (useStart ? this.selectedRange.side : this.selectedRange.endSide ?? this.selectedRange.side) ?? "additions"
			};
			this.updateSelection(lineNumber, eventSide);
			this.notifySelectionStart(this.selectedRange);
		} else {
			if (this.selectedRange?.start === lineNumber && this.selectedRange?.end === lineNumber) {
				this.updateSelection(null);
				this.notifySelectionEnd(null);
				this.notifySelectionChange();
				return;
			}
			this.selectedRange = null;
			this.anchor = {
				line: lineNumber,
				side: eventSide
			};
			this.updateSelection(lineNumber, eventSide);
			this.notifySelectionStart(this.selectedRange);
		}
		document.addEventListener("pointermove", this.handleMouseMove);
		document.addEventListener("pointerup", this.handleMouseUp);
	};
	handleMouseMove = (event) => {
		const mouseEventData = this.getMouseEventDataForPath(event.composedPath(), "move");
		if (mouseEventData == null || this.anchor == null) return;
		const { lineNumber, eventSide } = mouseEventData;
		this.updateSelection(lineNumber, eventSide);
	};
	handleMouseUp = () => {
		this.anchor = void 0;
		document.removeEventListener("pointermove", this.handleMouseMove);
		document.removeEventListener("pointerup", this.handleMouseUp);
		this.notifySelectionEnd(this.selectedRange);
		this.notifySelectionChange();
	};
	updateSelection(currentLine, side) {
		if (currentLine == null) this.selectedRange = null;
		else {
			const anchorSide = this.anchor?.side ?? side;
			this.selectedRange = {
				start: this.anchor?.line ?? currentLine,
				end: currentLine,
				side: anchorSide,
				endSide: anchorSide !== side ? side : void 0
			};
		}
		this._queuedRender ??= requestAnimationFrame(this.renderSelection);
	}
	getIndexesFromSelection(selectedRange, split) {
		if (this.pre == null) return;
		const startIndexes = this.getLineIndex(selectedRange.start, selectedRange.side);
		const finalIndexes = this.getLineIndex(selectedRange.end, selectedRange.endSide ?? selectedRange.side);
		return startIndexes != null && finalIndexes != null ? {
			start: split ? startIndexes[1] : startIndexes[0],
			end: split ? finalIndexes[1] : finalIndexes[0]
		} : void 0;
	}
	renderSelection = () => {
		if (this._queuedRender != null) {
			cancelAnimationFrame(this._queuedRender);
			this._queuedRender = void 0;
		}
		if (this.pre == null || this.renderedSelectionRange === this.selectedRange) return;
		const allSelected = this.pre.querySelectorAll("[data-selected-line]");
		for (const element of allSelected) element.removeAttribute("data-selected-line");
		this.renderedSelectionRange = this.selectedRange;
		if (this.selectedRange == null) return;
		const { children: codeElements } = this.pre;
		if (codeElements.length === 0) return;
		if (codeElements.length > 2) {
			console.error(codeElements);
			throw new Error("LineSelectionManager.applySelectionToDOM: Somehow there are more than 2 code elements...");
		}
		const split = this.pre.getAttribute("data-diff-type") === "split";
		const rowRange = this.getIndexesFromSelection(this.selectedRange, split);
		if (rowRange == null) {
			console.error({
				rowRange,
				selectedRange: this.selectedRange
			});
			throw new Error("LineSelectionManager.renderSelection: No valid rowRange");
		}
		const isSingle = rowRange.start === rowRange.end;
		const first = Math.min(rowRange.start, rowRange.end);
		const last = Math.max(rowRange.start, rowRange.end);
		for (const code of codeElements) {
			const [gutter, content] = code.children;
			const len = content.children.length;
			if (len !== gutter.children.length) throw new Error("LineSelectionManager.renderSelection: gutter and content children dont match, something is wrong");
			for (let i = 0; i < len; i++) {
				const contentElement = content.children[i];
				const gutterElement = gutter.children[i];
				if (!(contentElement instanceof HTMLElement) || !(gutterElement instanceof HTMLElement)) continue;
				const lineIndex = this.parseLineIndex(contentElement, split);
				if ((lineIndex ?? 0) > last) break;
				if (lineIndex == null || lineIndex < first) continue;
				let attributeValue = isSingle ? "single" : lineIndex === first ? "first" : lineIndex === last ? "last" : "";
				contentElement.setAttribute("data-selected-line", attributeValue);
				gutterElement.setAttribute("data-selected-line", attributeValue);
				if (gutterElement.nextSibling instanceof HTMLElement && contentElement.nextSibling instanceof HTMLElement && contentElement.nextSibling.hasAttribute("data-line-annotation")) {
					if (isSingle) {
						attributeValue = "last";
						contentElement.setAttribute("data-selected-line", "first");
					} else if (lineIndex === first) attributeValue = "";
					else if (lineIndex === last) contentElement.setAttribute("data-selected-line", "");
					contentElement.nextSibling.setAttribute("data-selected-line", attributeValue);
					gutterElement.nextSibling.setAttribute("data-selected-line", attributeValue);
				}
			}
		}
	};
	notifySelectionChange() {
		const { onLineSelected } = this.options;
		if (onLineSelected == null) return;
		onLineSelected(this.selectedRange ?? null);
	}
	notifySelectionStart(range) {
		const { onLineSelectionStart } = this.options;
		if (onLineSelectionStart == null) return;
		onLineSelectionStart(range);
	}
	notifySelectionEnd(range) {
		const { onLineSelectionEnd } = this.options;
		if (onLineSelectionEnd == null) return;
		onLineSelectionEnd(range);
	}
	getMouseEventDataForPath(path, eventType) {
		if (this.pre == null) return;
		let lineNumber;
		let lineIndex;
		let isNumberColumn = false;
		let eventSide;
		for (const element of path) {
			if (lineNumber != null && lineIndex != null && eventSide != null) break;
			if (!(element instanceof HTMLElement)) continue;
			if (element.hasAttribute("data-line-index")) {
				isNumberColumn = element.hasAttribute("data-column-number");
				lineNumber = this.getLineNumber(element);
				lineIndex = this.parseLineIndex(element, this.pre.getAttribute("data-diff-type") === "split");
				const lineType = element.getAttribute("data-line-type");
				if (lineType === "change-deletion") eventSide = "deletions";
				else if (lineType === "change-additions") eventSide = "additions";
				continue;
			}
			if (eventSide == null && element.hasAttribute("data-code")) {
				eventSide = element.hasAttribute("data-deletions") ? "deletions" : element.hasAttribute("data-additions") ? "additions" : void 0;
				break;
			}
		}
		if (eventType === "click" && !isNumberColumn || lineIndex == null || lineNumber == null) return;
		return {
			lineIndex,
			lineNumber,
			eventSide: eventSide ?? "additions"
		};
	}
	getLineNumber(element) {
		const lineNumber = parseInt(element.getAttribute("data-column-number") ?? element.getAttribute("data-line") ?? "", 10);
		return !Number.isNaN(lineNumber) ? lineNumber : void 0;
	}
	parseLineIndex(element, split) {
		const lineIndexes = (element.getAttribute("data-line-index") ?? "").split(",").map((value) => parseInt(value)).filter((value) => !Number.isNaN(value));
		if (split && lineIndexes.length === 2) return lineIndexes[1];
		else if (!split) return lineIndexes[0];
	}
};
function pluckLineSelectionOptions({ enableLineSelection, onLineSelected, onLineSelectionStart, onLineSelectionEnd }, getLineIndex) {
	return {
		enableLineSelection,
		onLineSelected,
		onLineSelectionStart,
		onLineSelectionEnd,
		getLineIndex
	};
}

//#endregion
export { LineSelectionManager, pluckLineSelectionOptions };
//# sourceMappingURL=LineSelectionManager.js.map