import { __awaiter, __extends, __generator } from "tslib";
import { AbstractLayout } from '@antv/f6-core';
import { Layout } from '../../layout';
import { mix, clone } from '@antv/util';
var LayoutPipesAdjustNames = ['force', 'grid', 'circular'];

var LayoutController =
/** @class */
function (_super) {
  __extends(LayoutController, _super);

  function LayoutController(graph) {
    var _this = _super.call(this, graph) || this;

    _this.graph = graph;
    _this.layoutCfg = graph.get('layout') || {};
    _this.layoutType = _this.getLayoutType();
    return _this;
  } // 更新布局参数


  LayoutController.prototype.updateLayoutCfg = function (cfg) {
    var _this = this;

    var _a = this,
        graph = _a.graph,
        layoutMethods = _a.layoutMethods;

    var layoutCfg = mix({}, this.layoutCfg, cfg);
    this.layoutCfg = layoutCfg;

    if (!(layoutMethods === null || layoutMethods === void 0 ? void 0 : layoutMethods.length)) {
      this.layout();
      return;
    }

    this.data = this.setDataFromGraph();
    graph.emit('beforelayout');
    var start = Promise.resolve();

    if (layoutMethods.length === 1) {
      start = start.then(function () {
        return _this.updateLayoutMethod(layoutMethods[0], layoutCfg);
      });
    } else {
      layoutMethods === null || layoutMethods === void 0 ? void 0 : layoutMethods.forEach(function (layoutMethod, index) {
        var currentCfg = layoutCfg.pipes[index];
        start = start.then(function () {
          return _this.updateLayoutMethod(layoutMethod, currentCfg);
        });
      });
    }

    this.data = this.setDataFromGraph();
    start.then(function () {
      if (layoutCfg.onAllLayoutEnd) layoutCfg.onAllLayoutEnd();
    }).catch(function (error) {
      console.warn('layout failed', error);
    });
  };
  /**
   * @param {function} success callback
   * @return {boolean} 是否使用web worker布局
   */


  LayoutController.prototype.layout = function (success) {
    var _this = this;

    var graph = this.graph;
    this.data = this.setDataFromGraph();
    var _a = this.data,
        nodes = _a.nodes,
        hiddenNodes = _a.hiddenNodes;

    if (!nodes) {
      return false;
    }

    var width = graph.get('width');
    var height = graph.get('height');
    var layoutCfg = {};
    Object.assign(layoutCfg, {
      width: width,
      height: height,
      center: [width / 2, height / 2]
    }, this.layoutCfg);
    this.layoutCfg = layoutCfg;
    this.destoryLayoutMethods();
    graph.emit('beforelayout');
    this.initPositions(layoutCfg.center, nodes); // init hidden ndoes

    this.initPositions(layoutCfg.center, hiddenNodes); // 在 onAllLayoutEnd 中执行用户自定义 onLayoutEnd，触发 afterlayout、更新节点位置、fitView/fitCenter、触发 afterrender

    var onLayoutEnd = layoutCfg.onLayoutEnd,
        layoutEndFormatted = layoutCfg.layoutEndFormatted,
        adjust = layoutCfg.adjust;

    if (!layoutEndFormatted) {
      layoutCfg.layoutEndFormatted = true;

      layoutCfg.onAllLayoutEnd = function () {
        return __awaiter(_this, void 0, void 0, function () {
          return __generator(this, function (_a) {
            switch (_a.label) {
              case 0:
                // 执行用户自定义 onLayoutEnd
                if (onLayoutEnd) {
                  onLayoutEnd();
                } // 更新节点位置


                this.refreshLayout();
                if (!(adjust && layoutCfg.pipes)) return [3
                /*break*/
                , 2];
                return [4
                /*yield*/
                , this.adjustPipesBox(this.data, adjust)];

              case 1:
                _a.sent();

                this.refreshLayout();
                _a.label = 2;

              case 2:
                // 触发 afterlayout
                graph.emit('afterlayout');
                return [2
                /*return*/
                ];
            }
          });
        });
      };
    }

    var start = Promise.resolve();

    if (layoutCfg.type) {
      start = start.then(function () {
        return _this.execLayoutMethod(layoutCfg, 0);
      });
    } else if (layoutCfg.pipes) {
      layoutCfg.pipes.forEach(function (cfg, index) {
        start = start.then(function () {
          return _this.execLayoutMethod(cfg, index);
        });
      });
    } // 最后统一在外部调用onAllLayoutEnd


    start.then(function () {
      if (layoutCfg.onAllLayoutEnd) layoutCfg.onAllLayoutEnd(); // 在执行 execute 后立即执行 success，且在 timeBar 中有 throttle，可以防止 timeBar 监听 afterrender 进行 changeData 后 layout，从而死循环
      // 对于 force 一类布局完成后的 fitView 需要用户自己在 onLayoutEnd 中配置

      if (success) success();
    }).catch(function (error) {
      console.warn('graph layout failed,', error);
    });
    return false;
  };

  LayoutController.prototype.execLayoutMethod = function (layoutCfg, order) {
    var _this = this;

    return new Promise(function (reslove, reject) {
      var graph = _this.graph;
      var layoutType = layoutCfg.type; // 每个布局方法都需要注册

      layoutCfg.onLayoutEnd = function () {
        graph.emit('aftersublayout', {
          type: layoutType
        });
        reslove();
      };

      var isForce = layoutType === 'force' || layoutType === 'g6force' || layoutType === 'gForce';

      if (isForce) {
        var onTick_1 = layoutCfg.onTick;

        var tick = function tick() {
          if (onTick_1) {
            onTick_1();
          }

          graph.refreshPositions();
        };

        layoutCfg.tick = tick;
      } else if (layoutCfg.type === 'comboForce') {
        layoutCfg.comboTrees = graph.get('comboTrees');
      }

      var enableTick = false;
      var layoutMethod;

      try {
        layoutMethod = new Layout(layoutCfg);
      } catch (e) {
        console.warn("The layout method: '" + layoutType + "' does not exist! Please specify it first.");
        reject();
      } // 是否需要迭代的方式完成布局。这里是来自布局对象的实例属性，是由布局的定义者在布局类定义的。


      enableTick = layoutMethod.enableTick;

      if (enableTick) {
        var onTick_2 = layoutCfg.onTick;

        var tick = function tick() {
          if (onTick_2) {
            onTick_2();
          }

          graph.refreshPositions();
        };

        layoutMethod.tick = tick;
      }

      var layoutData = _this.filterLayoutData(_this.data, layoutCfg);

      addLayoutOrder(layoutData, order);
      layoutMethod.init(layoutData); // 若存在节点没有位置信息，且没有设置 layout，在 initPositions 中 random 给出了所有节点的位置，不需要再次执行 random 布局
      // 所有节点都有位置信息，且指定了 layout，则执行布局（代表不是第一次进行布局）

      graph.emit('beforesublayout', {
        type: layoutType
      });
      layoutMethod.execute();
      if (layoutMethod.isCustomLayout && layoutCfg.onLayoutEnd) layoutCfg.onLayoutEnd();

      _this.layoutMethods.push(layoutMethod);
    });
  };

  LayoutController.prototype.updateLayoutMethod = function (layoutMethod, layoutCfg) {
    var _this = this;

    return new Promise(function (reslove, reject) {
      var graph = _this.graph;
      var layoutType = layoutCfg === null || layoutCfg === void 0 ? void 0 : layoutCfg.type; // 每个布局方法都需要注册

      layoutCfg.onLayoutEnd = function () {
        graph.emit('aftersublayout', {
          type: layoutType
        });
        reslove();
      };

      var layoutData = _this.filterLayoutData(_this.data, layoutCfg);

      layoutMethod.init(layoutData);
      layoutMethod.updateCfg(layoutCfg);
      graph.emit('beforesublayout', {
        type: layoutType
      });
      layoutMethod.execute();
      if (layoutMethod.isCustomLayout && layoutCfg.onLayoutEnd) layoutCfg.onLayoutEnd();
    });
  };

  LayoutController.prototype.adjustPipesBox = function (data, adjust) {
    var _this = this;

    return new Promise(function (resolve) {
      var nodes = data.nodes;

      if (!(nodes === null || nodes === void 0 ? void 0 : nodes.length)) {
        resolve();
      }

      if (!LayoutPipesAdjustNames.includes(adjust)) {
        console.warn("The adjust type " + adjust + " is not supported yet, please assign it with 'force', 'grid', or 'circular'.");
        resolve();
      }

      var layoutCfg = {
        center: _this.layoutCfg.center,
        nodeSize: function nodeSize(d) {
          return Math.max(d.height, d.width);
        },
        preventOverlap: true,
        onLayoutEnd: function onLayoutEnd() {}
      }; // 计算出大单元

      var _a = _this.getLayoutBBox(nodes),
          groupNodes = _a.groupNodes,
          layoutNodes = _a.layoutNodes;

      var preNodes = clone(layoutNodes); // 根据大单元坐标的变化，调整这里面每个小单元nodes

      layoutCfg.onLayoutEnd = function () {
        layoutNodes === null || layoutNodes === void 0 ? void 0 : layoutNodes.forEach(function (ele, index) {
          var _a, _b, _c;

          var dx = ele.x - ((_a = preNodes[index]) === null || _a === void 0 ? void 0 : _a.x);
          var dy = ele.y - ((_b = preNodes[index]) === null || _b === void 0 ? void 0 : _b.y);
          (_c = groupNodes[index]) === null || _c === void 0 ? void 0 : _c.forEach(function (n) {
            n.x += dx;
            n.y += dy;
          });
        });
        resolve();
      };

      var layoutMethod = new Layout(layoutCfg);
      layoutMethod.layout({
        nodes: layoutNodes
      });
    });
  };

  LayoutController.prototype.destroy = function () {
    this.destoryLayoutMethods();
    this.destroyed = true;
    this.graph.set('layout', undefined);
    this.layoutCfg = undefined;
    this.layoutType = undefined;
    this.layoutMethods = undefined;
    this.graph = null;
  };

  return LayoutController;
}(AbstractLayout);

export default LayoutController;

function addLayoutOrder(data, order) {
  var _a;

  if (!((_a = data === null || data === void 0 ? void 0 : data.nodes) === null || _a === void 0 ? void 0 : _a.length)) {
    return;
  }

  var nodes = data.nodes;
  nodes.forEach(function (node) {
    node.layoutOrder = order;
  });
}