import { fromPoints } from "../core/bbox.mjs";
import BoundingRect from "../core/BoundingRect.mjs";
import Point from "../core/Point.mjs";
import { map } from "../core/util.mjs";
import Polygon from "../graphic/shape/Polygon.mjs";
import Rect from "../graphic/shape/Rect.mjs";
import Sector from "../graphic/shape/Sector.mjs";
import { pathToPolygons } from "./convertPath.mjs";
import { clonePath } from "./path.mjs";
function getDividingGrids(dimSize, rowDim, count) {
  var rowSize = dimSize[rowDim];
  var columnSize = dimSize[1 - rowDim];
  var ratio = Math.abs(rowSize / columnSize);
  var rowCount = Math.ceil(Math.sqrt(ratio * count));
  var columnCount = Math.floor(count / rowCount);
  if (columnCount === 0) {
    columnCount = 1;
    rowCount = count;
  }
  var grids = [];
  for (var i = 0; i < rowCount; i++) {
    grids.push(columnCount);
  }
  var currentCount = rowCount * columnCount;
  var remained = count - currentCount;
  if (remained > 0) {
    for (var i = 0; i < remained; i++) {
      grids[i % rowCount] += 1;
    }
  }
  return grids;
}
function divideSector(sectorShape, count, outShapes) {
  var r0 = sectorShape.r0;
  var r = sectorShape.r;
  var startAngle = sectorShape.startAngle;
  var endAngle = sectorShape.endAngle;
  var angle = Math.abs(endAngle - startAngle);
  var arcLen = angle * r;
  var deltaR = r - r0;
  var isAngleRow = arcLen > Math.abs(deltaR);
  var grids = getDividingGrids([arcLen, deltaR], isAngleRow ? 0 : 1, count);
  var rowSize = (isAngleRow ? angle : deltaR) / grids.length;
  for (var row = 0; row < grids.length; row++) {
    var columnSize = (isAngleRow ? deltaR : angle) / grids[row];
    for (var column = 0; column < grids[row]; column++) {
      var newShape = {};
      if (isAngleRow) {
        newShape.startAngle = startAngle + rowSize * row;
        newShape.endAngle = startAngle + rowSize * (row + 1);
        newShape.r0 = r0 + columnSize * column;
        newShape.r = r0 + columnSize * (column + 1);
      } else {
        newShape.startAngle = startAngle + columnSize * column;
        newShape.endAngle = startAngle + columnSize * (column + 1);
        newShape.r0 = r0 + rowSize * row;
        newShape.r = r0 + rowSize * (row + 1);
      }
      newShape.clockwise = sectorShape.clockwise;
      newShape.cx = sectorShape.cx;
      newShape.cy = sectorShape.cy;
      outShapes.push(newShape);
    }
  }
}
function divideRect(rectShape, count, outShapes) {
  var width = rectShape.width;
  var height = rectShape.height;
  var isHorizontalRow = width > height;
  var grids = getDividingGrids([width, height], isHorizontalRow ? 0 : 1, count);
  var rowSizeDim = isHorizontalRow ? "width" : "height";
  var columnSizeDim = isHorizontalRow ? "height" : "width";
  var rowDim = isHorizontalRow ? "x" : "y";
  var columnDim = isHorizontalRow ? "y" : "x";
  var rowSize = rectShape[rowSizeDim] / grids.length;
  for (var row = 0; row < grids.length; row++) {
    var columnSize = rectShape[columnSizeDim] / grids[row];
    for (var column = 0; column < grids[row]; column++) {
      var newShape = {};
      newShape[rowDim] = row * rowSize;
      newShape[columnDim] = column * columnSize;
      newShape[rowSizeDim] = rowSize;
      newShape[columnSizeDim] = columnSize;
      newShape.x += rectShape.x;
      newShape.y += rectShape.y;
      outShapes.push(newShape);
    }
  }
}
function crossProduct2d(x1, y1, x2, y2) {
  return x1 * y2 - x2 * y1;
}
function lineLineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) {
  var mx = a2x - a1x;
  var my = a2y - a1y;
  var nx = b2x - b1x;
  var ny = b2y - b1y;
  var nmCrossProduct = crossProduct2d(nx, ny, mx, my);
  if (Math.abs(nmCrossProduct) < 1e-6) {
    return null;
  }
  var b1a1x = a1x - b1x;
  var b1a1y = a1y - b1y;
  var p = crossProduct2d(b1a1x, b1a1y, nx, ny) / nmCrossProduct;
  if (p < 0 || p > 1) {
    return null;
  }
  return new Point(p * mx + a1x, p * my + a1y);
}
function projPtOnLine(pt, lineA, lineB) {
  var dir = new Point();
  Point.sub(dir, lineB, lineA);
  dir.normalize();
  var dir2 = new Point();
  Point.sub(dir2, pt, lineA);
  var len = dir2.dot(dir);
  return len;
}
function addToPoly(poly, pt) {
  var last = poly[poly.length - 1];
  if (last && last[0] === pt[0] && last[1] === pt[1]) {
    return;
  }
  poly.push(pt);
}
function splitPolygonByLine(points, lineA, lineB) {
  var len = points.length;
  var intersections = [];
  for (var i = 0; i < len; i++) {
    var p0 = points[i];
    var p1 = points[(i + 1) % len];
    var intersectionPt = lineLineIntersect(p0[0], p0[1], p1[0], p1[1], lineA.x, lineA.y, lineB.x, lineB.y);
    if (intersectionPt) {
      intersections.push({
        projPt: projPtOnLine(intersectionPt, lineA, lineB),
        pt: intersectionPt,
        idx: i
      });
    }
  }
  if (intersections.length < 2) {
    return [{ points }, { points }];
  }
  intersections.sort(function(a, b) {
    return a.projPt - b.projPt;
  });
  var splitPt0 = intersections[0];
  var splitPt1 = intersections[intersections.length - 1];
  if (splitPt1.idx < splitPt0.idx) {
    var tmp = splitPt0;
    splitPt0 = splitPt1;
    splitPt1 = tmp;
  }
  var splitPt0Arr = [splitPt0.pt.x, splitPt0.pt.y];
  var splitPt1Arr = [splitPt1.pt.x, splitPt1.pt.y];
  var newPolyA = [splitPt0Arr];
  var newPolyB = [splitPt1Arr];
  for (var i = splitPt0.idx + 1; i <= splitPt1.idx; i++) {
    addToPoly(newPolyA, points[i].slice());
  }
  addToPoly(newPolyA, splitPt1Arr);
  addToPoly(newPolyA, splitPt0Arr);
  for (var i = splitPt1.idx + 1; i <= splitPt0.idx + len; i++) {
    addToPoly(newPolyB, points[i % len].slice());
  }
  addToPoly(newPolyB, splitPt0Arr);
  addToPoly(newPolyB, splitPt1Arr);
  return [{
    points: newPolyA
  }, {
    points: newPolyB
  }];
}
function binaryDividePolygon(polygonShape) {
  var points = polygonShape.points;
  var min = [];
  var max = [];
  fromPoints(points, min, max);
  var boundingRect = new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);
  var width = boundingRect.width;
  var height = boundingRect.height;
  var x = boundingRect.x;
  var y = boundingRect.y;
  var pt0 = new Point();
  var pt1 = new Point();
  if (width > height) {
    pt0.x = pt1.x = x + width / 2;
    pt0.y = y;
    pt1.y = y + height;
  } else {
    pt0.y = pt1.y = y + height / 2;
    pt0.x = x;
    pt1.x = x + width;
  }
  return splitPolygonByLine(points, pt0, pt1);
}
function binaryDivideRecursive(divider, shape, count, out) {
  if (count === 1) {
    out.push(shape);
  } else {
    var mid = Math.floor(count / 2);
    var sub = divider(shape);
    binaryDivideRecursive(divider, sub[0], mid, out);
    binaryDivideRecursive(divider, sub[1], count - mid, out);
  }
  return out;
}
function clone(path, count) {
  var paths = [];
  for (var i = 0; i < count; i++) {
    paths.push(clonePath(path));
  }
  return paths;
}
function copyPathProps(source, target) {
  target.setStyle(source.style);
  target.z = source.z;
  target.z2 = source.z2;
  target.zlevel = source.zlevel;
}
function polygonConvert(points) {
  var out = [];
  for (var i = 0; i < points.length; ) {
    out.push([points[i++], points[i++]]);
  }
  return out;
}
function split(path, count) {
  var outShapes = [];
  var shape = path.shape;
  var OutShapeCtor;
  switch (path.type) {
    case "rect":
      divideRect(shape, count, outShapes);
      OutShapeCtor = Rect;
      break;
    case "sector":
      divideSector(shape, count, outShapes);
      OutShapeCtor = Sector;
      break;
    case "circle":
      divideSector({
        r0: 0,
        r: shape.r,
        startAngle: 0,
        endAngle: Math.PI * 2,
        cx: shape.cx,
        cy: shape.cy
      }, count, outShapes);
      OutShapeCtor = Sector;
      break;
    default:
      var m = path.getComputedTransform();
      var scale = m ? Math.sqrt(Math.max(m[0] * m[0] + m[1] * m[1], m[2] * m[2] + m[3] * m[3])) : 1;
      var polygons = map(pathToPolygons(path.getUpdatedPathProxy(), scale), function(poly) {
        return polygonConvert(poly);
      });
      var polygonCount = polygons.length;
      if (polygonCount === 0) {
        binaryDivideRecursive(binaryDividePolygon, {
          points: polygons[0]
        }, count, outShapes);
      } else if (polygonCount === count) {
        for (var i = 0; i < polygonCount; i++) {
          outShapes.push({
            points: polygons[i]
          });
        }
      } else {
        var totalArea_1 = 0;
        var items = map(polygons, function(poly) {
          var min = [];
          var max = [];
          fromPoints(poly, min, max);
          var area = (max[1] - min[1]) * (max[0] - min[0]);
          totalArea_1 += area;
          return { poly, area };
        });
        items.sort(function(a, b) {
          return b.area - a.area;
        });
        var left = count;
        for (var i = 0; i < polygonCount; i++) {
          var item = items[i];
          if (left <= 0) {
            break;
          }
          var selfCount = i === polygonCount - 1 ? left : Math.ceil(item.area / totalArea_1 * count);
          if (selfCount < 0) {
            continue;
          }
          binaryDivideRecursive(binaryDividePolygon, {
            points: item.poly
          }, selfCount, outShapes);
          left -= selfCount;
        }
      }
      OutShapeCtor = Polygon;
      break;
  }
  if (!OutShapeCtor) {
    return clone(path, count);
  }
  var out = [];
  for (var i = 0; i < outShapes.length; i++) {
    var subPath = new OutShapeCtor();
    subPath.setShape(outShapes[i]);
    copyPathProps(path, subPath);
    out.push(subPath);
  }
  return out;
}
export {
  clone,
  split
};
