<template>
  <div :class="prefixCls">
    <div v-if="title || $slots.title" :class="`${prefixCls}-title`">
      <slot name="title">
        {{ title }}
      </slot>
    </div>
    <div :class="`${prefixCls}-content`">
      <div :class="`${prefixCls}-value`" :style="valueStyle">
        <span v-if="showPlaceholder">{{ placeholder }}</span>
        <template v-else>
          <span v-if="$slots.prefix" :class="`${prefixCls}-prefix`">
            <slot name="prefix" />
          </span>
          <template v-if="formatValue.isNumber">
            <span :class="`${prefixCls}-value-integer`">
              {{ formatValue.integer }}
            </span>
            <span
              v-if="formatValue.decimal"
              :class="`${prefixCls}-value-decimal`"
            >
              .{{ formatValue.decimal }}
            </span>
          </template>
          <template v-else>
            {{ formatValue.value }}
          </template>
          <span v-if="$slots.suffix" :class="`${prefixCls}-suffix`">
            <slot name="suffix" />
          </span>
        </template>
      </div>
      <div v-if="extra || $slots.extra" :class="`${prefixCls}-extra`">
        <slot name="extra">
          {{ extra }}
        </slot>
      </div>
    </div>
  </div>
</template>

<script>
import dayjs from 'dayjs';
import BTween from 'b-tween';
import NP from 'number-precision';

export default {
  name: 'ElStatistic',
  props: {
    /**
     * @zh 数值显示的标题
     * @en Title of the numerical display
     */
    title: String,
    /**
     * @zh 数值显示的值
     * @en Numerical display value
     */
    value: {
      type: [Number, Object, Date]
    },
    /**
     * @zh 数值显示的格式 [dayjs](https://day.js.org/docs/en/display/format)（日期模式使用）
     * @en Format of numerical display [dayjs](https://day.js.org/docs/en/display/format) (used in date mode)
     */
    format: {
      type: String,
      default: 'HH:mm:ss',
    },
    /**
     * @zh 额外的显示内容
     * @en Additional display content
     */
    extra: String,
    /**
     * @zh 是否开始动画
     * @en Whether to start animation
     */
    start: {
      type: Boolean,
      default: true,
    },
    /**
     * @zh 小数保留位数（数字模式使用）
     * @en Decimal reserved digits (used in digital mode)
     */
    precision: {
      type: Number,
      default: 0,
    },
    /**
     * @zh 进位分隔符（数字模式使用）
     * @en Carry separator (used in number mode)
     */
    separator: String,
    /**
     * @zh 是否展示进位分隔符（数字模式使用）
     * @en Whether to display the carry separator (used in number mode)
     */
    showGroupSeparator: {
      type: Boolean,
      default: false,
    },
    /**
     * @zh 是否开启动画
     * @en Whether to turn on animation
     */
    animation: {
      type: Boolean,
      default: false,
    },
    /**
     * @zh 动画的过度时间
     * @en Animation's duration time
     */
    animationDuration: {
      type: Number,
      default: 2000,
    },
    /**
     * @zh 动画的起始值
     * @en The starting value of the animation
     */
    valueFrom: {
      type: Number,
      default: undefined,
    },
    /**
     * @zh 提示文字（当 value 为 undefined 时显示）
     * @en Prompt text (displayed when value is undefined )
     * @version 2.28.0
     */
    placeholder: {
      type: String,
    },
    /**
     * @zh 自定义显示值的样式
     * @en Custom value style
     * @version 2.32.0
     */
    valueStyle: {
      type: Object
    },
  },
  /**
   * @zh 标题
   * @en Title
   * @slot title
   */
  /**
   * @zh 额外内容
   * @en Extra content
   * @slot extra
   */
  /**
   * @zh 前缀
   * @en Prefix
   * @slot prefix
   */
  /**
   * @zh 后缀
   * @en Suffix
   * @slot suffix
   */
  data() {
    return {
      prefixCls: 'el-statistic',
      tween: null,
      innerValue: ''
    }
  },
  computed: {
    numberValue() {
      if (Object.prototype.toString.call(this.value) === '[object Number]') {
        return this.value;
      }
      return 0
    },
    showPlaceholder() {
      return this.value === undefined;
    },
    formatValue() {
      let _value = this.innerValue;
      if (Object.prototype.toString.call(_value) === '[object Number]') {
        if (Object.prototype.toString.call(this.precision) === '[object Number]') {
          _value = NP.round(_value, this.precision).toFixed(this.precision);
        }
        const splitValue = String(_value).split('.');
        const integer = this.showGroupSeparator
          ? Number(splitValue[0]).toLocaleString('en-US')
          : splitValue[0];
        const decimal = splitValue[1];
        return {
          isNumber: true,
          integer,
          decimal,
        };
      }
      if (this.format) {
        _value = dayjs(_value).format(this.format);
      }
      return {
        isNumber: false,
        value: _value,
      };
    }
  },
  watch: {
    start() {
      if (this.value && this.animation && !this.tween) {
        this.animationFnc();
      }
    },
    value(val) {
      if (this.tween) {
        this.tween.stop();
        this.tween = null;
      }
      this.innerValue = val;
      if (this.animation && this.start) {
        this.animationFnc();
      }
    }
  },
  created() {
    if (
        Object.prototype.toString.call(this.valueFrom) === '[object Null]' ||
        this.valueFrom === undefined
      ) {
        this.innerValue = this.value;
      } else {
        this.innerValue = this.valueFrom;
      }
  },
  mounted() {
    if (this.animation && this.start) {
        this.animationFnc();
    }
  },
  methods: {
    animationFnc(from, to) {
      if (from !== to) {
        this.tween = new BTween({
          from: {
            value: from,
          },
          to: {
            value: to,
          },
          duration: this.animationDuration,
          easing: 'quartOut',
          onUpdate: (keys) => {
            this.innerValue = keys.value;
          },
          onFinish: () => {
            this.innerValue = to;
          },
        });
        this.tween.start();
      }
    }
  }
}
</script>