"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var canonical_remap_exports = {};
__export(canonical_remap_exports, {
  applyBorderEdit: () => applyBorderEdit,
  canonicalizeBorders: () => canonicalizeBorders,
  extractCellPositions: () => extractCellPositions
});
module.exports = __toCommonJS(canonical_remap_exports);
var import_tablemap = require("../tiptap/tablemap.js");
var import_edge_detection = require("./edge-detection.js");
function isExplicitBorder(cell, side) {
  const hasColor = cell.attrs.borderColors?.[side] !== void 0;
  const hasWidth = cell.attrs.borderWidths?.[side] !== void 0;
  return { hasColor, hasWidth };
}
function getBorderStyle(cell, side) {
  return {
    color: cell.attrs.borderColors?.[side],
    width: cell.attrs.borderWidths?.[side]
  };
}
function borderStylesEqual(a, b) {
  return a.color === b.color && a.width === b.width;
}
function hasUniformBorderStyle(cells, side, targetStyle) {
  if (cells.length === 0) {
    return false;
  }
  return cells.every((cell) => {
    const cellStyle = getBorderStyle(cell.node, side);
    return borderStylesEqual(cellStyle, targetStyle);
  });
}
function allCellsHaveDefaultBorder(cells, side) {
  return cells.every((cell) => {
    const explicit = isExplicitBorder(cell.node, side);
    return !explicit.hasColor && !explicit.hasWidth;
  });
}
function getEdgeRange(edge) {
  return edge.orientation === "horizontal" ? edge.colRange : edge.rowRange;
}
function getCellRange(cell, edge) {
  if (edge.orientation === "horizontal") {
    return { start: cell.col, end: cell.col + cell.colspan };
  } else {
    return { start: cell.row, end: cell.row + cell.rowspan };
  }
}
function isCellFullyContained(cell, edgeStart, edgeEnd, edge) {
  const { start: cellStart, end: cellEnd } = getCellRange(cell, edge);
  return cellStart >= edgeStart && cellEnd <= edgeEnd;
}
function setBorderStyle(update, side, style) {
  if (style.color !== void 0) {
    update.borderColors[side] = style.color;
  }
  if (style.width !== void 0) {
    update.borderWidths[side] = style.width;
  }
}
function clearBorderStyle(update, side) {
  update.borderColors[side] = void 0;
  update.borderWidths[side] = void 0;
}
function applyBorderStyleToCell(cell, side, style, clear, updates) {
  const update = getOrCreateUpdate(updates, cell.pos, cell.node);
  if (clear) {
    clearBorderStyle(update, side);
  } else {
    setBorderStyle(update, side, style);
  }
}
function extractCellPositions(tableNode, tableStart) {
  const map = import_tablemap.TableMap.get(tableNode);
  const cells = [];
  const seen = /* @__PURE__ */ new Set();
  let _rowIndex = 0;
  tableNode.forEach((rowNode, rowOffset) => {
    if (rowNode.type.spec.tableRole === "row") {
      let _colIndex = 0;
      rowNode.forEach((cellNode, cellOffset) => {
        if (cellNode.type.spec.tableRole === "cell" || cellNode.type.spec.tableRole === "header_cell") {
          const cellPos = tableStart + rowOffset + cellOffset + 1;
          if (!seen.has(cellPos)) {
            seen.add(cellPos);
            const placement = map.findCell(cellPos - tableStart);
            cells.push({
              pos: cellPos,
              node: cellNode,
              row: placement.row,
              col: placement.col,
              rowspan: placement.rowspan,
              colspan: placement.colspan
            });
          }
        }
        _colIndex++;
      });
      _rowIndex++;
    }
  });
  return { cells, width: map.width, height: map.height };
}
function applyBorderEdit(tr, tableNode, tableStart, selectedCells, side, style, clear = false) {
  const { cells, width, height } = extractCellPositions(tableNode, tableStart);
  const grid = (0, import_edge_detection.buildCellGrid)(cells, width, height);
  const selectedPositions = new Set(selectedCells.map((c) => c.pos));
  const selectedCellsData = cells.filter((c) => selectedPositions.has(c.pos));
  const cellSpacing = tableNode.attrs.tableData?.cellSpacing ?? 0;
  if (cellSpacing > 0) {
    const updates = /* @__PURE__ */ new Map();
    const sidesToUpdate = side === null ? ["top", "bottom", "left", "right"] : [side];
    for (const cell of selectedCellsData) {
      for (const s of sidesToUpdate) {
        applyBorderStyleToCell(cell, s, style, clear, updates);
      }
    }
    applyUpdates(tr, updates);
    return;
  }
  if (side === null) {
    applyBorderToAllSides(tr, selectedCellsData, grid, width, height, style, clear);
  } else {
    const updates = /* @__PURE__ */ new Map();
    applyBorderToSide(tr, selectedCellsData, side, grid, width, height, style, clear, updates);
    applyUpdates(tr, updates);
  }
}
function applyBorderToAllSides(tr, selectedCells, grid, _width, _height, style, clear) {
  const updates = /* @__PURE__ */ new Map();
  const sides = ["top", "bottom", "left", "right"];
  for (const side of sides) {
    applyBorderToSide(tr, selectedCells, side, grid, _width, _height, style, clear, updates);
  }
  applyUpdates(tr, updates);
}
function applyBorderToSide(_tr, selectedCells, side, grid, width, height, style, clear, updates) {
  const canonical = (0, import_edge_detection.isCanonicalSide)(side);
  const edgeMap = /* @__PURE__ */ new Map();
  for (const cell of selectedCells) {
    const edgeKey = getEdgeKey(cell, side);
    if (!edgeMap.has(edgeKey)) {
      edgeMap.set(edgeKey, []);
    }
    edgeMap.get(edgeKey).push(cell);
  }
  for (const [_edgeKey, cellsOnEdge] of edgeMap.entries()) {
    const edge = computeEdgeFromCells(cellsOnEdge, side, grid, width, height);
    if (!edge) {
      continue;
    }
    if (canonical) {
      applyCanonicalEdit(cellsOnEdge, side, edge, style, clear, updates);
    } else {
      applyNonCanonicalEdit(cellsOnEdge, side, edge, style, clear, updates);
    }
  }
}
function applyCanonicalEdit(cells, side, edge, style, clear, updates) {
  const oppositeSide = (0, import_edge_detection.getOppositeSide)(side);
  const oppositeCells = (0, import_edge_detection.getNonCanonicalCells)(edge);
  for (const cell of cells) {
    applyBorderStyleToCell(cell, side, style, clear, updates);
  }
  if (!clear && oppositeCells.length > 0) {
    const { start: edgeStart, end: edgeEnd } = getEdgeRange(edge);
    for (const cell of oppositeCells) {
      if (isCellFullyContained(cell, edgeStart, edgeEnd, edge)) {
        applyBorderStyleToCell(cell, oppositeSide, style, false, updates);
      }
    }
  }
}
function applyNonCanonicalEdit(cells, side, edge, style, clear, updates) {
  const canonicalSide = (0, import_edge_detection.getOppositeSide)(side);
  const canonicalCells = (0, import_edge_detection.getCanonicalCells)(edge);
  const { start: edgeStart, end: edgeEnd } = getEdgeRange(edge);
  const exactCoverage = (0, import_edge_detection.isExactCoverage)(edge);
  if (exactCoverage && !clear) {
    for (const cell of canonicalCells) {
      applyBorderStyleToCell(cell, canonicalSide, style, false, updates);
    }
    for (const cell of cells) {
      applyBorderStyleToCell(cell, side, style, false, updates);
    }
  } else if (exactCoverage && clear) {
    const canonicalHasDuplicate = hasUniformBorderStyle(cells, side, { color: void 0, width: void 0 }) || hasUniformBorderStyle(canonicalCells, canonicalSide, getBorderStyle(cells[0].node, side));
    for (const cell of cells) {
      const update = getOrCreateUpdate(updates, cell.pos, cell.node);
      clearBorderStyle(update, side);
    }
    if (canonicalHasDuplicate) {
      for (const cell of canonicalCells) {
        const update = getOrCreateUpdate(updates, cell.pos, cell.node);
        clearBorderStyle(update, canonicalSide);
      }
    }
  } else {
    for (const cell of cells) {
      applyBorderStyleToCell(cell, side, style, clear, updates);
    }
    if (!clear) {
      for (const cell of canonicalCells) {
        if (isCellFullyContained(cell, edgeStart, edgeEnd, edge)) {
          applyBorderStyleToCell(cell, canonicalSide, style, false, updates);
        }
      }
    }
  }
}
function getEdgeKey(cell, side) {
  switch (side) {
    case "top":
      return `h:${cell.row}:${cell.col}:${cell.col + cell.colspan}`;
    case "bottom":
      return `h:${cell.row + cell.rowspan}:${cell.col}:${cell.col + cell.colspan}`;
    case "left":
      return `v:${cell.col}:${cell.row}:${cell.row + cell.rowspan}`;
    case "right":
      return `v:${cell.col + cell.colspan}:${cell.row}:${cell.row + cell.rowspan}`;
    default:
      return "";
  }
}
function computeEdgeFromCells(cells, side, grid, _width, _height) {
  if (cells.length === 0) {
    return null;
  }
  if (side === "top" || side === "bottom") {
    let minCol = Infinity;
    let maxCol = -Infinity;
    let rowEdgeIndex = -1;
    for (const cell of cells) {
      minCol = Math.min(minCol, cell.col);
      maxCol = Math.max(maxCol, cell.col + cell.colspan);
      rowEdgeIndex = side === "top" ? cell.row : cell.row + cell.rowspan;
    }
    return (0, import_edge_detection.computeHorizontalEdge)(grid, rowEdgeIndex, minCol, maxCol, side);
  } else {
    let minRow = Infinity;
    let maxRow = -Infinity;
    let colEdgeIndex = -1;
    for (const cell of cells) {
      minRow = Math.min(minRow, cell.row);
      maxRow = Math.max(maxRow, cell.row + cell.rowspan);
      colEdgeIndex = side === "left" ? cell.col : cell.col + cell.colspan;
    }
    return (0, import_edge_detection.computeVerticalEdge)(grid, colEdgeIndex, minRow, maxRow, side);
  }
}
function getOrCreateUpdate(updates, pos, node) {
  if (!updates.has(pos)) {
    updates.set(pos, {
      pos,
      borderColors: { ...node.attrs.borderColors || {} },
      borderWidths: { ...node.attrs.borderWidths || {} }
    });
  }
  return updates.get(pos);
}
function applyUpdates(tr, updates) {
  for (const update of updates.values()) {
    const node = tr.doc.nodeAt(update.pos);
    if (!node) {
      continue;
    }
    tr.setNodeMarkup(update.pos, null, {
      ...node.attrs,
      borderColors: update.borderColors,
      borderWidths: update.borderWidths
    });
  }
}
function canonicalizeBorders(tr, tableNode, tableStart) {
  const cellSpacing = tableNode.attrs.tableData?.cellSpacing ?? 0;
  if (cellSpacing > 0) {
    return;
  }
  const { cells, width, height } = extractCellPositions(tableNode, tableStart);
  const grid = (0, import_edge_detection.buildCellGrid)(cells, width, height);
  const updates = /* @__PURE__ */ new Map();
  const allEdges = (0, import_edge_detection.getAllSharedEdges)(grid, width, height);
  for (const edge of allEdges) {
    canonicalizeEdge(edge, updates);
  }
  applyUpdates(tr, updates);
}
function canonicalizeEdge(edge, updates) {
  const canonicalCells = (0, import_edge_detection.getCanonicalCells)(edge);
  const nonCanonicalCells = (0, import_edge_detection.getNonCanonicalCells)(edge);
  if (canonicalCells.length === 0 || nonCanonicalCells.length === 0) {
    return;
  }
  const canonicalSide = edge.canonicalSide;
  const nonCanonicalSide = (0, import_edge_detection.getOppositeSide)(canonicalSide);
  const canonicalStyles = canonicalCells.map((c) => getBorderStyle(c.node, canonicalSide));
  const nonCanonicalStyles = nonCanonicalCells.map((c) => getBorderStyle(c.node, nonCanonicalSide));
  const canonicalUniform = canonicalStyles.every((s) => borderStylesEqual(s, canonicalStyles[0]));
  const nonCanonicalUniform = nonCanonicalStyles.every(
    (s) => borderStylesEqual(s, nonCanonicalStyles[0])
  );
  if (canonicalUniform && nonCanonicalUniform) {
    const canonicalStyle = canonicalStyles[0];
    const nonCanonicalStyle = nonCanonicalStyles[0];
    const bothExplicit = (canonicalStyle.color !== void 0 || canonicalStyle.width !== void 0) && (nonCanonicalStyle.color !== void 0 || nonCanonicalStyle.width !== void 0);
    if (bothExplicit && borderStylesEqual(canonicalStyle, nonCanonicalStyle)) {
      for (const cell of nonCanonicalCells) {
        const update = getOrCreateUpdate(updates, cell.pos, cell.node);
        clearBorderStyle(update, nonCanonicalSide);
      }
    }
  }
  if ((0, import_edge_detection.isExactCoverage)(edge)) {
    const canonicalAllDefault = allCellsHaveDefaultBorder(canonicalCells, canonicalSide);
    if (canonicalAllDefault && nonCanonicalUniform) {
      const nonCanonicalStyle = nonCanonicalStyles[0];
      const nonCanonicalExplicit = nonCanonicalStyle.color !== void 0 || nonCanonicalStyle.width !== void 0;
      if (nonCanonicalExplicit) {
        for (const cell of canonicalCells) {
          const update = getOrCreateUpdate(updates, cell.pos, cell.node);
          setBorderStyle(update, canonicalSide, nonCanonicalStyle);
        }
        for (const cell of nonCanonicalCells) {
          const update = getOrCreateUpdate(updates, cell.pos, cell.node);
          clearBorderStyle(update, nonCanonicalSide);
        }
      }
    }
  }
}
//# sourceMappingURL=canonical-remap.js.map
