<template>
  <div :style="canvaSize" />
</template>

<style></style>

<script>
/* eslint-disable vue/require-default-prop */

import debounce from "lodash.debounce";
import { addListener, removeListener } from "resize-detector";
const echarts = require("echarts");

// enumerating ECharts events for now
// 以下事件可以通过 @[eventName] 进行监听，并且都接受一个参数（触发的事件）
const EVENTS = [
  "legendselectchanged",
  "legendselected",
  "legendunselected",
  "legendscroll",
  "datazoom",
  "datarangeselected",
  "timelinechanged",
  "timelineplaychanged",
  "restore",
  "dataviewchanged",
  "magictypechanged",
  "geoselectchanged",
  "geoselected",
  "geounselected",
  "pieselectchanged",
  "pieselected",
  "pieunselected",
  "mapselectchanged",
  "mapselected",
  "mapunselected",
  "axisareaselected",
  "focusnodeadjacency",
  "unfocusnodeadjacency",
  "brush",
  "brushselected",
  "rendered",
  "finished",
  "click",
  "dblclick",
  "mouseover",
  "mouseout",
  "mousemove",
  "mousedown",
  "mouseup",
  "globalout",
  "contextmenu"
];

const ZR_EVENTS = [
  "click",
  "mousedown",
  "mouseup",
  "mousewheel",
  "dblclick",
  "contextmenu"
];

const INIT_TRIGGERS = ["theme", "initOptions", "autoresize"];
const REWATCH_TRIGGERS = ["manualUpdate", "watchShallow"];

export default {
  props: {
    options: Object,
    theme: [String, Object],
    initOptions: Object,
    group: String,
    autoresize: Boolean,
    watchShallow: Boolean,
    manualUpdate: Boolean,
    width: {
      type: Number,
      default: 600
    },
    height: {
      type: Number,
      default: 400
    }
  },
  data() {
    return {
      lastArea: 0
    };
  },
  computed: {
    canvaSize() {
      return { height: `${this.height}px`, width: `${this.width}px` };
    }
  },
  watch: {
    group(group) {
      this.chart.group = group;
    },
    options: {
      deep: true,
      handler() {
        this.refresh();
      }
    }
  },
  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() {
    // auto init if `options` is already provided
    if (this.options) {
      this.init();
    }
  },
  activated() {
    if (this.autoresize) {
      this.chart && this.chart.resize();
    }
  },
  unmounted() {
    if (this.chart) {
      this.destroy();
    }
  },
  methods: {
    // provide an explicit merge option method
    mergeOptions(options, notMerge, lazyUpdate) {
      if (this.manualUpdate) {
        this.manualOptions = options;
      }

      if (!this.chart) {
        this.init(options);
      } else {
        this.delegateMethod("setOption", options, notMerge, lazyUpdate);
      }
    },
    // just delegates ECharts methods to Vue component
    // use explicit params to reduce transpiled size for now
    appendData(params) {
      this.delegateMethod("appendData", params);
    },
    resize(options) {
      this.delegateMethod("resize", options);
    },
    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, options) {
      this.delegateMethod("showLoading", type, options);
    },
    hideLoading() {
      this.delegateMethod("hideLoading");
    },
    getDataURL(options) {
      return this.delegateMethod("getDataURL", options);
    },
    getConnectedDataURL(options) {
      return this.delegateMethod("getConnectedDataURL", options);
    },
    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(options) {
      if (this.chart) {
        return;
      }

      const chart = echarts.init(this.$el, this.theme, this.initOptions);

      if (this.group) {
        chart.group = this.group;
      }

      chart.setOption(options || this.manualOptions || this.options || {}, true);

      // expose ECharts events as custom events
      EVENTS.forEach((event) => {
        chart.on(event, (params) => {
          this.$emit(event, params);
        });
      });

      ZR_EVENTS.forEach((event) => {
        chart.getZr().on(event, (params) => {
          this.$emit(`zr:${event}`, params);
        });
      });

      if (this.autoresize) {
        this.lastArea = this.getArea();
        this.__resizeHandler = debounce(
            () => {
              if (this.lastArea === 0) {
                // emulate initial render for initially hidden charts
                this.mergeOptions({}, true);
                this.resize();
                this.mergeOptions(this.options || this.manualOptions || {}, true);
              } else {
                this.resize();
              }
              this.lastArea = this.getArea();
            },
            100,
            { leading: true }
        );
        addListener(this.$el, this.__resizeHandler);
      }

      Object.defineProperties(this, {
        // Only recalculated when accessed from JavaScript.
        // Won"t update DOM on value reset because getters
        // don"t depend on reactive values
        width: {
          configurable: true,
          getByCommitId: () => {
            return this.delegateGet("getWidth");
          }
        },
        height: {
          configurable: true,
          getByCommitId: () => {
            return this.delegateGet("getHeight");
          }
        },
        isDisposed: {
          configurable: true,
          getByCommitId: () => {
            return !!this.delegateGet("isDisposed");
          }
        },
        computedOptions: {
          configurable: true,
          getByCommitId: () => {
            return this.delegateGet("getOption");
          }
        }
      });

      this.chart = chart;
    },
    initOptionsWatcher() {
      if (this.__unwatchOptions) {
        this.__unwatchOptions();
        this.__unwatchOptions = null;
      }

      if (!this.manualUpdate) {
        this.__unwatchOptions = this.$watch(
            "options",
            (val, oldVal) => {
              if (!this.chart && val) {
                this.init();
              } else {
                // mutating `options` will lead to merging
                // replacing it with new reference will lead to not merging
                // eg.
                // `this.options = Object.assign({}, this.options, { ... })`
                // will trigger `this.chart.setOption(val, true)
                // `this.options.title.text = "Trends"`
                // will trigger `this.chart.setOption(val, false)`
                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);
  },
  registerMap(mapName, geoJSON, specialAreas) {
    echarts.registerMap(mapName, geoJSON, specialAreas);
  },
  registerTheme(name, theme) {
    echarts.registerTheme(name, theme);
  },
  graphic: echarts.graphic
};
</script>
