<template>
  <div
    :class="[
      'v-input-number',
      { medium: size === 'medium' },
      { small: size === 'small' },
      { mini: size === 'mini' }
    ]"
  >
    <span
      @click="handleClick('reduction')"
      v-if="controls"
      @mousedown="handleMouseDown('reduction')"
      @mouseup="handleMouseUp()"
      @mouseleave="handleMouseLeave"
      :class="[
        'v-input-number-span',
        { disabled: computedMin || disabled },
        { controlsPosition: controlsPosition === 'right' }
      ]"
    >
      <i class="button iconfont icon-jianhao"></i>
    </span>
    <span
      @click="handleClick('add')"
      v-if="controls"
      @mousedown="handleMouseDown('add')"
      @mouseup="handleMouseUp()"
      @mouseleave="handleMouseLeave"
      :class="[
        'v-input-number-span',
        { disabled: computedMax || disabled },
        { controlsPosition: controlsPosition === 'right' }
      ]"
    >
      <span class="button">+</span>
    </span>
    <input
      ref="cInputNumber"
      :class="[
        'v-input-number-input',
        { 'v-input-number-input-disabled': disabled },
        { 'v-input-number-input-inputDisabled': inputDisabled }
      ]"
      :placeholder="placeholder"
      :name="name"
      :disabled="disabled || inputDisabled"
      type="text"
      v-model="value"
      @change.stop="handleChange"
      @focus.stop="handleFocus"
      @blur.stop="handleBlur"
    />
  </div>
</template>

<script>
import VairInputNumber from '../../types/inputNumber';
import calculate from '../../hooks/calculate';
import { ref, watchEffect, computed, onMounted, onBeforeUnmount } from 'vue';
export default {
    name: 'input-number',
    props: VairInputNumber,
    setup (props, ctx) {
        const value = ref(0);
        const oldValue = ref(0);
        const cInputNumber = ref(null);
        const timer = ref(null);
        const { accAdd, subtr } = calculate.setup();

        onMounted(() => {
            watchEffect(() => {
                value.value = computedPropsModelValue.value;
                oldValue.value = computedPropsModelValue.value;
            });
        });

        const computedPropsModelValue = computed(() => {
            var number = 0;
            if (isNaN(props.modelValue)) {
                number = (props.min || props.min <= 0)? Math.floor(props.min) : 0;
            } else {
                if (props.min || props.min <= 0) {
                    number = Math.floor(props.min) <= props.modelValue? props.modelValue : Math.floor(props.min);
                } else {
                    number = props.modelValue;
                }
            }
            return number;
        });

        const handleChange = (event) => {
            const number = +event.target.value;
            if (isNaN(number) || event.target.value === '') {
                value.value = oldValue.value;
                return;
            } 
            if (!isNaN(props.min) && number <= props.min) {
                value.value = props.min;
            } else if (!isNaN(props.max) && number >= props.max) {
                value.value = props.max;
            }
            if (props.stepStrictly && !isNaN(props.step)) {
                const stepPrecision = getPrecision(props.step);
                const precisionFactor = Math.pow(10, stepPrecision);
                value.value = Math.round(value.value / props.step) * precisionFactor * props.step / precisionFactor;
            }
            ctx.emit('update:modelValue', +value.value);
            ctx.emit('change', +value.value, oldValue.value);
            oldValue.value = +value.value;
        };

        const getPrecision = (value) => {
            if (value === undefined) return 0;
            const valueString = value.toString();
            const dotPosition = valueString.indexOf('.');
            let precision = 0;
            if (dotPosition !== -1) {
            precision = valueString.length - dotPosition - 1;
            }
            return precision;
        };

        const handleClick = (type) => {
            if (type === 'add') {
                if (isNaN(props.max) || value.value < props.max) {
                    value.value = accAdd(value.value, computedStep.value('add'));
                }
            } else if (type === 'reduction') {
                if (isNaN(props.min) || value.value > props.min) {
                    value.value = subtr(value.value, computedStep.value('reduction'));
                }
            }
            if (value.value !== oldValue.value) {
                ctx.emit('update:modelValue', +value.value);
                ctx.emit('change', +value.value, oldValue.value);
                oldValue.value = value.value;
            }
        };

        const computedStep = computed(() => {
            return (type) => {
                var step = props.step > 0? props.step : 1;
                if (!isNaN(props.min) && type === 'reduction') {
                    if ((value.value - step) < props.min) {
                        step = value.value - props.min;
                    }
                } 
                if (!isNaN(props.max) && type === 'add') {
                    if ((value.value + step) > props.max) {
                        step = props.max - value.value;
                    }
                }
                return step;
            };
        });

        const computedMin = computed(() => {
            return !isNaN(props.min) && props.min >= value.value;
        });

        const computedMax = computed(() => {
            return !isNaN(props.max) &&  props.max <= value.value;
        });

        const handleFocus = (event) => {
            ctx.emit('focus', event);
        };

        const handleBlur = (event) => {
            ctx.emit('blur', event);
        };

        const focus = () => {
          cInputNumber.value.focus();
        };

        const blur = () => {
          cInputNumber.value.blur();
        };

        const select = () => {
          cInputNumber.value.select();
        };

        const handleMouseDown = (type) => {
            clearInterval(timer.value);
            timer.value = setTimeout(() => {
                clearInterval(timer.value);
                timer.value = setInterval(() => {
                    handleClick(type);
                }, 100);
            }, 200);
            
        };

        const handleMouseUp = () => {
            clearInterval(timer.value);
        };

        const handleMouseLeave = () => {
            clearInterval(timer.value);
        };

        onBeforeUnmount(() => {
            clearInterval(timer.value);
        });

        return {
            value,
            handleChange,
            handleClick,
            computedMin,
            computedMax,
            handleFocus,
            handleBlur,
            cInputNumber,
            handleMouseDown,
            handleMouseUp,
            handleMouseLeave,
            focus,
            blur,
            select
        }
    }
}
</script>

<style lang='less' scoped>
@import url('./index.less');
</style>