<template>
    <div>
        <q-input ref="input" outlined class="text-h6" :model-value="displayValue" :placeholder="placeholder"
            :disabled="inputNumberDisabled" :max="max" :min="min" :name="name" :label="label" @blur="handleBlur"
            @focus="handleFocus" @input="handleInput" @change="handleInputChange">
            <!-- @keydown.up.native.prevent="" -->
            <!-- @keydown.down.native.prevent="" -->
            <!-- <template v-slot:before> -->
            <template v-slot:prepend>
                <q-btn round flat icon="remove" v-touch-repeat:0:150.mouse.enter.space="decrease" @click="decrease"
                    :color="minDisabled ? 'grey-6' : 'negative'" :disable="minDisabled" @keydown.enter="decrease" />
            </template>
            <template v-slot:hint>
                Field hint
            </template>
            <!-- <template v-slot:after> -->
            <template v-slot:append>
                <!-- <q-icon name="add" :color="maxDisabled ? 'grey-6' : 'positive'" ></q-icon> -->
                <q-btn round flat icon="add" v-touch-repeat:0:150.mouse.enter.space="increase" @click="increase"
                    :color="maxDisabled ? 'grey-6' : 'positive'" :disable="maxDisabled" />
            </template>
        </q-input>
    </div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue';

const emit = defineEmits(['blur', 'focus', 'input', 'change']);

const input = ref();
const currentValue = ref();
const userInput = ref(null);

const props = defineProps({
    // 计数器步长
    step: {
        type: Number,
        default: 1
    },
    // 是否只能输入 step 的倍数
    stepStrictly: {
        type: Boolean,
        default: false
    },
    // 设置计数器允许的最大值
    max: {
        type: Number,
        default: Infinity
    },
    // 设置计数器允许的最小值
    min: {
        type: Number,
        default: -Infinity
    },
    modelValue: Number,
    disabled: {
        type: Boolean,
        default: false
    },
    name: String,
    label: String,
    placeholder: String,
    // 数值精度
    precision: {
        type: Number,
        validator(val: any) {
            return val >= 0 && val === parseInt(val, 10);
        }
    }
})

const minDisabled = computed(() => {
    return _decrease(props.modelValue, props.step) < props.min;
})

const maxDisabled = computed(() => {
    return _increase(props.modelValue, props.step) > props.max;
})

const numPrecision = computed(() => {
    const stepPrecision = getPrecision(props.step);
    if (props.precision !== undefined) {
        if (stepPrecision > props.precision) {
            console.warn('[Quasar Warn][InputNumber]precision should not be less than the decimal places of step');
        }
        return props.precision;
    } else {
        return Math.max(getPrecision(props.modelValue), stepPrecision);
    }
})

const inputNumberDisabled = computed(() => {
    return props.disabled;
})
const displayValue = computed(() => {
    if (userInput.value !== null) {
        return userInput.value;
    }

    let currentVal: number | string = currentValue.value;

    if (typeof currentVal === 'number') {
        if (props.stepStrictly) {
            const stepPrecision = getPrecision(props.step);
            const precisionFactor = Math.pow(10, stepPrecision);
            currentVal = Math.round(currentVal / props.step) * precisionFactor * props.step / precisionFactor;
        }

        if (props.precision !== undefined) {
            currentVal = currentVal.toFixed(props.precision);
        }
    }

    return currentVal;
})

const toPrecision = (num: any, precision?: any) => {
    if (precision === undefined) precision = numPrecision.value;
    return parseFloat(Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision));
}

const getPrecision = (value: any) => {
    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 _increase = (val: any, step: any) => {
    if (typeof val !== 'number' && val !== undefined) return currentValue.value;
    const precisionFactor = Math.pow(10, numPrecision.value);
    // Solve the accuracy problem of JS decimal calculation by converting the value to integer.
    // 通过将值转换为整数，解决JS十进制计算的精度问题。
    return toPrecision((precisionFactor * val + precisionFactor * step) / precisionFactor);
}

// 减少
const _decrease = (val: any, step: any) => {
    if (typeof val !== 'number' && val !== undefined) return currentValue.value;
    const precisionFactor = Math.pow(10, numPrecision.value);
    return toPrecision((precisionFactor * val - precisionFactor * step) / precisionFactor);
}

// 增加
const increase = () => {
    if (inputNumberDisabled.value || maxDisabled.value) return;
    const value = props.modelValue || 0;
    const newVal = _increase(value, props.step);
    setCurrentValue(newVal);
}

// 减少
const decrease = () => {
    if (inputNumberDisabled.value || minDisabled.value) return;
    const value = props.modelValue || 0;
    const newVal = _decrease(value, props.step);
    setCurrentValue(newVal);
}

// 设置最新值
const setCurrentValue = (newVal: any) => {
    const oldVal = currentValue.value;
    if (typeof newVal === 'number' && props.precision !== undefined) {
        newVal = toPrecision(newVal, props.precision);
    }
    if (newVal >= props.max) newVal = props.max;
    if (newVal <= props.min) newVal = props.min;
    if (oldVal === newVal) return;
    userInput.value = null;
    emit('update:model-value', newVal);
    emit('change', newVal, oldVal);
    currentValue.value = newVal;
}

// 在组件 Input 失去焦点时触发
const handleBlur = (event: any) => {
    emit('blur', event);
}
// 在组件 Input 获得焦点时触发
const handleFocus = (event: any) => {
    emit('focus', event);
}
// 绑定值被改变时触发
const handleInput = (value: any) => {
    userInput.value = value;
}
// 绑定值被改变时触发
const handleInputChange = (value: any) => {
    const newVal = value === '' ? undefined : Number(value);
    if (!isNaN(newVal as number) || value === '') {
        setCurrentValue(newVal);
    }
    userInput.value = null;
}
// 选中 input 中的文字
const select = () => {
    input.value.select();
}

// 监听
watch(() => props.modelValue,
    (value) => {
        let newVal = value === undefined ? value : Number(value);
        if (newVal !== undefined) {
            if (isNaN(newVal)) {
                return;
            }

            if (props.stepStrictly) {
                const stepPrecision = getPrecision(props.step);
                const precisionFactor = Math.pow(10, stepPrecision);
                newVal = Math.round(newVal / props.step) * precisionFactor * props.step / precisionFactor;
            }

            if (props.precision !== undefined) {
                newVal = toPrecision(newVal, props.precision);
            }
        }
        if (newVal >= props.max) newVal = props.max;
        if (newVal <= props.min) newVal = props.min;
        currentValue.value = newVal;
        userInput.value = null;
        emit('update:model-value', newVal);
    }, { immediate: true })

</script>