/**
 * @author: winyuan
 * @data: 2019/07/05
 * @repository: https://github.com/winyuan
 * @description: 图的布局方式/图的初始化
 */

import d3 from '../plugins/d3-installer'
import theme from '../theme'

/**
 * 图的布局方式/图的初始化
 * @type {{commonGraph: (function(*, *): G6.Graph)}}
 */
const initGraph = {
  /**
   * 一般布局
   * @param G6
   * @param options
   * @returns {G6.Graph}
   */
  commonGraph: function(G6, options) {
    let graphData = options.graphData
    let themeStyle = theme.defaultStyle // todo...先使用默认主题，后期可能增加其它风格的主体
    // 生成G6实例
    let graph = new G6.Graph({
      plugins: options.plugins,
      container: options.container,
      width: options.width,
      height: options.height,
      // layout: {
      //   type: 'random',
      //   width: options.width,
      //   height: options.height
      // },
      defaultNode: {
        type: 'cc-rect',
        labelCfg: {
          position: 'bottom'
        }
      },
      defaultEdge: {
        type: 'cc-line',
        labelCfg: {
          position: 'center',
          autoRotate: false
        }
      },
      nodeStateStyles: themeStyle.nodeStyle,
      // nodeStyle: {
      //   selected: {
      //     shadowColor: '#626262',
      //     shadowBlur: 8,
      //     shadowOffsetX: -1,
      //     shadowOffsetY: 3
      //   }
      // },
      edgeStateStyles: themeStyle.edgeStyle,
      // edgeStyle: {
      //   default: {
      //     stroke: '#e2e2e2',
      //     lineWidth: 3,
      //     lineAppendWidth: 10
      //   },
      //   selected: {
      //     shadowColor: '#626262',
      //     shadowBlur: 3
      //   }
      // },
      modes: options.modes
    })
    // 将 read 方法分解成 data() 和 render 方法，便于整个生命周期的管理
    graph.read(graphData)
    graph.render()
    // 返回G6实例
    return graph
  },
  /**
   * 力导布局
   * @param G6
   * @param options
   * @returns {*}
   */
  forceLayoutGraph: function(resolve, G6, options) {
    let graphData = options.graphData
    let themeStyle = theme.defaultStyle // todo...先使用默认主题，后期可能增加其它风格的主体
    // 生成G6实例
    let graph = new G6.Graph({
      container: options.container,
      width: options.width,
      height: options.height,
      nodeStateStyles: themeStyle.nodeStyle,
      edgeStateStyles: themeStyle.edgeStyle
    })
    // 初始化力导布局
    let simulation = d3
      .forceSimulation()
      .force(
        'link',
        d3
          .forceLink()
          .id(function(d) {
            return d.id
          })
          .distance(linkDistance)
          .strength(0.5)
      )
      .force('charge', d3.forceManyBody().strength(-500).distanceMax(500).distanceMin(100))
      .force('center', d3.forceCenter(options.width / 2, options.height / 2))
    // 定义节点数据
    simulation.nodes(graphData.nodes).on('tick', ticked)
    // 定义连线数据
    let edges = []
    for (let i = 0; i < graphData.edges.length; i++) {
      edges.push({
        id: graphData.edges[i].id,
        source: graphData.edges[i].source,
        target: graphData.edges[i].target
      })
    }
    simulation.force('link').links(edges)
    graph.data(graphData)
    graph.render()

    function linkDistance(d) {
      return 150
    }

    function ticked() {
      // protect: planA: 移除事件监听器 planB: 手动停止力模拟
      if (graph.destroyed) {
        // simulation.nodes(graphData.nodes).on('tick', null)
        simulation.stop()
        return
      }
      if (!graph.get('data')) {
        // 若是第一次渲染，定义数据，渲染
        graph.data(graphData)
        graph.render()
      } else {
        // 后续渲染，直接刷新所有点和边的位置
        graph.refreshPositions()
      }
    }

    // 控制时间: 只布局10秒
    let t = setTimeout(function() {
      simulation.stop()
      resolve(graph)
    }, 10000)

    // 判断force-layout结束
    simulation.on('end', () => {
      clearTimeout(t)
      resolve(graph)
    })
  }
}

export default initGraph
