/**
 * @author claude
 * @date 2020/3/15
 * @description 注册基础节点, 其他节点都在此基础上继承和扩展
 */

import itemEvents from './item-event'
import anchorEvent from './anchor-event'
import defaultStyles from '../defaultStyles'
import startIcon from '../../../icon/start.svg'
import endIcon from '../../../icon/end.svg'
import decisionIcon from '../../../icon/decision.svg'
import decisionMergeIcon from '../../../icon/decisionMerge.svg'
import abnormalIcon from '../../../icon/abnormal.svg'
import timeIntervalIcon from '../../../icon/timeInterval.svg'
import dateIcon from '../../../icon/date.svg'
import dataIcon from '../../../icon/data.svg'
import automaticIcon from '../../../icon/automatic.svg'
import humanIcon from '../../../icon/human.svg'
import mergeIcon from '../../../icon/merge.svg'
import forkIcon from '../../../icon/fork.svg'
import deleteIcon from '../../../icon/delete.svg'
import subProcIcon from '../../../icon/subProc.svg'

const {
  iconStyles, nodeStyles, anchorPointStyles, nodeLabelStyles
} = defaultStyles

const nodeTypeToImg = {
  delete: deleteIcon,
  start: startIcon,
  end: endIcon,
  decision: decisionIcon,
  decisionMerge: decisionMergeIcon,
  abnormal: abnormalIcon,
  timeInterval: timeIntervalIcon,
  date: dateIcon,
  automatic: automaticIcon,
  human: humanIcon,
  merge: mergeIcon,
  data: dataIcon,
  fork: forkIcon,
  subProc: subProcIcon
}
function getStyle(options, cfg) {
  return {
    ...cfg,
    // 自定义默认样式
    ...nodeStyles,
    ...options,
    // 当前节点样式
    ...cfg.style,
    // 文本配置
    labelCfg: {
      ...nodeLabelStyles,
      ...cfg.labelCfg
    },
    // 图标样式
    iconStyles: {
      ...iconStyles,
      ...cfg.iconStyles
    },
    // 锚点样式
    anchorPointStyles: {
      ...anchorPointStyles,
      ...cfg.anchorPointStyles
    },
    ...cfg.nodeStateStyles,
    // 锚点高亮样式
    anchorHotsoptStyles: cfg.anchorHotsoptStyles
  }
}

/*
 * 注册基础node => 添加锚点/图标 => 绘制node => 初始化node状态 => node动画(设置交互动画)
 */
