import { getElementBounds } from "../getElementBounds.js";

//#region src/canvas/overlays/overlayState.ts
/**
* Extract overlay targets from selection context and highlighted element.
* This is pure SEMANTICS - it determines WHAT should be shown.
*/
function getOverlayTargets(selection, highlightedElement) {
	return {
		selectedIds: selection?.selectedIds ? new Set(selection.selectedIds) : /* @__PURE__ */ new Set(),
		boxSelectBounds: selection?.boxSelectBounds ?? null,
		highlightedElementId: highlightedElement?.getAttribute("data-element-id") ?? highlightedElement?.id ?? null
	};
}
/**
* Calculate screen bounds for a single element.
* This is the SINGLE SOURCE OF TRUTH for element → screen bounds conversion.
*
* Strategy:
* 1. Try to use metadata (already in canvas coordinates)
* 2. Fall back to DOM measurement if metadata unavailable
*
* @param elementId - The element's ID (data-element-id or id attribute)
* @param canvas - Canvas element with getElementData method
* @param canvasRect - Canvas content element's bounding rect
* @param scale - Current zoom scale
* @returns Screen bounds or null if element not found
*/
function calculateElementScreenBounds(elementId, canvas, canvasRect, scale) {
	const metadata = canvas.getElementData(elementId);
	if (metadata && metadata.width > 0 && metadata.height > 0) return {
		x: canvasRect.left + metadata.x * scale,
		y: canvasRect.top + metadata.y * scale,
		width: metadata.width * scale,
		height: metadata.height * scale
	};
	const element = findElement(elementId, canvas);
	if (!element) return null;
	const bounds = getElementBounds(element);
	return {
		x: bounds.left,
		y: bounds.top,
		width: bounds.width,
		height: bounds.height
	};
}
/**
* Find an element by ID in the canvas (checks both light DOM and shadow DOM).
*/
function findElement(elementId, canvas) {
	if (canvas.getElement) {
		const element = canvas.getElement(elementId);
		if (element) return element;
	}
	if (canvas.shadowRoot) {
		const element = canvas.shadowRoot.querySelector(`[data-element-id="${elementId}"]`);
		if (element) return element;
	}
	return canvas.querySelector(`[data-element-id="${elementId}"]`);
}
/**
* Calculate the union of multiple bounds (bounding box that contains all).
*/
function unionBounds(bounds) {
	if (bounds.length === 0) return null;
	let minX = Infinity;
	let minY = Infinity;
	let maxX = -Infinity;
	let maxY = -Infinity;
	for (const b of bounds) {
		minX = Math.min(minX, b.x);
		minY = Math.min(minY, b.y);
		maxX = Math.max(maxX, b.x + b.width);
		maxY = Math.max(maxY, b.y + b.height);
	}
	return {
		x: minX,
		y: minY,
		width: maxX - minX,
		height: maxY - minY
	};
}
/**
* Calculate selection bounds for all selected elements.
*/
function calculateSelectionBounds(selectedIds, canvas, canvasRect, scale) {
	if (selectedIds.size === 0) return null;
	return unionBounds(Array.from(selectedIds).map((id) => calculateElementScreenBounds(id, canvas, canvasRect, scale)).filter((b) => b !== null));
}
/**
* Calculate box-select bounds (convert canvas bounds to screen bounds).
*
* @param boxSelectBounds - Box selection bounds in canvas coordinates
* @param panZoomElement - The pan-zoom element (for canvasToScreen conversion)
* @param panZoomTransform - Current pan-zoom transform
* @returns Screen bounds or null
*/
function calculateBoxSelectBounds(boxSelectBounds, panZoomElement, panZoomTransform) {
	if (!boxSelectBounds || !panZoomElement || !panZoomTransform) return null;
	const pz = panZoomElement;
	if (typeof pz.canvasToScreen === "function") {
		const topLeft = pz.canvasToScreen(boxSelectBounds.left, boxSelectBounds.top);
		const bottomRight = pz.canvasToScreen(boxSelectBounds.right, boxSelectBounds.bottom);
		return {
			x: topLeft.x,
			y: topLeft.y,
			width: bottomRight.x - topLeft.x,
			height: bottomRight.y - topLeft.y
		};
	}
	const panZoomRect = panZoomElement.getBoundingClientRect();
	const { scale, x, y } = panZoomTransform;
	const screenLeft = panZoomRect.left + boxSelectBounds.left * scale + x;
	const screenTop = panZoomRect.top + boxSelectBounds.top * scale + y;
	const screenRight = panZoomRect.left + boxSelectBounds.right * scale + x;
	const screenBottom = panZoomRect.top + boxSelectBounds.bottom * scale + y;
	return {
		x: screenLeft,
		y: screenTop,
		width: screenRight - screenLeft,
		height: screenBottom - screenTop
	};
}
/**
* Calculate highlight bounds for the highlighted element.
*/
function calculateHighlightBounds(highlightedElementId, canvas, canvasRect, scale) {
	if (!highlightedElementId) return null;
	return calculateElementScreenBounds(highlightedElementId, canvas, canvasRect, scale);
}
/**
* Calculate the complete overlay state from targets.
* This is the main entry point that combines semantics and mechanism.
*
* @param targets - What should be shown (from getOverlayTargets)
* @param canvas - Canvas element with metadata
* @param canvasRect - Canvas content bounding rect
* @param panZoomElement - Pan-zoom element (for box-select conversion)
* @param panZoomTransform - Current transform
* @returns Complete overlay state
*/
function calculateOverlayState(targets, canvas, canvasRect, panZoomElement, panZoomTransform) {
	const scale = panZoomTransform?.scale ?? 1;
	return {
		selection: calculateSelectionBounds(targets.selectedIds, canvas, canvasRect, scale),
		boxSelect: calculateBoxSelectBounds(targets.boxSelectBounds, panZoomElement, panZoomTransform),
		highlight: calculateHighlightBounds(targets.highlightedElementId, canvas, canvasRect, scale)
	};
}

//#endregion
export { calculateOverlayState, getOverlayTargets };
//# sourceMappingURL=overlayState.js.map