"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 tablemap_exports = {};
__export(tablemap_exports, {
  TableMap: () => TableMap
});
module.exports = __toCommonJS(tablemap_exports);
let readFromCache;
let addToCache;
if (typeof WeakMap !== "undefined") {
  let cache = /* @__PURE__ */ new WeakMap();
  readFromCache = (key) => cache.get(key);
  addToCache = (key, value) => {
    cache.set(key, value);
    return value;
  };
} else {
  const cache = [];
  const cacheSize = 10;
  let cachePos = 0;
  readFromCache = (key) => {
    for (let i = 0; i < cache.length; i += 2) {
      if (cache[i] == key) {
        return cache[i + 1];
      }
    }
  };
  addToCache = (key, value) => {
    if (cachePos === cacheSize) {
      cachePos = 0;
    }
    cache[cachePos++] = key;
    return cache[cachePos++] = value;
  };
}
class TableMap {
  constructor(width, height, map, problems) {
    this.width = width;
    this.height = height;
    this.map = map;
    this.problems = problems;
    this._cellPlacements = null;
  }
  get cellPlacements() {
    if (!this._cellPlacements) {
      this._cellPlacements = /* @__PURE__ */ new Map();
      const seen = /* @__PURE__ */ new Set();
      for (let row = 0; row < this.height; row++) {
        for (let col = 0; col < this.width; col++) {
          const pos = this.map[row * this.width + col];
          if (seen.has(pos)) {
            continue;
          }
          seen.add(pos);
          let right = col + 1;
          while (right < this.width && this.map[row * this.width + right] === pos) {
            right++;
          }
          let bottom = row + 1;
          while (bottom < this.height && this.map[bottom * this.width + col] === pos) {
            bottom++;
          }
          this._cellPlacements.set(pos, {
            col,
            row,
            colspan: right - col,
            rowspan: bottom - row
          });
        }
      }
    }
    return this._cellPlacements;
  }
  // Find the placement of the cell at the given position.
  findCell(pos) {
    const placement = this.cellPlacements.get(pos);
    if (!placement) {
      throw new RangeError(`No cell with offset ${pos} found`);
    }
    return placement;
  }
  // Iterate each unique cell exactly once, in reading order (left-to-right, top-to-bottom).
  forEachCell(callback) {
    this.cellPlacements.forEach((placement, pos) => callback(pos, placement));
  }
  // Find the left side of the cell at the given position.
  colCount(pos) {
    for (let i = 0; i < this.map.length; i++) {
      if (this.map[i] == pos) {
        return i % this.width;
      }
    }
    throw new RangeError(`No cell with offset ${pos} found`);
  }
  // Find the next cell in the given direction, starting from the cell
  // at `pos`, if any.
  nextCell(pos, axis, dir) {
    const { col, row, colspan, rowspan } = this.findCell(pos);
    if (axis == "horiz") {
      if (dir < 0 ? col == 0 : col + colspan == this.width) {
        return null;
      }
      return this.map[row * this.width + (dir < 0 ? col - 1 : col + colspan)];
    } else {
      if (dir < 0 ? row == 0 : row + rowspan == this.height) {
        return null;
      }
      return this.map[col + this.width * (dir < 0 ? row - 1 : row + rowspan)];
    }
  }
  // Get the rectangle spanning the two given cells.
  rectBetween(...positions) {
    const placements = positions.map((pos) => this.findCell(pos));
    let bounds = placements.reduce(
      (acc, p) => ({
        top: Math.min(acc.top, p.row),
        left: Math.min(acc.left, p.col),
        bottom: Math.max(acc.bottom, p.row + p.rowspan),
        right: Math.max(acc.right, p.col + p.colspan)
      }),
      {
        top: placements[0].row,
        left: placements[0].col,
        bottom: placements[0].row + placements[0].rowspan,
        right: placements[0].col + placements[0].colspan
      }
    );
    const visited = /* @__PURE__ */ new Set();
    const queue = [];
    for (let r = bounds.top; r < bounds.bottom; r++) {
      for (let c = bounds.left; c < bounds.right; c++) {
        const pos = this.map[r * this.width + c];
        if (!visited.has(pos)) {
          visited.add(pos);
          queue.push(pos);
        }
      }
    }
    while (queue.length) {
      const pos = queue.shift();
      const p = this.findCell(pos);
      const newTop = Math.min(bounds.top, p.row);
      const newLeft = Math.min(bounds.left, p.col);
      const newBottom = Math.max(bounds.bottom, p.row + p.rowspan);
      const newRight = Math.max(bounds.right, p.col + p.colspan);
      const expanded = newTop < bounds.top || newLeft < bounds.left || newBottom > bounds.bottom || newRight > bounds.right;
      if (expanded) {
        bounds = { top: newTop, left: newLeft, bottom: newBottom, right: newRight };
        for (let r = bounds.top; r < bounds.bottom; r++) {
          for (let c = bounds.left; c < bounds.right; c++) {
            const p2 = this.map[r * this.width + c];
            if (!visited.has(p2)) {
              visited.add(p2);
              queue.push(p2);
            }
          }
        }
        if (bounds.top === 0 && bounds.left === 0 && bounds.bottom === this.height && bounds.right === this.width) {
          return bounds;
        }
      }
    }
    return bounds;
  }
  // Get the shallow rectangle spanning the two given cells.
  rectBetweenShallow(a, b) {
    const pa = this.findCell(a);
    const pb = this.findCell(b);
    return {
      left: Math.min(pa.col, pb.col),
      top: Math.min(pa.row, pb.row),
      right: Math.max(pa.col + pa.colspan, pb.col + pb.colspan),
      bottom: Math.max(pa.row + pa.rowspan, pb.row + pb.rowspan)
    };
  }
  // Return the position of all cells that have the top left corner in
  // the given rectangle.
  cellsInRect(rect) {
    const result = [];
    const seen = {};
    for (let row = rect.top; row < rect.bottom; row++) {
      for (let col = rect.left; col < rect.right; col++) {
        const index = row * this.width + col;
        const pos = this.map[index];
        if (seen[pos]) {
          continue;
        }
        seen[pos] = true;
        if (col == rect.left && col && this.map[index - 1] == pos || row == rect.top && row && this.map[index - this.width] == pos) {
          continue;
        }
        result.push(pos);
      }
    }
    return result;
  }
  // Return the position of all cells that overlap with the given rectangle,
  // including cells that start outside the rect but span into it.
  cellsOverlappingRect(rect) {
    const result = [];
    const seen = {};
    for (let row = rect.top; row < rect.bottom; row++) {
      for (let col = rect.left; col < rect.right; col++) {
        const index = row * this.width + col;
        const pos = this.map[index];
        if (seen[pos]) {
          continue;
        }
        seen[pos] = true;
        result.push(pos);
      }
    }
    return result;
  }
  // Return the position at which the cell at the given row and column
  // starts, or would start, if a cell started there.
  positionAt(row, col, table) {
    for (let i = 0, rowStart = 0; ; i++) {
      const rowEnd = rowStart + table.child(i).nodeSize;
      if (i == row) {
        let index = col + row * this.width;
        const rowEndIndex = (row + 1) * this.width;
        while (index < rowEndIndex && this.map[index] < rowStart) {
          index++;
        }
        return index == rowEndIndex ? rowEnd - 1 : this.map[index];
      }
      rowStart = rowEnd;
    }
  }
  // Find the table map for the given table node.
  static get(table) {
    return readFromCache(table) || addToCache(table, computeMap(table));
  }
  // Build a table map from a grid of cells without ProseMirror nodes.
  // Each row is an array of physical cells with colspan/rowspan.
  // The map array stores sequential cell indices (0-based) instead of byte offsets.
  static fromGrid(rows) {
    const height = rows.length;
    if (height === 0) {
      return new TableMap(0, 0, [], null);
    }
    let width = 0;
    for (const cell of rows[0]) {
      width += cell.colspan;
    }
    const map = new Array(width * height).fill(0);
    let problems = null;
    let cellIndex = 0;
    for (let row = 0; row < height; row++) {
      let mapPos = row * width;
      while (mapPos < (row + 1) * width && map[mapPos] !== 0) {
        mapPos++;
      }
      for (const cell of rows[row]) {
        while (mapPos < (row + 1) * width && map[mapPos] !== 0) {
          mapPos++;
        }
        const col = mapPos % width;
        for (let h = 0; h < cell.rowspan; h++) {
          if (h + row >= height) {
            (problems || (problems = [])).push({
              type: "overlong_rowspan",
              pos: cellIndex,
              n: cell.rowspan - h
            });
            break;
          }
          const start = (row + h) * width + col;
          for (let w = 0; w < cell.colspan; w++) {
            if (map[start + w] === 0) {
              map[start + w] = cellIndex + 1;
            } else {
              (problems || (problems = [])).push({
                type: "collision",
                row,
                pos: cellIndex,
                n: cell.colspan - w
              });
            }
          }
        }
        mapPos += cell.colspan;
        cellIndex++;
      }
    }
    for (let i = 0; i < map.length; i++) {
      map[i]--;
    }
    return new TableMap(width, height, map, problems);
  }
}
function computeMap(table) {
  if (table.type.spec.tableRole != "table") {
    throw new RangeError("Not a table node: " + table.type.name);
  }
  const width = findWidth(table), height = table.childCount;
  const map = [];
  let mapPos = 0;
  let problems = null;
  const colWidths = [];
  for (let i = 0, e = width * height; i < e; i++) {
    map[i] = 0;
  }
  for (let row = 0, pos = 0; row < height; row++) {
    const rowNode = table.child(row);
    pos++;
    for (let i = 0; ; i++) {
      while (mapPos < map.length && map[mapPos] != 0) {
        mapPos++;
      }
      if (i == rowNode.childCount) {
        break;
      }
      const cellNode = rowNode.child(i);
      const { colspan, rowspan, colwidth } = cellNode.attrs;
      for (let h = 0; h < rowspan; h++) {
        if (h + row >= height) {
          (problems || (problems = [])).push({
            type: "overlong_rowspan",
            pos,
            n: rowspan - h
          });
          break;
        }
        const start = mapPos + h * width;
        for (let w = 0; w < colspan; w++) {
          if (map[start + w] == 0) {
            map[start + w] = pos;
          } else {
            (problems || (problems = [])).push({
              type: "collision",
              row,
              pos,
              n: colspan - w
            });
          }
          const colW = colwidth && colwidth[w];
          if (colW) {
            const widthIndex = (start + w) % width * 2, prev = colWidths[widthIndex];
            if (prev === null || prev != colW && colWidths[widthIndex + 1] == 1) {
              colWidths[widthIndex] = colW;
              colWidths[widthIndex + 1] = 1;
            } else if (prev == colW) {
              colWidths[widthIndex + 1]++;
            }
          }
        }
      }
      mapPos += colspan;
      pos += cellNode.nodeSize;
    }
    const expectedPos = (row + 1) * width;
    let missing = 0;
    while (mapPos < expectedPos) {
      if (map[mapPos++] == 0) {
        missing++;
      }
    }
    if (missing) {
      (problems || (problems = [])).push({ type: "missing", row, n: missing });
    }
    pos++;
  }
  if (width === 0 || height === 0) {
    (problems || (problems = [])).push({ type: "zero_sized" });
  }
  const tableMap = new TableMap(width, height, map, problems);
  let badWidths = false;
  for (let i = 0; !badWidths && i < colWidths.length; i += 2) {
    if (!(colWidths[i] === null) && colWidths[i + 1] < height) {
      badWidths = true;
    }
  }
  if (badWidths) {
    findBadColWidths(tableMap, colWidths, table);
  }
  return tableMap;
}
function findWidth(table) {
  let width = -1;
  let hasRowSpan = false;
  for (let row = 0; row < table.childCount; row++) {
    const rowNode = table.child(row);
    let rowWidth = 0;
    if (hasRowSpan) {
      for (let j = 0; j < row; j++) {
        const prevRow = table.child(j);
        for (let i = 0; i < prevRow.childCount; i++) {
          const cell = prevRow.child(i);
          if (j + cell.attrs.rowspan > row) {
            rowWidth += cell.attrs.colspan;
          }
        }
      }
    }
    for (let i = 0; i < rowNode.childCount; i++) {
      const cell = rowNode.child(i);
      rowWidth += cell.attrs.colspan;
      if (cell.attrs.rowspan > 1) {
        hasRowSpan = true;
      }
    }
    if (width == -1) {
      width = rowWidth;
    } else if (width != rowWidth) {
      width = Math.max(width, rowWidth);
    }
  }
  return width;
}
function findBadColWidths(map, colWidths, table) {
  if (!map.problems) {
    map.problems = [];
  }
  const seen = {};
  for (let i = 0; i < map.map.length; i++) {
    const pos = map.map[i];
    if (seen[pos]) {
      continue;
    }
    seen[pos] = true;
    const node = table.nodeAt(pos);
    if (!node) {
      throw new RangeError(`No cell with offset ${pos} found`);
    }
    let updated = null;
    const attrs = node.attrs;
    for (let j = 0; j < attrs.colspan; j++) {
      const col = (i + j) % map.width;
      const colWidth = colWidths[col * 2];
      if (!(colWidth === null) && (!attrs.colwidth || attrs.colwidth[j] != colWidth)) {
        (updated || (updated = freshColWidth(attrs)))[j] = colWidth;
      }
    }
    if (updated) {
      map.problems.unshift({
        type: "colwidth mismatch",
        pos,
        colwidth: updated
      });
    }
  }
}
function freshColWidth(attrs) {
  if (attrs.colwidth) {
    return attrs.colwidth.slice();
  }
  const result = [];
  for (let i = 0; i < attrs.colspan; i++) {
    result.push(0);
  }
  return result;
}
//# sourceMappingURL=tablemap.js.map
