'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var PathProxy = require('../../../../../../zrender@6.0.0/node_modules/zrender/lib/core/PathProxy.js');
var layout = require('../../util/layout.js');

var RADIAN = Math.PI / 180;
function chordCircularLayout(ecModel, api) {
  ecModel.eachSeriesByType('chord', function (seriesModel) {
    chordLayout(seriesModel, api);
  });
}
function chordLayout(seriesModel, api) {
  var nodeData = seriesModel.getData();
  var nodeGraph = nodeData.graph;
  var edgeData = seriesModel.getEdgeData();
  var edgeCount = edgeData.count();
  if (!edgeCount) {
    return;
  }
  var _a = layout.getCircleLayout(seriesModel, api),
    cx = _a.cx,
    cy = _a.cy,
    r = _a.r,
    r0 = _a.r0;
  var padAngle = Math.max((seriesModel.get('padAngle') || 0) * RADIAN, 0);
  var minAngle = Math.max((seriesModel.get('minAngle') || 0) * RADIAN, 0);
  var startAngle = -seriesModel.get('startAngle') * RADIAN;
  var endAngle = startAngle + Math.PI * 2;
  var clockwise = seriesModel.get('clockwise');
  var dir = clockwise ? 1 : -1;
  // Normalize angles
  var angles = [startAngle, endAngle];
  PathProxy.normalizeArcAngles(angles, !clockwise);
  var normalizedStartAngle = angles[0],
    normalizedEndAngle = angles[1];
  var totalAngle = normalizedEndAngle - normalizedStartAngle;
  var allZero = nodeData.getSum('value') === 0 && edgeData.getSum('value') === 0;
  // Sum of each node's edge values
  var nodeValues = [];
  var renderedNodeCount = 0;
  nodeGraph.eachEdge(function (edge) {
    // All links use the same value 1 when allZero is true
    var value = allZero ? 1 : edge.getValue('value');
    if (allZero && (value > 0 || minAngle)) {
      // When allZero is true, angle is in direct proportion to number
      // of links both in and out of the node.
      renderedNodeCount += 2;
    }
    var node1Index = edge.node1.dataIndex;
    var node2Index = edge.node2.dataIndex;
    nodeValues[node1Index] = (nodeValues[node1Index] || 0) + value;
    nodeValues[node2Index] = (nodeValues[node2Index] || 0) + value;
  });
  // Update nodeValues with data.value if exists
  var nodeValueSum = 0;
  nodeGraph.eachNode(function (node) {
    var dataValue = node.getValue('value');
    if (!isNaN(dataValue)) {
      nodeValues[node.dataIndex] = Math.max(dataValue, nodeValues[node.dataIndex] || 0);
    }
    if (!allZero && (nodeValues[node.dataIndex] > 0 || minAngle)) {
      // When allZero is false, angle is in direct proportion to node's
      // value
      renderedNodeCount++;
    }
    nodeValueSum += nodeValues[node.dataIndex] || 0;
  });
  if (renderedNodeCount === 0 || nodeValueSum === 0) {
    return;
  }
  if (padAngle * renderedNodeCount >= Math.abs(totalAngle)) {
    // Not enough angle to render the pad, minAngle has higher priority, and padAngle takes the rest
    padAngle = Math.max(0, (Math.abs(totalAngle) - minAngle * renderedNodeCount) / renderedNodeCount);
  }
  if ((padAngle + minAngle) * renderedNodeCount >= Math.abs(totalAngle)) {
    // Not enough angle to render the minAngle, so ignore the minAngle
    minAngle = (Math.abs(totalAngle) - padAngle * renderedNodeCount) / renderedNodeCount;
  }
  var unitAngle = (totalAngle - padAngle * renderedNodeCount * dir) / nodeValueSum;
  var totalDeficit = 0; // sum of deficits of nodes with span < minAngle
  var totalSurplus = 0; // sum of (spans - minAngle) of nodes with span > minAngle
  var totalSurplusSpan = 0; // sum of spans of nodes with span > minAngle
  var minSurplus = Infinity; // min of (spans - minAngle) of nodes with span > minAngle
  nodeGraph.eachNode(function (node) {
    var value = nodeValues[node.dataIndex] || 0;
    var spanAngle = unitAngle * (nodeValueSum ? value : 1) * dir;
    if (Math.abs(spanAngle) < minAngle) {
      totalDeficit += minAngle - Math.abs(spanAngle);
    } else {
      minSurplus = Math.min(minSurplus, Math.abs(spanAngle) - minAngle);
      totalSurplus += Math.abs(spanAngle) - minAngle;
      totalSurplusSpan += Math.abs(spanAngle);
    }
    node.setLayout({
      angle: spanAngle,
      value: value
    });
  });
  var surplusAsMuchAsPossible = false;
  if (totalDeficit > totalSurplus) {
    // Not enough angle to spread the nodes, scale all
    var scale_1 = totalDeficit / totalSurplus;
    nodeGraph.eachNode(function (node) {
      var spanAngle = node.getLayout().angle;
      if (Math.abs(spanAngle) >= minAngle) {
        node.setLayout({
          angle: spanAngle * scale_1,
          ratio: scale_1
        }, true);
      } else {
        node.setLayout({
          angle: minAngle,
          ratio: minAngle === 0 ? 1 : spanAngle / minAngle
        }, true);
      }
    });
  } else {
    // For example, if totalDeficit is 60 degrees and totalSurplus is 70
    // degrees but one of the sector can only reduced by 1 degree,
    // if we decrease it with the ratio of value to other surplused nodes,
    // it will have smaller angle than minAngle itself.
    // So we need to borrow some angle from other nodes.
    nodeGraph.eachNode(function (node) {
      if (surplusAsMuchAsPossible) {
        return;
      }
      var spanAngle = node.getLayout().angle;
      var borrowRatio = Math.min(spanAngle / totalSurplusSpan, 1);
      var borrowAngle = borrowRatio * totalDeficit;
      if (spanAngle - borrowAngle < minAngle) {
        // It will have less than minAngle after borrowing
        surplusAsMuchAsPossible = true;
      }
    });
  }
  var restDeficit = totalDeficit;
  nodeGraph.eachNode(function (node) {
    if (restDeficit <= 0) {
      return;
    }
    var spanAngle = node.getLayout().angle;
    if (spanAngle > minAngle && minAngle > 0) {
      var borrowRatio = surplusAsMuchAsPossible ? 1 : Math.min(spanAngle / totalSurplusSpan, 1);
      var maxBorrowAngle = spanAngle - minAngle;
      var borrowAngle = Math.min(maxBorrowAngle, Math.min(restDeficit, totalDeficit * borrowRatio));
      restDeficit -= borrowAngle;
      node.setLayout({
        angle: spanAngle - borrowAngle,
        ratio: (spanAngle - borrowAngle) / spanAngle
      }, true);
    } else if (minAngle > 0) {
      node.setLayout({
        angle: minAngle,
        ratio: spanAngle === 0 ? 1 : minAngle / spanAngle
      }, true);
    }
  });
  var angle = normalizedStartAngle;
  var edgeAccAngle = [];
  nodeGraph.eachNode(function (node) {
    var spanAngle = Math.max(node.getLayout().angle, minAngle);
    node.setLayout({
      cx: cx,
      cy: cy,
      r0: r0,
      r: r,
      startAngle: angle,
      endAngle: angle + spanAngle * dir,
      clockwise: clockwise
    }, true);
    edgeAccAngle[node.dataIndex] = angle;
    angle += (spanAngle + padAngle) * dir;
  });
  nodeGraph.eachEdge(function (edge) {
    var value = allZero ? 1 : edge.getValue('value');
    var spanAngle = unitAngle * (nodeValueSum ? value : 1) * dir;
    var node1Index = edge.node1.dataIndex;
    var sStartAngle = edgeAccAngle[node1Index] || 0;
    var sSpan = Math.abs((edge.node1.getLayout().ratio || 1) * spanAngle);
    var sEndAngle = sStartAngle + sSpan * dir;
    var s1 = [cx + r0 * Math.cos(sStartAngle), cy + r0 * Math.sin(sStartAngle)];
    var s2 = [cx + r0 * Math.cos(sEndAngle), cy + r0 * Math.sin(sEndAngle)];
    var node2Index = edge.node2.dataIndex;
    var tStartAngle = edgeAccAngle[node2Index] || 0;
    var tSpan = Math.abs((edge.node2.getLayout().ratio || 1) * spanAngle);
    var tEndAngle = tStartAngle + tSpan * dir;
    var t1 = [cx + r0 * Math.cos(tStartAngle), cy + r0 * Math.sin(tStartAngle)];
    var t2 = [cx + r0 * Math.cos(tEndAngle), cy + r0 * Math.sin(tEndAngle)];
    edge.setLayout({
      s1: s1,
      s2: s2,
      sStartAngle: sStartAngle,
      sEndAngle: sEndAngle,
      t1: t1,
      t2: t2,
      tStartAngle: tStartAngle,
      tEndAngle: tEndAngle,
      cx: cx,
      cy: cy,
      r: r0,
      value: value,
      clockwise: clockwise
    });
    edgeAccAngle[node1Index] = sEndAngle;
    edgeAccAngle[node2Index] = tEndAngle;
  });
}

exports.default = chordCircularLayout;
