<template>
    <div :class="wrapClasses">
        <div :class="inputWrapClasses">
            <div 
                v-if="showHandle"
                :class="handlerClasses">
                <a
                    @mousedown="handleTouchStart($event, 'up')" 
                    @mouseup="handleTouchEnd($event, 'up')"
                    @click="handleUp"
                    :class="upClasses">
                    <span :class="innerUpClasses" @click="handlePreventDefault"></span>
                </a>
                <a
                    @mousedown="handleTouchStart($event, 'down')" 
                    @mouseup="handleTouchEnd($event, 'down')"
                    @click="handleDown"
                    :class="downClasses">
                    <span :class="innerDownClasses" @click="handlePreventDefault"></span>
                </a>
            </div>
            <input
                :class="inputClasses"
                :disabled="disabled"
                :readonly="readonly || !editable"
                :name="name"
                :value="formatterValue"
                :placeholder="placeholder"
                :autofocus="autofocus"
                autocomplete="off"
                spellcheck="false"
                @focus="handleFocus"
                @blur="handleBlur"
                @keydown.stop="handleKeydown"
                @keyup.enter="handleEnter"
                @input="handleChange"
                @change="handleChange"
                @mouseup="handlePreventDefault">
        </div>
    </div>
</template>
<script>
    import Emitter from '../../../mixins/emitter'
    import { oneOf, findComponentUpward } from '../../../utils/assist'

    function addNum(num, step) {
        let numValue, stepValue
        try {
            numValue = num.toString().split('.')[1].length 
        } catch (e) {
            numValue = 0
        }
        try {
            stepValue = step.toString().split('.')[1].length 
        } catch (e) {
            stepValue = 0
        }

        let m = Math.pow(10, Math.max(numValue, stepValue))
        return (Math.round(num * m) + Math.round(step * m)) / m
    }

    const prefixCls = 'yy-input-number'
    const iconPrefixCls = 'yy-icon-153'
    export default {
        name: 'YYInputNumber',
        mixins: [ Emitter ],
        props: {
            max: {
                type: Number,
                default: Infinity
            },
            min: {
                type: Number,
                default: 0
            },
            step: {
                type: Number,
                default: 1
            },
            activeChange: {
                type: Boolean,
                default: true
            },
            value: {
                type: Number,
                default: 1
            },
            size: {
                validator(value) {
                    return oneOf(value, ['small', 'large'])
                }
            },
            disabled: {
                type: Boolean,
                default: false
            },
            autofocus: {
                type: Boolean,
                default: false
            },
            readonly: {
                type: Boolean,
                default: false
            },
            editable: {
                type: Boolean,
                default: true
            },
            name: {
                type: String
            },
            precision: {
                type: Number
            },
            formatter: {
                type: Function
            },
            parser: {
                type: Function
            },
            placeholder: {
                type: String,
                default: ''
            },
            showHandle: {
                type: Boolean,
                default: true
            }
        },
        data() {
            return {
                focused: false,
                upDisabled: false,
                downDisabled: false,
                currentValue: this.value,
                timeoutEvent: 0,
                intervalEvent: 0
            }
        },
        computed: {
            wrapClasses() {
                return [
                    `${prefixCls}`,
                    {
                        [`${prefixCls}-${this.size}`]: !!this.size,
                        [`${prefixCls}-disabled`]: this.disabled,
                        [`${prefixCls}-focused`]: this.focused
                    }
                ]
            },
            handlerClasses() {
                return `${prefixCls}-handler-wrap`
            },
            upClasses() {
                return [
                    `${prefixCls}-handler`,
                    `${prefixCls}-handler-up`,
                    {
                        [`${prefixCls}-handler-up-disabled`]: this.upDisabled
                    }
                ]
            },
            innerUpClasses() {
                return `${prefixCls}-handler-up-inner ${iconPrefixCls} ${iconPrefixCls}-arrow-up`
            },
            downClasses() {
                return [
                    `${prefixCls}-handler`,
                    `${prefixCls}-handler-down`,
                    {
                        [`${prefixCls}-handler-down-disabled`]: this.downDisabled
                    }
                ]
            },
            innerDownClasses() {
                return `${prefixCls}-handler-down-inner ${iconPrefixCls} ${iconPrefixCls}-arrow-down`
            },
            inputWrapClasses() {
                return `${prefixCls}-input-wrap`
            },
            inputClasses() {
                return `${prefixCls}-input`
            },
            precisionValue() {
                // can not display 1.0
                if (!this.currentValue) return this.currentValue
                return this.precision ? this.currentValue.toFixed(this.precision) : this.currentValue
            },
            formatterValue() {
                if (this.formatter && this.precisionValue !== null) {
                    return this.formatter(this.precisionValue)
                } else {
                    return this.precisionValue
                }
            }
        },
        watch: {
            value(val) {
                this.currentValue = val
            },
            currentValue(val) {
                this.handleChangeValue(val)
            },
            min() {
                this.handleChangeValue(this.currentValue)
            },
            max() {
                this.handleChangeValue(this.currentValue)
            }
        },
        methods: {
            handlePreventDefault(event) {
                event.preventDefault()
            },
            handleUp(event) {
                const targetValue = Number(event.target.value)
                if (this.upDisabled && isNaN(targetValue)) {
                    return false
                }
                this.changeStep('up', event)
            },
            handleDown(event) {
                const targetValue = Number(event.target.value)
                if (this.downDisabled && isNaN(targetValue)) {
                    return false
                }
                this.changeStep('down', event)
            },
            changeStep(type, event) {
                if (this.disabled || this.readonly) {
                    return false
                }

                const targetValue = Number(event.target.value)
                let value = Number(this.currentValue)
                const step = Number(this.step)
                if (isNaN(value)) {
                    return false
                }

                if (!isNaN(targetValue)) {
                    if (type === 'up') {
                        if (addNum(targetValue, step) <= this.max) {
                            value = targetValue
                        } else {
                            return false
                        }
                    } else if (type === 'down') {
                        if (addNum(targetValue, -step) >= this.min) {
                            value = targetValue
                        } else {
                            return false
                        }
                    }
                }

                if (type === 'up') {
                    value = addNum(value, step)
                } else if (type === 'down') {
                    value = addNum(value, -step)
                }
                this.setValue(value)
            },
            
            handleFocus(event) {
                this.focused = true
                this.$emit('on-focus', event)
            },
            handleBlur() {
                this.focused = false
                this.$emit('on-blur')
                this.dispatch('YYFormItem', 'on-form-blur', this.currentValue)
            },
            handleKeydown(event) {
                if (event.keyCode === 38 || event.keyCode === 40) {
                    this.handlePreventDefault(event)
                    this[event.keyCode === 38 ? `handleUp` : `handleDown`](event)
                } 
            },
            handleEnter(event) {
                this.$emit('on-enter', event)
            },
            handleTouchStart(event, type) {
                this.timeoutEvent = setTimeout(() => {
                    this.handleLongTouch(event, type)
                }, 500)
                return false
            },
            handleTouchEnd(event, type) {
                clearTimeout(this.timeoutEvent)
                this.timeoutEvent = 0
                clearInterval(this.intervalEvent)
                this.intervalEvent = 0
            },
            handleLongTouch(event, type) {
                this.intervalEvent = setInterval(()=>{
                    this[type === 'up' ? `handleUp` : `handleDown`](event)
                }, 50)
            },
            handleChange(event) {
                if (event.type == 'input' && !this.activeChange) return
                let value = event.target.value.trim()
                if (this.parser) {
                    value = this.parser(value)
                }

                const isEmptyString = value.length === 0
                if (isEmptyString) {
                    this.setValue(null)
                    return
                }
                if (event.type == 'input' && value.match(/^\-?\.?$|\.$/)) return 

                value = Number(value)

                if (!isNaN(value)) {
                    this.currentValue = value
                    this.setValue(value)
                } else {
                    event.target.value = this.currentValue
                }
            },
            handleChangeValue(value) {
                value = Number(value)

                const step = this.step
                this.upDisabled = !isNaN(value) ? value + step > this.max : true
                this.downDisabled = !isNaN(value) ? value - step < this.min : true
            },
            setValue(value) {
                if (value && !isNaN(this.precision)) value = Number(Number(value).toFixed(this.precision))

                const { min, max } = this
                if (value !== null) {
                    if (value > max) {
                        value = max
                    } else if (value < min) {
                        value = min
                    }
                }

                this.$nextTick(() => {
                    this.currentValue = value
                    this.$emit('input', value)
                    this.$emit('on-change', value)
                    this.dispatch('YYFormItem', 'on-form-change', value)
                })
            },
        },
        mounted() {
            this.handleChangeValue(this.currentValue)
        }
    }
</script>
