/**
 * 平面图内容区尺寸：由 items + padding 推导 contentWidth/Height 与 offset。
 * 供 useFloorMapTransformMetrics（单画布视口）与 FloorMapOverview（多画布平铺）共用，
 * 避免两处各自维护包围盒算法。
 */

import { getContentSize, getPixelBoundingBox } from "./layoutMeasurements";

/** 内容区宽高与图元层偏移（与 Transform content 坐标系一致） */

/**
 * 根据 items 计算内容区尺寸与偏移。
 * 算法与 {@link useFloorMapTransformMetrics} 内原 useMemo 一致（不含「撑满视口」扩展）。
 */
export function computeFloorMapContentStage(params) {
  var items = params.items,
    cellSize = params.cellSize,
    itemGap = params.itemGap,
    itemUnit = params.itemUnit,
    boundingPadding = params.boundingPadding,
    _params$preferPixelBo = params.preferPixelBoundingBox,
    preferPixelBoundingBox = _params$preferPixelBo === void 0 ? true : _params$preferPixelBo;
  var padSingle = boundingPadding;
  var pad = padSingle * 2;
  var useBoundingBox = preferPixelBoundingBox && itemUnit === 'pixel' && items.length > 0;
  var box = useBoundingBox ? getPixelBoundingBox(items, cellSize) : null;
  if (box) {
    return {
      contentWidth: box.maxRight - box.minLeft + pad,
      contentHeight: box.maxBottom - box.minTop + pad,
      contentOffsetX: padSingle - box.minLeft,
      contentOffsetY: padSingle - box.minTop,
      alignPadSingle: padSingle
    };
  }
  var fromItems = getContentSize(items, cellSize, itemGap, itemUnit);
  return {
    contentWidth: fromItems.contentWidth + pad,
    contentHeight: fromItems.contentHeight + pad,
    contentOffsetX: 0,
    contentOffsetY: 0,
    alignPadSingle: null
  };
}

/**
 * 全览网格行列：n≤3 单行；n≥4 用 ceil(√n) 列、ceil(n/cols) 行，行列接近且末行空格最少。
 * 例：2→2×1，3→3×1，4→2×2，5/6→3×2，7-9→3×3，10-12→4×3。
 *
 * 仅供旧版「等大缩略图卡片」全览使用；新版 All 视图按原始尺寸装箱，请用
 * {@link packCanvasesIntoShelves}。
 */
export function getOverviewGridDims(count) {
  if (count <= 0) return {
    cols: 1,
    rows: 1
  };
  if (count <= 3) return {
    cols: count,
    rows: 1
  };
  var cols = Math.ceil(Math.sqrt(count));
  var rows = Math.ceil(count / cols);
  return {
    cols: cols,
    rows: rows
  };
}

/** 装箱输入：每个画布在统一比例尺下的原始尺寸（像素） */

/** 装箱产物：每个画布的左上角坐标 + 总布局尺寸（不含外侧 padding） */

/**
 * 多画布 All 视图的紧凑装箱（Bottom-Left Fill, BLF）：
 *
 * - 保留输入顺序（与 Tab 顺序一致），不旋转、不缩放，原始尺寸即渲染尺寸；
 * - 对每张画布枚举候选落点 = `{(0,0)} ∪ 已放画布的{右侧, 下方}角`；过滤掉与已放冲突
 *   的位置，再挑 **y 最小、再 x 最小** 的位置落下——优先填低洼，自然会把矮 / 窄画布
 *   塞进高画布旁的空白；不会出现「等最高画布把整行撑满才换行」导致的大量空白；
 * - 目标外框宽度 `targetWidth = max(最宽画布, √(总面积 × {@link aspectBias}))`，
 *   令整体形状趋于 `aspectBias:1`（默认 2.5 → 宽:高 ≈ 2.5:1，轻微偏横）；
 *   单张画布若本身比 targetWidth 还宽，仍允许独自首行铺开，避免被截。
 *
 * 复杂度 O(n²)（每个画布对所有已放做候选 + 冲突校验），n 通常 < 20，性能足够。
 * 比旧的行装填（shelf packing）能显著减少长高画布旁的留白。
 *
 * @param boxes - 待装箱画布尺寸（保持顺序）
 * @param gap - 画布之间的像素间距（同时作为冲突检测的最小净距）
 * @param aspectBias - 目标宽高比偏置（width:height）；越大越横向，默认 2.5
 *   - `1.5` ~ `2`：接近正方形，仅略偏横
 *   - `2.5` ~ `3`：明显但温和的横向布局（推荐）
 *   - `>= 4`：强横向，画布数较多时易接近单行
 */
