const require_runtime = require("../../../_virtual/_rolldown/runtime.js");
const require_objectSpread2 = require("../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js");
const require_snap = require("../utils/snap.js");
const require_alignSnap = require("../utils/alignSnap.js");
const require_floorMapSelectionZone = require("../utils/floorMapSelectionZone.js");
const require_FloorMapEditQuickActions = require("./FloorMapEditQuickActions.js");
let react = require("react");
react = require_runtime.__toESM(react);
let classnames = require("classnames");
classnames = require_runtime.__toESM(classnames);
require("./EditableItemLayer.less");
//#region src/components/pisellFloorMapLayout/components/EditableItemLayer.tsx
/**
* EditableItemLayer - 编辑态图元层：拖拽、缩放、选中、吸附
*/
function isSceneLayoutTransformLocked(item) {
	var _item$_sceneElement;
	return Boolean((_item$_sceneElement = item._sceneElement) === null || _item$_sceneElement === void 0 ? void 0 : _item$_sceneElement.layoutTransformLocked);
}
const PREFIX = "pisell-floor-map-layout";
const DRAG_THRESHOLD = 4;
/** 选中图元抬升到该层级，保证快捷栏与缩放点不被其它图元（任意 item.zIndex）遮住 */
const SELECTED_ITEM_STACK_Z = 1e6;
const RESIZE_CORNER_HANDLES = [
	"nw",
	"ne",
	"se",
	"sw"
];
function getItemKey(item, index, rowKey) {
	var _rowKey;
	if (!rowKey) return item.id;
	if (typeof rowKey === "string") return String((_rowKey = item[rowKey]) !== null && _rowKey !== void 0 ? _rowKey : item.id);
	return rowKey(item, index);
}
function getShapeStyle(shape = "rect") {
	switch (shape) {
		case "circle": return { borderRadius: "50%" };
		case "triangle": return { clipPath: "polygon(50% 0%, 0% 100%, 100% 100%)" };
		default: return {};
	}
}
function EditableItemLayer(props) {
	const { items, renderItem, rowKey, cellSize, itemUnit = "pixel", itemGap = 0, contentWidth, contentHeight, contentOffsetX = 0, contentOffsetY = 0, selectedId, onSelect, onUpdateItem, onUpdateItemsBatch, snapStep = 0, scale = 1, onInteractionChange, onLiveRectChange, onLiveRectsChange, alignSnapThresholdPx = 6, onAlignGuidesChange, alignGuideStroke = "#1570EF", getMergedItemWrapperStyle, className, editQuickBar } = props;
	const contentRef = (0, react.useRef)(null);
	const liveRectRef = (0, react.useRef)(null);
	const [interaction, setInteraction] = (0, react.useState)(null);
	const [liveRect, setLiveRect] = (0, react.useState)(null);
	const groupLiveRectsRef = (0, react.useRef)(null);
	const [groupLiveRects, setGroupLiveRects] = (0, react.useState)(null);
	/** 拖动/缩放过程中的对齐辅助线（红色虚线） */
	const [alignGuides, setAlignGuides] = (0, react.useState)([]);
	/** 基础像素矩形（与 liveRect 无关），供对齐吸附排除自身后参考 */
	const getBaseRect = (0, react.useCallback)((item) => {
		var _item$width2, _item$height2;
		const isPixel = itemUnit === "pixel";
		const g = itemGap !== null && itemGap !== void 0 ? itemGap : 0;
		if (isPixel) {
			var _item$width, _item$height;
			return {
				left: item.x,
				top: item.y,
				width: (_item$width = item.width) !== null && _item$width !== void 0 ? _item$width : cellSize,
				height: (_item$height = item.height) !== null && _item$height !== void 0 ? _item$height : cellSize
			};
		}
		const w = (_item$width2 = item.width) !== null && _item$width2 !== void 0 ? _item$width2 : 1;
		const h = (_item$height2 = item.height) !== null && _item$height2 !== void 0 ? _item$height2 : 1;
		return {
			left: g > 0 ? item.x * (cellSize + g) + g : item.x * cellSize,
			top: g > 0 ? item.y * (cellSize + g) + g : item.y * cellSize,
			width: g > 0 ? w * cellSize + (w - 1) * g : w * cellSize,
			height: g > 0 ? h * cellSize + (h - 1) * g : h * cellSize
		};
	}, [
		cellSize,
		itemGap,
		itemUnit
	]);
	const getItemStyle = (0, react.useCallback)((item, index) => {
		const isPixel = itemUnit === "pixel";
		const g = itemGap !== null && itemGap !== void 0 ? itemGap : 0;
		let left;
		let top;
		let width;
		let height;
		if (isPixel) {
			var _item$width3, _item$height3;
			width = (_item$width3 = item.width) !== null && _item$width3 !== void 0 ? _item$width3 : cellSize;
			height = (_item$height3 = item.height) !== null && _item$height3 !== void 0 ? _item$height3 : cellSize;
			left = item.x;
			top = item.y;
		} else {
			var _item$width4, _item$height4;
			const w = (_item$width4 = item.width) !== null && _item$width4 !== void 0 ? _item$width4 : 1;
			const h = (_item$height4 = item.height) !== null && _item$height4 !== void 0 ? _item$height4 : 1;
			left = g > 0 ? item.x * (cellSize + g) + g : item.x * cellSize;
			top = g > 0 ? item.y * (cellSize + g) + g : item.y * cellSize;
			width = g > 0 ? w * cellSize + (w - 1) * g : w * cellSize;
			height = g > 0 ? h * cellSize + (h - 1) * g : h * cellSize;
		}
		const groupLr = groupLiveRects === null || groupLiveRects === void 0 ? void 0 : groupLiveRects[item.id];
		if (groupLr) {
			left = groupLr.x;
			top = groupLr.y;
			width = groupLr.width;
			height = groupLr.height;
		} else if (liveRect && liveRect.id === item.id) {
			left = liveRect.x;
			top = liveRect.y;
			width = liveRect.width;
			height = liveRect.height;
		}
		return {
			left,
			top,
			width,
			height
		};
	}, [
		itemUnit,
		cellSize,
		itemGap,
		liveRect,
		groupLiveRects
	]);
	/** 使用 Pointer Events，触控/笔/鼠标均可拖拽；仅靠 mouse 事件在触控设备上无法连续触发 move/up */
	const handlePointerDown = (0, react.useCallback)((e, item, index) => {
		if (e.button !== 0) return;
		if (e.target.closest(`.${PREFIX}-editable-resize-handle`)) return;
		const merged = item;
		if (isSceneLayoutTransformLocked(merged)) return;
		e.stopPropagation();
		e.preventDefault();
		onInteractionChange === null || onInteractionChange === void 0 || onInteractionChange(true);
		const style = getItemStyle(item, index);
		/**
		* 选区分组拖拽：拖拽开始那一刻，按「几何完全包含」快照成员，
		* 拖动过程中成员集合不变（避免边界抖动）。
		*/
		let group;
		if (require_floorMapSelectionZone.isSelectionZoneItem(merged)) {
			const zoneRect = {
				left: style.left,
				top: style.top,
				width: style.width,
				height: style.height
			};
			const itemRects = items.map((it) => ({
				item: it,
				rect: getBaseRect(it)
			}));
			const memberIds = new Set(require_floorMapSelectionZone.computeSelectionZoneMemberIds(item.id, zoneRect, itemRects));
			group = items.filter((it) => memberIds.has(it.id)).map((it) => {
				const r = getBaseRect(it);
				return {
					id: it.id,
					x: r.left,
					y: r.top,
					width: r.width,
					height: r.height
				};
			});
		}
		setInteraction(require_objectSpread2._objectSpread2({
			type: "drag",
			itemId: item.id,
			startX: e.clientX,
			startY: e.clientY,
			itemX: style.left,
			itemY: style.top,
			itemW: style.width,
			itemH: style.height
		}, group && group.length > 0 ? { group } : {}));
	}, [
		getItemStyle,
		onInteractionChange,
		items,
		getBaseRect
	]);
	const handleResizePointerDown = (0, react.useCallback)((e, itemId, handle) => {
		if (e.button !== 0) return;
		const item = items.find((i) => i.id === itemId);
		if (!item) return;
		if (isSceneLayoutTransformLocked(item)) return;
		e.stopPropagation();
		e.preventDefault();
		onInteractionChange === null || onInteractionChange === void 0 || onInteractionChange(true);
		const style = getItemStyle(item, items.indexOf(item));
		setInteraction({
			type: "resize",
			itemId,
			handle,
			startX: e.clientX,
			startY: e.clientY,
			startW: style.width,
			startH: style.height,
			startItemX: style.left,
			startItemY: style.top
		});
	}, [
		items,
		getItemStyle,
		onInteractionChange
	]);
	react.default.useEffect(() => {
		if (!interaction) return;
		/** 阈值按 scale 折算到内容坐标，让它在屏幕上恒定 */
		const threshold = Math.max(0, alignSnapThresholdPx) / Math.max(scale, .01);
		/** 内部计算 snap 目标：排除自身，使用基础 rect（避开 liveRect 自吸） */
		const otherRects = items.filter((it) => it.id !== interaction.itemId).map((it) => require_objectSpread2._objectSpread2({ id: it.id }, getBaseRect(it)));
		const onMove = (e) => {
			if (interaction.type === "drag") {
				if (interaction.group) {
					const gdx = (e.clientX - interaction.startX) / scale;
					const gdy = (e.clientY - interaction.startY) / scale;
					const map = { [interaction.itemId]: {
						x: interaction.itemX + gdx,
						y: interaction.itemY + gdy,
						width: interaction.itemW,
						height: interaction.itemH
					} };
					interaction.group.forEach((m) => {
						map[m.id] = {
							x: m.x + gdx,
							y: m.y + gdy,
							width: m.width,
							height: m.height
						};
					});
					groupLiveRectsRef.current = map;
					setGroupLiveRects(map);
					const rectMap = {};
					Object.entries(map).forEach(([id, r]) => {
						rectMap[id] = {
							left: r.x,
							top: r.y,
							width: r.width,
							height: r.height
						};
					});
					onLiveRectsChange === null || onLiveRectsChange === void 0 || onLiveRectsChange(rectMap);
					return;
				}
				const dx = (e.clientX - interaction.startX) / scale;
				const dy = (e.clientY - interaction.startY) / scale;
				const rawNext = {
					x: interaction.itemX + dx,
					y: interaction.itemY + dy,
					width: interaction.itemW,
					height: interaction.itemH
				};
				const snap = require_alignSnap.snapRectToOthers({
					id: interaction.itemId,
					left: rawNext.x,
					top: rawNext.y,
					width: rawNext.width,
					height: rawNext.height
				}, otherRects, {
					mode: "drag",
					threshold
				});
				const next = {
					x: snap.rect.left,
					y: snap.rect.top,
					width: snap.rect.width,
					height: snap.rect.height
				};
				liveRectRef.current = next;
				setLiveRect(require_objectSpread2._objectSpread2({ id: interaction.itemId }, next));
				onLiveRectChange === null || onLiveRectChange === void 0 || onLiveRectChange(interaction.itemId, {
					left: next.x,
					top: next.y,
					width: next.width,
					height: next.height
				});
				setAlignGuides(snap.guides);
				onAlignGuidesChange === null || onAlignGuidesChange === void 0 || onAlignGuidesChange(snap.guides);
			} else {
				const dx = (e.clientX - interaction.startX) / scale;
				const dy = (e.clientY - interaction.startY) / scale;
				const { handle, startW, startH, startItemX, startItemY } = interaction;
				let w = startW;
				let h = startH;
				let x = startItemX;
				let y = startItemY;
				if (handle.includes("e")) w = Math.max(20, startW + dx);
				if (handle.includes("w")) {
					w = Math.max(20, startW - dx);
					x = startItemX + dx;
				}
				if (handle.includes("s")) h = Math.max(20, startH + dy);
				if (handle.includes("n")) {
					h = Math.max(20, startH - dy);
					y = startItemY + dy;
				}
				const movableEdgesX = handle.includes("w") ? ["left"] : handle.includes("e") ? ["right"] : [];
				const movableEdgesY = handle.includes("n") ? ["top"] : handle.includes("s") ? ["bottom"] : [];
				const snap = require_alignSnap.snapRectToOthers({
					id: interaction.itemId,
					left: x,
					top: y,
					width: w,
					height: h
				}, otherRects, {
					mode: "resize",
					threshold,
					movableEdgesX,
					movableEdgesY
				});
				const next = {
					x: snap.rect.left,
					y: snap.rect.top,
					width: Math.max(20, snap.rect.width),
					height: Math.max(20, snap.rect.height)
				};
				liveRectRef.current = next;
				setLiveRect(require_objectSpread2._objectSpread2({ id: interaction.itemId }, next));
				onLiveRectChange === null || onLiveRectChange === void 0 || onLiveRectChange(interaction.itemId, {
					left: next.x,
					top: next.y,
					width: next.width,
					height: next.height
				});
				setAlignGuides(snap.guides);
				onAlignGuidesChange === null || onAlignGuidesChange === void 0 || onAlignGuidesChange(snap.guides);
			}
		};
		const onUp = () => {
			const current = interaction;
			if (!current) return;
			const last = liveRectRef.current;
			const groupLast = groupLiveRectsRef.current;
			onInteractionChange === null || onInteractionChange === void 0 || onInteractionChange(false);
			onLiveRectChange === null || onLiveRectChange === void 0 || onLiveRectChange(null, null);
			onLiveRectsChange === null || onLiveRectsChange === void 0 || onLiveRectsChange(null);
			setAlignGuides([]);
			onAlignGuidesChange === null || onAlignGuidesChange === void 0 || onAlignGuidesChange([]);
			setLiveRect(null);
			liveRectRef.current = null;
			setGroupLiveRects(null);
			groupLiveRectsRef.current = null;
			setInteraction(null);
			if (current.type === "drag") {
				if (current.group) {
					const zone = groupLast === null || groupLast === void 0 ? void 0 : groupLast[current.itemId];
					if (zone && (Math.abs(zone.x - current.itemX) > DRAG_THRESHOLD || Math.abs(zone.y - current.itemY) > DRAG_THRESHOLD)) {
						/** 以选区吸附后的位移为准，整组套用同一 delta，保持成员相对布局 */
						const snappedZone = require_snap.snapRect(zone.x, zone.y, 0, 0, snapStep);
						const ddx = snappedZone.x - current.itemX;
						const ddy = snappedZone.y - current.itemY;
						const patches = [{
							id: current.itemId,
							patch: {
								x: Math.round(current.itemX + ddx),
								y: Math.round(current.itemY + ddy)
							}
						}, ...current.group.map((m) => ({
							id: m.id,
							patch: {
								x: Math.round(m.x + ddx),
								y: Math.round(m.y + ddy)
							}
						}))];
						onUpdateItemsBatch === null || onUpdateItemsBatch === void 0 || onUpdateItemsBatch(patches);
					}
					return;
				}
				if (last && (Math.abs(last.x - current.itemX) > DRAG_THRESHOLD || Math.abs(last.y - current.itemY) > DRAG_THRESHOLD)) {
					const snapped = require_snap.snapRect(last.x, last.y, 0, 0, snapStep);
					onUpdateItem(current.itemId, {
						x: Math.round(snapped.x),
						y: Math.round(snapped.y)
					});
				}
			} else if (last) {
				const snapped = require_snap.snapRect(last.x, last.y, last.width, last.height, snapStep);
				onUpdateItem(current.itemId, {
					x: Math.round(snapped.x),
					y: Math.round(snapped.y),
					width: Math.round(snapped.width),
					height: Math.round(snapped.height)
				});
			}
		};
		const opts = { capture: true };
		document.addEventListener("pointermove", onMove, opts);
		document.addEventListener("pointerup", onUp, opts);
		document.addEventListener("pointercancel", onUp, opts);
		return () => {
			document.removeEventListener("pointermove", onMove, opts);
			document.removeEventListener("pointerup", onUp, opts);
			document.removeEventListener("pointercancel", onUp, opts);
		};
	}, [
		interaction,
		scale,
		snapStep,
		onUpdateItem,
		onUpdateItemsBatch,
		onInteractionChange,
		onLiveRectChange,
		onLiveRectsChange,
		items,
		getBaseRect,
		alignSnapThresholdPx,
		onAlignGuidesChange
	]);
	const handleItemClick = (0, react.useCallback)((e, item) => {
		if (interaction) return;
		e.stopPropagation();
		onSelect(item.id);
	}, [interaction, onSelect]);
	const itemNodes = (0, react.useMemo)(() => {
		return [...items].sort((a, b) => {
			var _a$zIndex, _b$zIndex;
			return ((_a$zIndex = a.zIndex) !== null && _a$zIndex !== void 0 ? _a$zIndex : 0) - ((_b$zIndex = b.zIndex) !== null && _b$zIndex !== void 0 ? _b$zIndex : 0);
		}).map((item, index) => {
			var _item$zIndex, _item$zIndex2;
			const key = getItemKey(item, index, rowKey);
			const merged = item;
			const style = getItemStyle(item, index);
			const shapeStyle = getShapeStyle(item.shape);
			const isSelected = selectedId === item.id;
			const wrapperStyle = getMergedItemWrapperStyle === null || getMergedItemWrapperStyle === void 0 ? void 0 : getMergedItemWrapperStyle(item, index);
			const baseZ = (wrapperStyle === null || wrapperStyle === void 0 ? void 0 : wrapperStyle.zIndex) !== void 0 ? wrapperStyle.zIndex : (_item$zIndex = item.zIndex) !== null && _item$zIndex !== void 0 ? _item$zIndex : 0;
			return /* @__PURE__ */ react.default.createElement("div", {
				key,
				className: (0, classnames.default)(`${PREFIX}-editable-item`, {
					[`${PREFIX}-editable-item-selected`]: isSelected,
					[`${PREFIX}-editable-item-selected-lock`]: isSelected && isSceneLayoutTransformLocked(merged)
				}),
				style: require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({
					position: "absolute",
					left: style.left,
					top: style.top,
					width: style.width,
					height: style.height,
					boxSizing: "border-box"
				}, shapeStyle), wrapperStyle), {}, { zIndex: isSelected ? SELECTED_ITEM_STACK_Z + ((_item$zIndex2 = item.zIndex) !== null && _item$zIndex2 !== void 0 ? _item$zIndex2 : 0) : baseZ }),
				onPointerDown: (e) => handlePointerDown(e, item, index),
				onClick: (e) => handleItemClick(e, item)
			}, /* @__PURE__ */ react.default.createElement("div", { className: `${PREFIX}-editable-item-inner` }, renderItem(item, index, { viewportScale: scale })), isSelected && !isSceneLayoutTransformLocked(merged) && /* @__PURE__ */ react.default.createElement(react.default.Fragment, null, RESIZE_CORNER_HANDLES.map((h) => {
				/** viewport scale 补偿：保持视觉 20px 不随画布放大变大 */
				const size = 20 / Math.max(scale, .01);
				const half = size / 2;
				return /* @__PURE__ */ react.default.createElement("div", {
					key: h,
					className: (0, classnames.default)(`${PREFIX}-editable-resize-handle`, `${PREFIX}-editable-resize-handle-${h}`),
					onPointerDown: (e) => handleResizePointerDown(e, item.id, h),
					style: {
						position: "absolute",
						width: size,
						height: size,
						marginTop: -half,
						marginRight: -half,
						marginBottom: -half,
						marginLeft: -half
					}
				});
			})), isSelected && editQuickBar ? /* @__PURE__ */ react.default.createElement(require_FloorMapEditQuickActions.FloorMapEditQuickActions, {
				labels: editQuickBar.labels,
				isLocked: editQuickBar.isLocked,
				onToggleLock: editQuickBar.onToggleLock,
				onDuplicate: editQuickBar.onDuplicate,
				onDelete: editQuickBar.onDelete,
				viewportScale: scale
			}) : null);
		});
	}, [
		items,
		renderItem,
		rowKey,
		getItemStyle,
		selectedId,
		handlePointerDown,
		handleItemClick,
		handleResizePointerDown,
		getMergedItemWrapperStyle,
		editQuickBar,
		scale
	]);
	return /* @__PURE__ */ react.default.createElement("div", {
		ref: contentRef,
		className: (0, classnames.default)(`${PREFIX}-editable-item-layer`, className),
		style: {
			position: "absolute",
			left: 0,
			top: 0,
			width: contentWidth,
			height: contentHeight,
			minWidth: contentWidth,
			minHeight: contentHeight
		}
	}, contentOffsetX !== 0 || contentOffsetY !== 0 ? /* @__PURE__ */ react.default.createElement("div", { style: {
		position: "absolute",
		left: 0,
		top: 0,
		width: contentWidth,
		height: contentHeight,
		transform: `translate(${contentOffsetX}px, ${contentOffsetY}px)`
	} }, itemNodes, alignGuides.length > 0 ? /* @__PURE__ */ react.default.createElement(AlignGuideOverlay, {
		guides: alignGuides,
		width: contentWidth,
		height: contentHeight,
		scale,
		stroke: alignGuideStroke
	}) : null) : /* @__PURE__ */ react.default.createElement(react.default.Fragment, null, itemNodes, alignGuides.length > 0 ? /* @__PURE__ */ react.default.createElement(AlignGuideOverlay, {
		guides: alignGuides,
		width: contentWidth,
		height: contentHeight,
		scale,
		stroke: alignGuideStroke
	}) : null));
}
/** 拖动/缩放时的对齐辅助线层；线宽按 scale 反向缩放，保持屏幕恒定 1px */
function AlignGuideOverlay(props) {
	const { guides, width, height, scale, stroke } = props;
	const safeScale = Math.max(scale, .01);
	const sw = 1 / safeScale;
	return /* @__PURE__ */ react.default.createElement("svg", {
		width,
		height,
		style: {
			position: "absolute",
			left: 0,
			top: 0,
			pointerEvents: "none",
			overflow: "visible"
		},
		"aria-hidden": true
	}, guides.map((g, i) => {
		if (g.orientation === "v") return /* @__PURE__ */ react.default.createElement("line", {
			key: `v-${i}-${g.position}`,
			x1: g.position,
			y1: g.start,
			x2: g.position,
			y2: g.end,
			stroke,
			strokeWidth: sw,
			strokeDasharray: `${4 / safeScale} ${3 / safeScale}`
		});
		return /* @__PURE__ */ react.default.createElement("line", {
			key: `h-${i}-${g.position}`,
			x1: g.start,
			y1: g.position,
			x2: g.end,
			y2: g.position,
			stroke,
			strokeWidth: sw,
			strokeDasharray: `${4 / safeScale} ${3 / safeScale}`
		});
	}));
}
//#endregion
exports.default = EditableItemLayer;