export default G6 => {
  G6.registerNode(
    'base-node',
    {
      getShapeStyle(cfg) {
        const width = cfg.style.width || 80
        const height = cfg.style.height || 40
        return getStyle.call(
          this,
          {
            width,
            height,
            radius: 5,
            // 将图形中心坐标移动到图形中心, 用于方便鼠标位置计算
            x: -width / 2,
            y: -height / 2
          },
          cfg
        )
      },
      // 绘制图标
      drawIcon(cfg, group, attrs) {
        const nodeType = cfg.customAttrs.nodeType
        const { logoIcon, stateIcon } = attrs
        logoIcon.img = nodeTypeToImg[nodeType]
        const icons = [logoIcon, stateIcon]
        icons.forEach(($item, index) => {
          if ($item) {
            const className = `${attrs.type}-${index === 0 ? 'logoIcon' : 'stateIcon'}`

            let item = group.$getItem(className)

            if (!item) {
              const icon = group.addShape($item.img ? 'image' : 'text', {
                attrs: {
                  ...$item,
                  ...$item.style
                },
                draggable: true,
                className
              })

              icon.toFront()
              item = group.$getItem(className)
            }

            if (item) {
              if ($item.show) {
                item.show()
              }
              else {
                item.hide()
              }
            }
          }
        })
      },
      // 绘制锚点
      initAnchor(cfg, group) {
        group.anchorShapes = []
        group.showAnchor = () => {
          this.drawAnchor(cfg, group)
        }
        group.clearAnchor = () => {
          if (group.anchorShapes) {
            const line = group.$getItem('dashed-line')

            if (line) {
              line.remove()
            }
            group.anchorShapes.forEach(a => a.remove())
          }
          group.anchorShapes = []
        }
      },
      drawAnchor(cfg, group) {
        const { type, direction, anchorPointStyles } = group.getFirst().attr()
        const item = group.get('children')[0]
        const bBox = item.getBBox()
        const anchors = this.getAnchorPoints(cfg)

        // 绘制锚点坐标
        anchors
          && anchors.forEach((p, i) => {
            const diff = type === 'triangle-node' ? (direction === 'up' ? 1 : 0) : 0.5
            const x = bBox.width * (p[0] - 0.5)
            const y = bBox.height * (p[1] - diff)

            /**
             * 绘制三层锚点
             * 最底层: 锚点bg
             * 中间层: 锚点
             * 最顶层: 锚点group, 用于事件触发
             */
            // 视觉锚点
            const anchor = group.addShape('circle', {
              attrs: {
                x,
                y,
                ...anchorPointStyles
              },
              zIndex: 1,
              nodeId: group.get('id'),
              className: 'node-anchor',
              draggable: true,
              isAnchor: true,
              index: i
            })

            // 锚点事件触发的元素
            const anchorGroup = group.addShape('circle', {
              attrs: {
                x,
                y,
                r: 11,
                fill: '#000',
                opacity: 0
              },
              zIndex: 2,
              nodeId: group.get('id'),
              className: 'node-anchor-group',
              draggable: true,
              isAnchor: true,
              index: i
            })

            /**
             * 添加锚点事件绑定
             */
            anchorEvent(anchorGroup, group, p)

            group.anchorShapes.push(anchor)
            group.anchorShapes.push(anchorGroup)
          })

        // 查找所有锚点
        group.getAllAnchors = () => group.anchorShapes.filter(c => c.get('isAnchor') === true)
        // 查找指定锚点
        group.getAnchor = i =>
          group.anchorShapes.filter(c => c.get('className') === 'node-anchor' && c.get('index') === i)
        // 查找所有锚点背景
        group.getAllAnchorBg = () => group.anchorShapes.filter(c => c.get('className') === 'node-anchor-bg')
      },
      /* 添加文本节点 */
      /* https://g6.antv.vision/zh/docs/manual/advanced/keyconcept/shape-and-properties/#%E6%96%87%E6%9C%AC-text */
      addLabel(cfg, group, attrs) {
        const { label, labelCfg, labels } = attrs
        // 字体小于12时 svg会报错
        /* if (labelCfg && labelCfg.fontSize < 12) {
        labelCfg.fontSize = 12
      } */

        // 多行文本
        if (labels) {
          labels.forEach(item => {
            const {
              label,
              labelCfg: { maxlength },
              className
            } = item

            let text = maxlength ? label.substr(0, maxlength) : label || ''

            if (label.length > maxlength) {
              text = `${text}...`
            }

            group.addShape('text', {
              attrs: {
                text,
                ...item,
                ...item.labelCfg,
                ...item.labelCfg.style
              },
              className: `node-text ${className}`,
              draggable: true
            })
          })
        }
        else if (label) {
          const { maxlength } = labelCfg

          let text = maxlength ? label.substr(0, maxlength) : label || ''

          if (label.length > maxlength) {
            text = `${text}...`
          }

          group.addShape('text', {
            attrs: {
              text,
              x: 0,
              y: 40,
              ...labelCfg,
              ...labelCfg.style
            },
            className: 'node-text',
            draggable: true
          })
        }
      },
      drawModelRect(group, attrs) {
        const { preRect, width, height } = attrs
        const $preRect = {
          show: true, // 是否显示左侧方条
          width: 4,
          fill: '#40a9ff',
          radius: 2
        }

        if (!preRect || preRect.show !== false) {
          group.addShape('rect', {
            attrs: {
              x: -width / 2,
              y: -height / 2,
              height,
              ...$preRect,
              ...preRect
            },
            draggable: true
          })
        }
      },
      /* 绘制节点，包含文本 */
      draw(cfg, group) {
        return this.drawShape(cfg, group)
      },
      /* 绘制节点，包含文本 */
      drawShape(cfg, group) {
        // 元素分组
        // 合并外部样式和默认样式
        const attrs = this.getShapeStyle(cfg, group)
        // 添加节点
        const shape = group.addShape(this.shapeType, {
          // shape 属性在定义时返回
          className: `${this.shapeType}-shape`,
          xShapeNode: true, // 自定义节点标识
          draggable: true,
          attrs
        })

        // 给 group 添加自定义方法 按className查找元素
        group.$getItem = className => group.get('children').find(item => item.get('className') === className)

        if (this.type === 'modelRect-node') {
          this.drawModelRect(group, attrs)
        }
        // 添加文本节点
        this.addLabel(cfg, group, attrs)
        // 添加图标
        this.drawIcon(cfg, group, attrs)
        // 添加锚点
        this.initAnchor(cfg, group)

        return shape
      },
      /* 更新节点，包含文本 */
      update(cfg, node) {
        const model = node.get('model')
        const { attrs } = node.get('keyShape')
        const group = node.get('group')
        const text = group.$getItem('node-text')
        const item = group.get('children')[0]
        let labelCfgStyle = {}
        if (model.labelCfg && model.labelCfg.style) {
          labelCfgStyle = model.labelCfg.style
        }
        // 更新文本内容
        text
          && text.attr({
            text: model.label,
            ...labelCfgStyle
          })
        // 更新节点属性
        if (attrs.type === 'diamond-node') {
          const path = this.getPath({
            style: {
              size: model.size
            }
          })
          item.attr({
            ...attrs,
            ...model.style,
            path,
            width: model.size && model.size[0],
            height: model.size && model.size[1]
          })
        }
        else {
          const logoIcon = group.get('children').find(x => x.cfg.className === `${attrs.type}-logoIcon`)

          if (logoIcon) {
            logoIcon.attr({ ...model.logoIcon })
          }

          const stateIcon = group.get('children').find(x => x.cfg.className === `${attrs.type}-stateIcon`)

          if (stateIcon) {
            stateIcon.attr({ ...model.stateIcon })
          }

          item.attr({
            ...attrs,
            ...model.style
          })
        }
      },
      /* 设置节点的状态，主要是交互状态，业务状态请在 draw 方法中实现 */
      setState(name, value, item) {
        const buildInEvents = [
          'anchorShow',
          'anchorActived',
          'nodeState',
          'nodeState:default',
          'nodeState:selected',
          'nodeState:hover',
          'nodeOnDragStart',
          'nodeOnDrag',
          'nodeOnDragEnd'
        ]
        const group = item.getContainer()

        if (group.get('destroyed')) {
          return
        }
        if (buildInEvents.includes(name)) {
          // 内部this绑定到了当前item实例
          itemEvents[name].call(this, value, group)
        }
        else if (this.stateApplying) {
          // eslint-disable-next-line no-useless-call
          this.stateApplying.call(this, name, value, item)
        }
        else {
          console.warn(
            `warning: ${name} 事件回调未注册!\n可继承该节点并通过 stateApplying 方法进行注册\n如已注册请忽略 (-_-!)`
          )
        }
      },
      /* 获取锚点（相关边的连入点） */
      getAnchorPoints(cfg) {
        return (
          cfg.anchorPoints || [
            [0.5, 0],
            [1, 0.5],
            [0.5, 1],
            [0, 0.5]
          ]
        )
      }
    },
    'single-node'
  )
}