export function packCanvasesIntoShelves(boxes, gap) {
  var aspectBias = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 2.5;
  if (boxes.length === 0) {
    return {
      positions: [],
      totalWidth: 0,
      totalHeight: 0
    };
  }
  var safeGap = Math.max(0, gap);
  var safeBias = Number.isFinite(aspectBias) && aspectBias > 0 ? aspectBias : 1;
  var totalArea = boxes.reduce(function (acc, b) {
    return acc + Math.max(0, b.width) * Math.max(0, b.height);
  }, 0);
  var widestBox = boxes.reduce(function (max, b) {
    return Math.max(max, b.width);
  }, 0);
  var targetWidth = Math.max(widestBox, Math.sqrt(totalArea * safeBias));
  var placed = [];
  var positions = new Array(boxes.length);
  var totalWidth = 0;
  var totalHeight = 0;

  /**
   * 两矩形（带 safeGap 净距）是否冲突。
   * 不冲突条件：A 在 B 右侧 / 左侧 / 下方 / 上方 至少留 safeGap。
   * 取反即为冲突条件（x 与 y 方向都「靠得太近」）。
   */
  var conflicts = function conflicts(x, y, w, h, p) {
    var xClash = x + w + safeGap > p.x && p.x + p.w + safeGap > x;
    var yClash = y + h + safeGap > p.y && p.y + p.h + safeGap > y;
    return xClash && yClash;
  };
  boxes.forEach(function (box, i) {
    /**
     * 候选位置：原点 + 每张已放画布的「右侧角、下方角」。
     * 简单角点集对 n<20 的常见场景已能贴合 MaxRects 的视觉效果。
     */
    var candidates = [{
      x: 0,
      y: 0
    }];
    for (var _i = 0, _placed = placed; _i < _placed.length; _i++) {
      var p = _placed[_i];
      candidates.push({
        x: p.x + p.w + safeGap,
        y: p.y
      });
      candidates.push({
        x: p.x,
        y: p.y + p.h + safeGap
      });
    }
    var bestX = Number.POSITIVE_INFINITY;
    var bestY = Number.POSITIVE_INFINITY;
    for (var _i2 = 0, _candidates = candidates; _i2 < _candidates.length; _i2++) {
      var cand = _candidates[_i2];
      var x = cand.x,
        y = cand.y;
      /**
       * 超出目标宽度的候选直接淘汰；除非：
       * - 这是第一张画布（placed 为空），或
       * - 这张画布本身就比 targetWidth 还宽（允许独占首行）。
       */
      if (x + box.width > targetWidth && placed.length > 0 && box.width <= targetWidth) {
        continue;
      }
      var clash = false;
      for (var _i3 = 0, _placed2 = placed; _i3 < _placed2.length; _i3++) {
        var _p = _placed2[_i3];
        if (conflicts(x, y, box.width, box.height, _p)) {
          clash = true;
          break;
        }
      }
      if (clash) continue;
      if (y < bestY || y === bestY && x < bestX) {
        bestX = x;
        bestY = y;
      }
    }
    if (!Number.isFinite(bestX)) {
      /** 兜底：实在挤不下就堆到所有已放画布的最下方（极端宽画布场景） */
      bestX = 0;
      bestY = placed.reduce(function (m, p) {
        return Math.max(m, p.y + p.h + safeGap);
      }, 0);
    }
    placed.push({
      x: bestX,
      y: bestY,
      w: box.width,
      h: box.height
    });
    positions[i] = {
      x: bestX,
      y: bestY
    };
    if (bestX + box.width > totalWidth) totalWidth = bestX + box.width;
    if (bestY + box.height > totalHeight) totalHeight = bestY + box.height;
  });
  return {
    positions: positions,
    totalWidth: totalWidth,
    totalHeight: totalHeight
  };
}