<template>
    <div class="focus-input-number" :class="[
      inputNumberSize ? 'el-input-number--' + inputNumberSize : '',
      { 'disabled': disabled },
      { 'is-without-controls': !controls },
      { 'is-controls-right': controlsAtRight }
    ]">

    <span v-if="controls" class="focus-input-number-decrease" :class="{'disabled': minDisabled}"
          v-repeat-click="decrease"
          @keydown.enter="decrease"
          role="button">
        <focus-icon :name="controlsAtRight ? 'down' : 'deduct'"></focus-icon>
    </span>

    <span v-if="controls" class="focus-input-number-increase" :class="{'disabled': maxDisabled}"
          v-repeat-click="increase"
          @keydown.enter="increase"
          role="button">

        <focus-icon :name="controlsAtRight ? 'up' : 'add'"></focus-icon>
    </span>

    <focus-input :value="currentValue" :disabled="disabled" :size="inputNumberSize" :max="max" :min="min"
                 :name="name" ref="input" :label="label"
                 @keydown.up.native.prevent="increase"
                 @keydown.down.native.prevent="decrease"
                 @blur="handleBlur"
                 @focus="handleFocus"
                 @input="debounceHandleInput">

        <template slot="prepend" v-if="$slots.prepend">
            <slot name="prepend"></slot>
        </template>

        <template slot="append" v-if="$slots.append">
            <slot name="append"></slot>
        </template>
    </focus-input>

    </div>
</template>
<script>
  import focusInput from 'focus-view/packages/input';
  import debounce from 'throttle-debounce/debounce';
  import Focus from 'focus-view/src/mixins/focus';
  import RepeatClick from 'focus-view/src/directives/repeat-click';
  import FocusIcon from 'focus-view/packages/icon';

  export default {
    name: 'focusInputNumber',
    mixins: [Focus('input')],
    inject: {
      elFormItem: {
        default: ''
      }
    },
    directives: {
      repeatClick: RepeatClick
    },
    components: {
        FocusIcon,
        focusInput
    },
    props: {
      step: {
        type: Number,
        default: 1
      },
      max: {
        type: Number,
        default: Infinity
      },
      min: {
        type: Number,
        default: -Infinity
      },
      value: {
        default: 0
      },
      disabled: Boolean,
      size: String,
      controls: {
        type: Boolean,
        default: true
      },
      controlsPosition: {
        type: String,
        default: ''
      },
      debounce: {
        type: Number,
        default: 300
      },
      name: String,
      label: String
    },
    data() {
      return {
        currentValue: 0
      };
    },
    watch: {
      value: {
        immediate: true,
        handler(value) {
          let newVal = Number(value);
          if (isNaN(newVal)) return;
          if (newVal >= this.max) newVal = this.max;
          if (newVal <= this.min) newVal = this.min;
          this.currentValue = newVal;
          this.$emit('input', newVal);
        }
      }
    },
    computed: {
      minDisabled() {
        return this._decrease(this.value, this.step) < this.min;
      },
      maxDisabled() {
        return this._increase(this.value, this.step) > this.max;
      },
      precision() {
        const { value, step, getPrecision } = this;
        return Math.max(getPrecision(value), getPrecision(step));
      },
      controlsAtRight() {
        return this.controlsPosition === 'right';
      },
      _elFormItemSize() {
        return (this.elFormItem || {}).elFormItemSize;
      },
      inputNumberSize() {
        return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
      }
    },
    methods: {
      toPrecision(num, precision) {
        if (precision === undefined) precision = this.precision;
        return parseFloat(parseFloat(Number(num).toFixed(precision)));
      },
      getPrecision(value) {
        const valueString = value.toString();
        const dotPosition = valueString.indexOf('.');
        let precision = 0;
        if (dotPosition !== -1) {
          precision = valueString.length - dotPosition - 1;
        }
        return precision;
      },
      _increase(val, step) {
        if (typeof val !== 'number') return this.currentValue;

        const precisionFactor = Math.pow(10, this.precision);

        return this.toPrecision((precisionFactor * val + precisionFactor * step) / precisionFactor);
      },
      _decrease(val, step) {
        if (typeof val !== 'number') return this.currentValue;

        const precisionFactor = Math.pow(10, this.precision);

        return this.toPrecision((precisionFactor * val - precisionFactor * step) / precisionFactor);
      },
      increase() {
        if (this.disabled || this.maxDisabled) return;
        const value = this.value || 0;
        const newVal = this._increase(value, this.step);
        if (newVal > this.max) return;
        this.setCurrentValue(newVal);
      },
      decrease() {
        if (this.disabled || this.minDisabled) return;
        const value = this.value || 0;
        const newVal = this._decrease(value, this.step);
        if (newVal < this.min) return;
        this.setCurrentValue(newVal);
      },
      handleBlur(event) {
        this.$emit('blur', event);
        this.$refs.input.setCurrentValue(this.currentValue);
      },
      handleFocus(event) {
        this.$emit('focus', event);
      },
      setCurrentValue(newVal) {
        const oldVal = this.currentValue;
        if (newVal >= this.max) newVal = this.max;
        if (newVal <= this.min) newVal = this.min;
        if (oldVal === newVal) {
          this.$refs.input.setCurrentValue(this.currentValue);
          return;
        }
        this.$emit('change', newVal, oldVal);
        this.$emit('input', newVal);
        this.currentValue = newVal;
      },
      handleInput(value) {
        if (value === '') {
          return;
        }

        if (value.indexOf('.') === (value.length - 1)) {
          return;
        }

        if (value.indexOf('-') === (value.length - 1)) {
          return;
        }

        const newVal = Number(value);
        if (!isNaN(newVal)) {
          this.setCurrentValue(newVal);
        } else {
          this.$refs.input.setCurrentValue(this.currentValue);
        }
      }
    },
    created() {
      this.debounceHandleInput = debounce(this.debounce, value => {
        this.handleInput(value);
      });
    },
    mounted() {
      let innerInput = this.$refs.input.$refs.input;
      innerInput.setAttribute('role', 'spinbutton');
      innerInput.setAttribute('aria-valuemax', this.max);
      innerInput.setAttribute('aria-valuemin', this.min);
      innerInput.setAttribute('aria-valuenow', this.currentValue);
      innerInput.setAttribute('aria-disabled', this.disabled);
    },
    updated() {
      let innerInput = this.$refs.input.$refs.input;
      innerInput.setAttribute('aria-valuenow', this.currentValue);
    }
  };
</script>
