/* eslint-disable */
define(function (require, exports, module) {
  var kity = require('../core/kity');
  var utils = require('../core/utils');
  var keymap = require('../core/keymap');
  var MinderNode = require('../core/node');
  var Command = require('../core/command');
  var Module = require('../core/module');
  var Renderer = require('../core/render');

  Module.register('Expand', function () {
    var minder = this;
    var EXPAND_STATE_DATA = 'expandState',
      STATE_EXPAND = 'expand',
      STATE_COLLAPSE = 'collapse';

    // 将展开的操作和状态读取接口拓展到 MinderNode 上
    kity.extendClass(MinderNode, {
      /**
       * 展开节点
       * @param  {Policy} policy 展开的策略，默认为 KEEP_STATE
       */
      expand: function () {
        this.setData(EXPAND_STATE_DATA, STATE_EXPAND);
        return this;
      },

      /**
       * 收起节点
       */
      collapse: function () {
        this.setData(EXPAND_STATE_DATA, STATE_COLLAPSE);
        return this;
      },

      /**
       * 判断节点当前的状态是否为展开
       */
      isExpanded: function () {
        var expanded = this.getData(EXPAND_STATE_DATA) !== STATE_COLLAPSE;
        return expanded && (this.isRoot() || this.parent.isExpanded());
      },

      /**
       * 判断节点当前的状态是否为收起
       */
      isCollapsed: function () {
        return !this.isExpanded();
      },
    });

    /**
     * @command Expand
     * @description 展开当前选中的节点，保证其可见
     * @param {bool} justParents 是否只展开到父亲
     *     * `false` - （默认）保证选中的节点以及其子树可见
     *     * `true` - 只保证选中的节点可见，不展开其子树
     * @state
     *   0: 当前有选中的节点
     *  -1: 当前没有选中的节点
     */
    var ExpandCommand = kity.createClass('ExpandCommand', {
      base: Command,

      execute: function (km, justParents) {
        var node = km.getSelectedNode();
        if (!node) return;
        if (justParents) {
          node = node.parent;
        }
        while (node.parent) {
          node.expand();
          node = node.parent;
        }
        node.renderTree();
        km.layout(100);
      },

      queryState: function (km) {
        var node = km.getSelectedNode();
        return node && !node.isRoot() && !node.isExpanded() ? 0 : -1;
      },
    });

    /**
     * @command ExpandToLevel
     * @description 展开脑图到指定的层级
     * @param {number} level 指定展开到的层级，最少值为 1。
     * @state
     *   0: 一直可用
     */
    var ExpandToLevelCommand = kity.createClass('ExpandToLevelCommand', {
      base: Command,
      execute: function (km, level) {
        km.getRoot().traverse(function (node) {
          if (node.getLevel() < level) node.expand();
          if (node.getLevel() == level && !node.isLeaf()) node.collapse();
        });
        km.refresh(100);
      },
      enableReadOnly: true,
    });

    /**
     * @command Collapse
     * @description 收起当前节点的子树
     * @state
     *   0: 当前有选中的节点
     *  -1: 当前没有选中的节点
     */
    var CollapseCommand = kity.createClass('CollapseCommand', {
      base: Command,

      execute: function (km) {
        var node = km.getSelectedNode();
        if (!node) return;

        node.collapse();
        node.renderTree();
        km.layout();
      },

      queryState: function (km) {
        var node = km.getSelectedNode();
        return node && !node.isRoot() && node.isExpanded() ? 0 : -1;
      },
    });

    var Expander = kity.createClass('Expander', {
      base: kity.Group,

      constructor: function (node) {
        this.callBase();
        this.radius = 6;
        this.outline = new kity.Circle(this.radius).stroke('gray').fill('white');
        this.sign = new kity.Path().stroke('gray');
        this.addShapes([this.outline, this.sign]);
        this.initEvent(node);
        this.setId(utils.uuid('node_expander'));
        this.setStyle('cursor', 'pointer');
      },

      initEvent: function (node) {
        this.on('mousedown', function (e) {
          if (minder._status === 'readonly') return;

          minder.select([node], true);
          if (node.isExpanded()) {
            node.collapse();
          } else {
            node.expand();
          }
          node.renderTree().getMinder().layout(100);
          node.getMinder().fire('contentchange');
          e.stopPropagation();
          e.preventDefault();
        });
        this.on('dblclick click mouseup', function (e) {
          e.stopPropagation();
          e.preventDefault();
        });
      },

      setState: function (state) {
        if (state == 'hide') {
          this.setVisible(false);
          return;
        }
        this.setVisible(true);
        var pathData = ['M', 1.5 - this.radius, 0, 'L', this.radius - 1.5, 0];
        if (state == STATE_COLLAPSE) {
          pathData.push(['M', 0, 1.5 - this.radius, 'L', 0, this.radius - 1.5]);
        }
        this.sign.setPathData(pathData);
      },
    });

    var ExpanderRenderer = kity.createClass('ExpanderRenderer', {
      base: Renderer,

      create: function (node) {
        if (node.isRoot()) return;
        this.expander = new Expander(node);
        node.getRenderContainer().prependShape(this.expander);
        node.expanderRenderer = this;
        this.node = node;
        return this.expander;
      },

      shouldRender: function (node) {
        return !node.isRoot();
      },

      update: function (expander, node, box) {
        if (!node.parent) return;

        var visible = node.parent.isExpanded();

        expander.setState(visible && node.children.length ? node.getData(EXPAND_STATE_DATA) : 'hide');

        var vector = node.getLayoutVectorIn().normalize(expander.radius + node.getStyle('stroke-width'));
        var position = node.getVertexIn().offset(vector.reverse());

        this.expander.setTranslate(position);
      },
    });

    return {
      commands: {
        expand: ExpandCommand,
        expandtolevel: ExpandToLevelCommand,
        collapse: CollapseCommand,
      },
      events: {
        'layoutapply': function (e) {
          var r = e.node.getRenderer('ExpanderRenderer');
          if (r.getRenderShape()) {
            r.update(r.getRenderShape(), e.node);
          }
        },
        'beforerender': function (e) {
          var node = e.node;
          var visible = !node.parent || node.parent.isExpanded();
          var minder = this;

          node.getRenderContainer().setVisible(visible);
          if (!visible) e.stopPropagation();
        },
        'normal.keydown': function (e) {
          if (this.getStatus() == 'textedit') return;
          if (e.originEvent.keyCode == keymap['/']) {
            var node = this.getSelectedNode();
            if (!node || node == this.getRoot()) return;
            var expanded = node.isExpanded();
            this.getSelectedNodes().forEach(function (node) {
              if (expanded) node.collapse();
              else node.expand();
              node.renderTree();
            });
            this.layout(100);
            this.fire('contentchange');
            e.preventDefault();
            e.stopPropagationImmediately();
          }
          if (e.isShortcutKey('Alt+`')) {
            this.execCommand('expandtolevel', 9999);
          }
          for (var i = 1; i < 6; i++) {
            if (e.isShortcutKey('Alt+' + i)) {
              this.execCommand('expandtolevel', i);
            }
          }
        },
      },
      renderers: {
        outside: ExpanderRenderer,
      },
      contextmenu: [
        {
          command: 'expandtoleaf',
          query: function () {
            return !minder.getSelectedNode();
          },
          fn: function (minder) {
            minder.execCommand('expandtolevel', 9999);
          },
        },
        {
          command: 'expandtolevel1',
          query: function () {
            return !minder.getSelectedNode();
          },
          fn: function (minder) {
            minder.execCommand('expandtolevel', 1);
          },
        },
        {
          command: 'expandtolevel2',
          query: function () {
            return !minder.getSelectedNode();
          },
          fn: function (minder) {
            minder.execCommand('expandtolevel', 2);
          },
        },
        {
          command: 'expandtolevel3',
          query: function () {
            return !minder.getSelectedNode();
          },
          fn: function (minder) {
            minder.execCommand('expandtolevel', 3);
          },
        },
        {
          divider: true,
        },
      ],
    };
  });
});
