function buildCellGrid(tableCells, tableWidth, tableHeight) {
  const grid = Array(tableHeight).fill(null).map(() => Array(tableWidth).fill(null));
  for (const cell of tableCells) {
    for (let r = cell.row; r < cell.row + cell.rowspan; r++) {
      for (let c = cell.col; c < cell.col + cell.colspan; c++) {
        if (r < tableHeight && c < tableWidth) {
          grid[r][c] = cell;
        }
      }
    }
  }
  return grid;
}
function getCellAt(grid, row, col) {
  if (row < 0 || row >= grid.length || col < 0 || col >= grid[0]?.length) {
    return null;
  }
  return grid[row][col] || null;
}
function collectUniqueCells(grid, indices, getCell, shouldInclude) {
  const cells = [];
  const seen = /* @__PURE__ */ new Set();
  for (const idx of indices) {
    const cell = getCell(idx);
    if (cell && !seen.has(cell.pos) && shouldInclude(cell, idx)) {
      cells.push(cell);
      seen.add(cell.pos);
    }
  }
  return cells;
}
function computeHorizontalEdge(grid, rowEdgeIndex, colStart, colEnd, _side) {
  const tableHeight = grid.length;
  const tableWidth = grid[0]?.length || 0;
  const columns = Array.from({ length: colEnd - colStart }, (_, i) => colStart + i).filter(
    (c) => c < tableWidth
  );
  const aboveCells = rowEdgeIndex > 0 ? collectUniqueCells(
    grid,
    columns,
    (c) => getCellAt(grid, rowEdgeIndex - 1, c),
    (cell, _) => cell.row + cell.rowspan === rowEdgeIndex
  ) : [];
  const belowCells = rowEdgeIndex < tableHeight ? collectUniqueCells(
    grid,
    columns,
    (c) => getCellAt(grid, rowEdgeIndex, c),
    (cell, _) => cell.row === rowEdgeIndex
  ) : [];
  return {
    orientation: "horizontal",
    rowEdgeIndex,
    colRange: { start: colStart, end: colEnd },
    canonicalSide: "top",
    aboveCells,
    belowCells
  };
}
function computeVerticalEdge(grid, colEdgeIndex, rowStart, rowEnd, _side) {
  const tableHeight = grid.length;
  const tableWidth = grid[0]?.length || 0;
  const rows = Array.from({ length: rowEnd - rowStart }, (_, i) => rowStart + i).filter(
    (r) => r < tableHeight
  );
  const leftCells = colEdgeIndex > 0 ? collectUniqueCells(
    grid,
    rows,
    (r) => getCellAt(grid, r, colEdgeIndex - 1),
    (cell, _) => cell.col + cell.colspan === colEdgeIndex
  ) : [];
  const rightCells = colEdgeIndex < tableWidth ? collectUniqueCells(
    grid,
    rows,
    (r) => getCellAt(grid, r, colEdgeIndex),
    (cell, _) => cell.col === colEdgeIndex
  ) : [];
  return {
    orientation: "vertical",
    colEdgeIndex,
    rowRange: { start: rowStart, end: rowEnd },
    canonicalSide: "left",
    leftCells,
    rightCells
  };
}
function isExactCoverage(edge) {
  const canonicalCells = getCanonicalCells(edge);
  if (canonicalCells.length === 0) {
    return false;
  }
  const isHorizontal = edge.orientation === "horizontal";
  const { start: rangeStart, end: rangeEnd } = isHorizontal ? edge.colRange : edge.rowRange;
  let canonicalMin = Infinity;
  let canonicalMax = -Infinity;
  for (const cell of canonicalCells) {
    const cellStart = isHorizontal ? cell.col : cell.row;
    const cellEnd = isHorizontal ? cell.col + cell.colspan : cell.row + cell.rowspan;
    canonicalMin = Math.min(canonicalMin, cellStart);
    canonicalMax = Math.max(canonicalMax, cellEnd);
  }
  if (canonicalMin !== rangeStart || canonicalMax !== rangeEnd) {
    return false;
  }
  const covered = new Array(rangeEnd - rangeStart).fill(false);
  for (const cell of canonicalCells) {
    const cellStart = isHorizontal ? cell.col : cell.row;
    const cellEnd = isHorizontal ? cell.col + cell.colspan : cell.row + cell.rowspan;
    const clampedStart = Math.max(cellStart, rangeStart);
    const clampedEnd = Math.min(cellEnd, rangeEnd);
    for (let i = clampedStart; i < clampedEnd; i++) {
      const idx = i - rangeStart;
      if (idx >= 0 && idx < covered.length) {
        covered[idx] = true;
      }
    }
  }
  return covered.every((c) => c);
}
function getAllSharedEdges(grid, tableWidth, tableHeight) {
  const edges = [];
  for (let rowEdge = 1; rowEdge < tableHeight; rowEdge++) {
    const edgeSegments = getHorizontalEdgeSegments(grid, rowEdge, tableWidth);
    for (const segment of edgeSegments) {
      const edge = computeHorizontalEdge(grid, rowEdge, segment.start, segment.end, "top");
      if (edge.aboveCells.length > 0 && edge.belowCells.length > 0) {
        edges.push(edge);
      }
    }
  }
  for (let colEdge = 1; colEdge < tableWidth; colEdge++) {
    const edgeSegments = getVerticalEdgeSegments(grid, colEdge, tableHeight);
    for (const segment of edgeSegments) {
      const edge = computeVerticalEdge(grid, colEdge, segment.start, segment.end, "left");
      if (edge.leftCells.length > 0 && edge.rightCells.length > 0) {
        edges.push(edge);
      }
    }
  }
  return edges;
}
function getEdgeSegments(length, checkSharedEdge) {
  const segments = [];
  let segmentStart = 0;
  for (let i = 0; i < length; i++) {
    if (!checkSharedEdge(i) && i > segmentStart) {
      segments.push({ start: segmentStart, end: i });
      segmentStart = i;
    }
  }
  if (length > segmentStart) {
    segments.push({ start: segmentStart, end: length });
  }
  return segments.filter((s) => s.end > s.start);
}
function getHorizontalEdgeSegments(grid, rowEdgeIndex, tableWidth) {
  return getEdgeSegments(tableWidth, (c) => {
    const cellAbove = rowEdgeIndex > 0 ? getCellAt(grid, rowEdgeIndex - 1, c) : null;
    const cellBelow = rowEdgeIndex < grid.length ? getCellAt(grid, rowEdgeIndex, c) : null;
    return !!(cellAbove && cellBelow && cellAbove.pos !== cellBelow.pos && cellAbove.row + cellAbove.rowspan === rowEdgeIndex && cellBelow.row === rowEdgeIndex);
  });
}
function getVerticalEdgeSegments(grid, colEdgeIndex, tableHeight) {
  return getEdgeSegments(tableHeight, (r) => {
    const cellLeft = colEdgeIndex > 0 ? getCellAt(grid, r, colEdgeIndex - 1) : null;
    const cellRight = colEdgeIndex < (grid[0]?.length || 0) ? getCellAt(grid, r, colEdgeIndex) : null;
    return !!(cellLeft && cellRight && cellLeft.pos !== cellRight.pos && cellLeft.col + cellLeft.colspan === colEdgeIndex && cellRight.col === colEdgeIndex);
  });
}
function isCanonicalSide(side) {
  return side === "top" || side === "left";
}
function getOppositeSide(side) {
  switch (side) {
    case "top":
      return "bottom";
    case "bottom":
      return "top";
    case "left":
      return "right";
    case "right":
      return "left";
    default:
      return "top";
  }
}
function getCanonicalCells(edge) {
  if (edge.orientation === "horizontal") {
    return edge.canonicalSide === "top" ? edge.belowCells : edge.aboveCells;
  } else {
    return edge.canonicalSide === "left" ? edge.rightCells : edge.leftCells;
  }
}
function getNonCanonicalCells(edge) {
  if (edge.orientation === "horizontal") {
    return edge.canonicalSide === "top" ? edge.aboveCells : edge.belowCells;
  } else {
    return edge.canonicalSide === "left" ? edge.leftCells : edge.rightCells;
  }
}
function isCellOuterEdge(cell, side, tableWidth, tableHeight, isCellInSelection) {
  switch (side) {
    case "top":
      return cell.row === 0 || !isCellInSelection(cell.row - 1, cell.col);
    case "bottom":
      return cell.row + cell.rowspan >= tableHeight || !isCellInSelection(cell.row + cell.rowspan, cell.col);
    case "left":
      return cell.col === 0 || !isCellInSelection(cell.row, cell.col - 1);
    case "right":
      return cell.col + cell.colspan >= tableWidth || !isCellInSelection(cell.row, cell.col + cell.colspan);
    default:
      return false;
  }
}
const OUTER_SIDES = ["top", "bottom", "left", "right"];
function groupOuterEdgeCells(selected, getCellData, tableWidth, tableHeight, isCellInSelection) {
  const grouped = new Map(OUTER_SIDES.map((side) => [side, []]));
  for (const entry of selected) {
    const cellData = getCellData(entry);
    if (!cellData) {
      continue;
    }
    for (const side of OUTER_SIDES) {
      if (isCellOuterEdge(cellData, side, tableWidth, tableHeight, isCellInSelection)) {
        grouped.get(side).push(entry);
      }
    }
  }
  return grouped;
}
export {
  buildCellGrid,
  computeHorizontalEdge,
  computeVerticalEdge,
  getAllSharedEdges,
  getCanonicalCells,
  getCellAt,
  getNonCanonicalCells,
  getOppositeSide,
  groupOuterEdgeCells,
  isCanonicalSide,
  isCellOuterEdge,
  isExactCoverage
};
//# sourceMappingURL=edge-detection.js.map
