text: /**
   * @fileOverview node item
   * @author huangtonger@aliyun.com
   */
  
  const Util = require('../util/')
  const Item = require('./item')
  const CACHE_ANCHOR_POINTS = 'anchorPointsCache'
  const CACHE_BBOX = 'bboxCache'
  
  function getNearestPoint(points, curPoint) {
    let index = 0
    let nearestPoint = points[0]
    let minDistance = pointDistance(points[0], curPoint)
    for (let i = 0; i < points.length; i++) {
      const point = points[i]
      const distance = pointDistance(point, curPoint)
      if (distance < minDistance) {
        nearestPoint = point
        minDistance = distance
        index = i
      }
    }
    nearestPoint.anchorIndex = index
    return nearestPoint
  }
  
  function pointDistance(p1, p2) {
    return (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)
  }
  
  class Node extends Item {
    getDefaultCfg() {
      return {
        type: 'node',
        anchors: [],
        edges: [],
        status: []
      }
    }
  
    // getNeighbors() {
    //   const nodes = [];
    //   let node = null;
    //   Util.each(this.get('edges'), edge => {
    //     if (edge.get('source') === this) {
    //       node = edge.get('target');
    //     } else {
    //       node = edge.get('source');
    //     }
    //     if (nodes.indexOf(node) < 0) {
    //       nodes.push(node);
    //     }
    //   });
    //   return nodes;
    // }
  
    /**
     * 获取从节点关联的所有边
     * @return {Array} 边的集合
     */
    getEdges() {
      return this.get('edges')
    }
    /**
     * 获取引入节点的边 target == this
     * @return {Array} 边的集合
     */
    getInEdges() {
      const self = this
      return this.get('edges').filter(edge => {
        return edge.get('target') === self
      })
    }
    /**
     * 获取从节点引出的边 source == this
     * @return {Array} 边的集合
     */
    getOutEdges() {
      const self = this
      return this.get('edges').filter(edge => {
        return edge.get('source') === self
      })
    }
  
    // showAnchors() {
    //   // todo
    // }
    // hideAnchors() {
  
    // }
    /**
     * 根据锚点的索引获取连接点
     * @param  {Number} index 索引
     * @return {Object} 连接点 {x,y}
     */
    getLinkPointByAnchor(index) {
      const anchorPoints = this.getAnchorPoints()
      return anchorPoints[index]
    }
  
    /**
      * 获取连接点
      * @param {Object} point 节点外面的一个点，用于计算交点、最近的锚点
      * @return {Object} 连接点 {x,y}
      */
    getLinkPoint(point) {
      // const model = this.get('model');
      const keyShape = this.get('keyShape')
      const type = keyShape.get('type')
      const bbox = this.getBBox()
      const { centerX, centerY } = bbox
      const anchorPoints = this.getAnchorPoints()
      let intersectPoint
      switch (type) {
        case 'circle':
          intersectPoint = Util.getCircleIntersectByPoint({
            x: centerX,
            y: centerY,
            r: bbox.width / 2
          }, point)
          break
        case 'ellipse':
          intersectPoint = Util.getEllispeIntersectByPoint({
            x: centerX,
            y: centerY,
            rx: bbox.width / 2,
            ry: bbox.height / 2
          }, point)
          break
        default:
          intersectPoint = Util.getRectIntersectByPoint(bbox, point)
      }
      let linkPoint = intersectPoint
      // 如果存在锚点，则使用交点计算最近的锚点
      if (anchorPoints.length) {
        if (!linkPoint) { // 如果计算不出交点
          linkPoint = point
        }
        linkPoint = getNearestPoint(anchorPoints, linkPoint)
      }
      if (!linkPoint) { // 如果最终依然没法找到锚点和连接点，直接返回中心点
        linkPoint = { x: centerX, y: centerY }
      }
      return linkPoint
    }
  
    /**
     * 锁定节点
     */
    lock() {
      this.set('locked', true)
    }
  
    /**
     * 解锁锁定的节点
     */
    unlock() {
      this.set('locked', false)
    }
  
    hasLocked() {
      return this.get('locked')
    }
  
    /**
     * 添加边
     * @param {Edge} edge 边
     */
    addEdge(edge) {
      this.get('edges').push(edge)
    }
  
    /**
     * 移除边
     * @param {Edge} edge 边
     */
    removeEdge(edge) {
      const edges = this.getEdges()
      const index = edges.indexOf(edge)
      if (index > -1) {
        edges.splice(index, 1)
      }
    }
  
    clearCache() {
      this.set(CACHE_BBOX, null) // 清理缓存的 bbox
      this.set(CACHE_ANCHOR_POINTS, null)
    }
  
    // 是否仅仅移动节点，其他属性没变化
    _isOnlyMove(cfg) {
      if (!cfg) {
        return false // 刷新时不仅仅移动
      }
      // 不能直接使用 cfg.x && cfg.y 这类的判定，因为 0 的情况会出现
      const existX = !Util.isNil(cfg.x)
      const existY = !Util.isNil(cfg.y)
      const keys = Object.keys(cfg)
      return (keys.length === 1 && (existX || existY)) || // 仅有一个字段，包含 x 或者 包含 y
        (keys.length === 2 && existX && existY) // 两个字段，同时有 x，同时有 y
    }
  
    /**
     * 获取锚点的定义
     * @return {array} anchorPoints， {x,y,...cfg}
     */
    getAnchorPoints() {
      let anchorPoints = this.get(CACHE_ANCHOR_POINTS)
      if (!anchorPoints) {
        anchorPoints = []
        const shapeFactory = this.get('shapeFactory')
        const bbox = this.getBBox()
        const model = this.get('model')
        const shapeCfg = this.getShapeCfg(model)
        const points = shapeFactory.getAnchorPoints(model.shape, shapeCfg) || []
        Util.each(points, (pointArr, index) => {
          const point = Util.mix({
            x: bbox.minX + pointArr[0] * bbox.width,
            y: bbox.minY + pointArr[1] * bbox.height
          }, pointArr[2], {
            index
          })
          anchorPoints.push(point)
        })
        this.set(CACHE_ANCHOR_POINTS, anchorPoints)
      }
      return anchorPoints
    }
  }
  module.exports = Node
  