// packages/block-editor/src/components/grid/use-grid-layout-sync.js
import { useDispatch, useSelect } from "@wordpress/data";
import { useEffect, useMemo } from "@wordpress/element";
import { usePrevious } from "@wordpress/compose";
import { store as blockEditorStore } from "../../store";
import { GridRect } from "./utils";
import { setImmutably } from "../../utils/object";
function useGridLayoutSync({ clientId: gridClientId }) {
  const { gridLayout, blockOrder, selectedBlockLayout } = useSelect(
    (select) => {
      const { getBlockAttributes: getBlockAttributes2, getBlockOrder } = select(blockEditorStore);
      const selectedBlock = select(blockEditorStore).getSelectedBlock();
      return {
        gridLayout: getBlockAttributes2(gridClientId).layout ?? {},
        blockOrder: getBlockOrder(gridClientId),
        selectedBlockLayout: selectedBlock?.attributes.style?.layout
      };
    },
    [gridClientId]
  );
  const { getBlockAttributes, getBlockRootClientId } = useSelect(blockEditorStore);
  const { updateBlockAttributes, __unstableMarkNextChangeAsNotPersistent } = useDispatch(blockEditorStore);
  const selectedBlockRect = useMemo(
    () => selectedBlockLayout ? new GridRect(selectedBlockLayout) : null,
    [selectedBlockLayout]
  );
  const previouslySelectedBlockRect = usePrevious(selectedBlockRect);
  const previousIsManualPlacement = usePrevious(
    gridLayout.isManualPlacement
  );
  const previousBlockOrder = usePrevious(blockOrder);
  useEffect(() => {
    const updates = {};
    if (gridLayout.isManualPlacement) {
      const occupiedRects = [];
      for (const clientId of blockOrder) {
        const {
          columnStart,
          rowStart,
          columnSpan = 1,
          rowSpan = 1
        } = getBlockAttributes(clientId).style?.layout ?? {};
        if (!columnStart || !rowStart) {
          continue;
        }
        occupiedRects.push(
          new GridRect({
            columnStart,
            rowStart,
            columnSpan,
            rowSpan
          })
        );
      }
      for (const clientId of blockOrder) {
        const attributes = getBlockAttributes(clientId);
        const {
          columnStart,
          rowStart,
          columnSpan = 1,
          rowSpan = 1
        } = attributes.style?.layout ?? {};
        if (columnStart && rowStart) {
          continue;
        }
        const [newColumnStart, newRowStart] = placeBlock(
          occupiedRects,
          gridLayout.columnCount,
          columnSpan,
          rowSpan,
          previouslySelectedBlockRect?.columnEnd,
          previouslySelectedBlockRect?.rowEnd
        );
        occupiedRects.push(
          new GridRect({
            columnStart: newColumnStart,
            rowStart: newRowStart,
            columnSpan,
            rowSpan
          })
        );
        updates[clientId] = {
          style: {
            ...attributes.style,
            layout: {
              ...attributes.style?.layout,
              columnStart: newColumnStart,
              rowStart: newRowStart
            }
          }
        };
      }
      const bottomMostRow = Math.max(
        ...occupiedRects.map((r) => r.rowEnd)
      );
      if (!gridLayout.rowCount || gridLayout.rowCount < bottomMostRow) {
        updates[gridClientId] = {
          layout: {
            ...gridLayout,
            rowCount: bottomMostRow
          }
        };
      }
      for (const clientId of previousBlockOrder ?? []) {
        if (!blockOrder.includes(clientId)) {
          const rootClientId = getBlockRootClientId(clientId);
          if (rootClientId === null) {
            continue;
          }
          const rootAttributes = getBlockAttributes(rootClientId);
          if (rootAttributes?.layout?.type === "grid") {
            continue;
          }
          const attributes = getBlockAttributes(clientId);
          const {
            columnStart,
            rowStart,
            columnSpan,
            rowSpan,
            ...layout
          } = attributes.style?.layout ?? {};
          if (columnStart || rowStart || columnSpan || rowSpan) {
            const hasEmptyLayoutAttribute = Object.keys(layout).length === 0;
            updates[clientId] = setImmutably(
              attributes,
              ["style", "layout"],
              hasEmptyLayoutAttribute ? void 0 : layout
            );
          }
        }
      }
    } else {
      if (previousIsManualPlacement === true) {
        for (const clientId of blockOrder) {
          const attributes = getBlockAttributes(clientId);
          const { columnStart, rowStart, ...layout } = attributes.style?.layout ?? {};
          if (columnStart || rowStart) {
            const hasEmptyLayoutAttribute = Object.keys(layout).length === 0;
            updates[clientId] = setImmutably(
              attributes,
              ["style", "layout"],
              hasEmptyLayoutAttribute ? void 0 : layout
            );
          }
        }
      }
      if (gridLayout.rowCount) {
        updates[gridClientId] = {
          layout: {
            ...gridLayout,
            rowCount: void 0
          }
        };
      }
    }
    if (Object.keys(updates).length) {
      __unstableMarkNextChangeAsNotPersistent();
      updateBlockAttributes(
        Object.keys(updates),
        updates,
        /* uniqueByBlock: */
        true
      );
    }
  }, [
    // Actual deps to sync:
    gridClientId,
    gridLayout,
    previousBlockOrder,
    blockOrder,
    previouslySelectedBlockRect,
    previousIsManualPlacement,
    // These won't change, but the linter thinks they might:
    __unstableMarkNextChangeAsNotPersistent,
    getBlockAttributes,
    getBlockRootClientId,
    updateBlockAttributes
  ]);
}
function placeBlock(occupiedRects, gridColumnCount, blockColumnSpan, blockRowSpan, startColumn = 1, startRow = 1) {
  for (let row = startRow; ; row++) {
    for (let column = row === startRow ? startColumn : 1; column <= gridColumnCount; column++) {
      const candidateRect = new GridRect({
        columnStart: column,
        rowStart: row,
        columnSpan: blockColumnSpan,
        rowSpan: blockRowSpan
      });
      if (!occupiedRects.some(
        (r) => r.intersectsRect(candidateRect)
      )) {
        return [column, row];
      }
    }
  }
}
export {
  useGridLayoutSync
};
//# sourceMappingURL=use-grid-layout-sync.js.map
