<template>
  <div class="echarts" />
</template>

<script>
  import * as echarts from 'echarts'
  import debounce from 'lodash/debounce'
  import theme from './theme/ms-echarts-theme.json'
  import { addListener, removeListener } from 'resize-detector'

  const INIT_TRIGGERS = ['theme', 'initOptions', 'autoResize']
  const REWATCH_TRIGGERS = ['manualUpdate', 'watchShallow']

  export default defineComponent({
    props: {
      option: {
        type: Object,
        default: () => {},
      },
      theme: {
        type: [String, Object],
        default: () => {},
      },
      initOptions: {
        type: Object,
        default: () => {},
      },
      group: {
        type: String,
        default: '',
      },
      autoResize: {
        type: Boolean,
        default: true,
      },
      watchShallow: {
        type: Boolean,
        default: false,
      },
      manualUpdate: {
        type: Boolean,
        default: false,
      },
    },
    data() {
      return {
        lastArea: 0,
      }
    },
    watch: {
      group(group) {
        this.chart.group = group
      },
    },
    created() {
      this.initOptionsWatcher()
      INIT_TRIGGERS.forEach((prop) => {
        this.$watch(
          prop,
          () => {
            this.refresh()
          },
          { deep: true }
        )
      })
      REWATCH_TRIGGERS.forEach((prop) => {
        this.$watch(prop, () => {
          this.initOptionsWatcher()
          this.refresh()
        })
      })
    },
    mounted() {
      if (this.option) {
        echarts.registerTheme('ms-echarts-theme', theme)
        this.init()
      }
    },
    activated() {
      if (this.autoResize) {
        this.chart && this.chart.resize()
      }
    },
    unmounted() {
      if (this.chart) {
        this.destroy()
      }
    },
    methods: {
      mergeOptions(option, notMerge, lazyUpdate) {
        if (this.manualUpdate) {
          this.manualOptions = option
        }
        if (!this.chart) {
          this.init(option)
        } else {
          this.delegateMethod('setOption', option, notMerge, lazyUpdate)
        }
      },
      appendData(params) {
        this.delegateMethod('appendData', params)
      },
      resize(option) {
        this.delegateMethod('resize', option)
      },
      dispatchAction(payload) {
        this.delegateMethod('dispatchAction', payload)
      },
      convertToPixel(finder, value) {
        return this.delegateMethod('convertToPixel', finder, value)
      },
      convertFromPixel(finder, value) {
        return this.delegateMethod('convertFromPixel', finder, value)
      },
      containPixel(finder, value) {
        return this.delegateMethod('containPixel', finder, value)
      },
      showLoading(type, option) {
        this.delegateMethod('showLoading', type, option)
      },
      hideLoading() {
        this.delegateMethod('hideLoading')
      },
      getDataURL(option) {
        return this.delegateMethod('getDataURL', option)
      },
      getConnectedDataURL(option) {
        return this.delegateMethod('getConnectedDataURL', option)
      },
      clear() {
        this.delegateMethod('clear')
      },
      dispose() {
        this.delegateMethod('dispose')
      },
      delegateMethod(name, ...args) {
        if (!this.chart) {
          this.init()
        }
        return this.chart[name](...args)
      },
      delegateGet(methodName) {
        if (!this.chart) {
          this.init()
        }
        return this.chart[methodName]()
      },
      getArea() {
        return this.$el.offsetWidth * this.$el.offsetHeight
      },
      init(option) {
        if (this.chart) {
          return
        }
        const chart = echarts.init(this.$el, this.theme, this.initOptions)
        if (this.group) {
          chart.group = this.group
        }
        chart.clear()
        chart.setOption(option || this.manualOptions || this.option || {}, true)
        Object.keys(this.$attrs).forEach((event) => {
          const handler = this.$attrs[event]
          if (event.indexOf('zr:') === 0) {
            chart.getZr().on(event.slice(3), handler)
          } else {
            chart.on(event, handler)
          }
        })
        if (this.autoResize) {
          this.lastArea = this.getArea()
          this.__resizeHandler = debounce(
            () => {
              if (this.lastArea === 0) {
                this.mergeOptions({}, true)
                this.resize()
                this.mergeOptions(this.option || this.manualOptions || {}, true)
              } else {
                this.resize()
              }
              this.lastArea = this.getArea()
            },
            100,
            { leading: true }
          )
          addListener(this.$el, this.__resizeHandler)
        }
        this.chart = chart
        Object.defineProperties(this, {
          width: {
            configurable: true,
            get: () => {
              return this.delegateGet('getWidth')
            },
          },
          height: {
            configurable: true,
            get: () => {
              return this.delegateGet('getHeight')
            },
          },
          isDisposed: {
            configurable: true,
            get: () => {
              return !!this.delegateGet('isDisposed')
            },
          },
          computedOptions: {
            configurable: true,
            get: () => {
              return this.delegateGet('getOption')
            },
          },
        })
      },
      initOptionsWatcher() {
        if (this.__unwatchOptions) {
          this.__unwatchOptions()
          this.__unwatchOptions = null
        }
        if (!this.manualUpdate) {
          this.__unwatchOptions = this.$watch(
            'option',
            (val, oldVal) => {
              if (!this.chart && val) {
                this.init()
              } else {
                this.chart.setOption(val, val !== oldVal)
              }
            },
            { deep: !this.watchShallow }
          )
        }
      },
      destroy() {
        if (this.autoResize) {
          removeListener(this.$el, this.__resizeHandler)
        }
        this.dispose()
        this.chart = null
      },
      refresh() {
        if (this.chart) {
          this.destroy()
          this.init()
        }
      },
    },
    connect(group) {
      if (typeof group !== 'string') {
        group = group.map((chart) => chart.chart)
      }
      echarts.connect(group)
    },
    disconnect(group) {
      echarts.disConnect(group)
    },
    getMap(mapName) {
      return echarts.getMap(mapName)
    },
    registerMap(mapName, geoJSON, specialAreas) {
      echarts.registerMap(mapName, geoJSON, specialAreas)
    },
    graphic: echarts.graphic,
  })
</script>
<style>
  .echarts {
    width: 600px;
    height: 400px;
  }
</style>
