<!-- webview only -->
<template>
    <section class="b-input" @click.capture="focusInput" @touchstart.capture="$emit('touchstart', $event)" @touchmove.capture="$emit('touchmove', $event)" @touchend.capture="$emit('touchend', $event)" @touchcancel.capture="$emit('touchcancel', $event)">
        <section :class="['b-input-wrapper', {showContent}]"><span v-show="hasValue" :class="{password: !isAndroid && password}">{{showText}}</span><span :style="placeholderStyle" v-show="!hasValue" ref="placeholder" :class="['b-input-placeholder', placeholderClass]">{{placeholder}}</span></section>
        <input type="hidden" class="b-input-hidden" :value="showText"/>
    </section>
</template>
<script>
const listeners = {};
const observer = new MutationObserver(() => {
    for (const key in listeners) {
        if (listeners.hasOwnProperty(key)) {
            const listener = listeners[key];
            if (typeof listener === 'function') {
                listener();
            }
        }
    }
});
observer.observe(document.documentElement, {
    attributes: true,
    characterData: true,
    childList: true,
    subtree: true,
});

const styleKeys = ['x', 'y', 'color', 'fontFamily', 'fontSize', 'fontWeight',
    'opacity', 'height', 'width', 'borderBottomWidth', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth',
    'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'display', 'textAlign'];
const getComputed = (element, keys) => {
    const computed = window.getComputedStyle(element, null);

    // 这个对象用来计算最终native input的样式和位置
    const useful = {
        color: '',
        fontFamily: '',
        fontSize: 0,
        fontWeight: '',
        opacity: 0,
        height: 0,
        width: 0,
        borderBottomWidth: 0,
        borderLeftWidth: 0,
        borderRightWidth: 0,
        borderTopWidth: 0,
        paddingBottom: 0,
        paddingLeft: 0,
        paddingRight: 0,
        paddingTop: 0,
        display: '',
        textAlign: '',
    };

    for (const key in useful) {
        if (useful.hasOwnProperty(key) && (!keys || keys.indexOf(key) >= 0)) {
            const value = useful[key];
            if (value === 0) {
                // 0 代表数值型 去掉px单位
                useful[key] = parseFloat(computed[key]) || 0;
            } else {
                useful[key] = computed[key];
            }
        }
    }

    let offsetParent = element.offsetParent;
    // computed里面有x,y不能通过循环赋值
    useful.x = (!keys || keys.indexOf('x') >= 0) ? element.offsetLeft : 0;
    useful.y = (!keys || keys.indexOf('y') >= 0) ? element.offsetTop : 0;
    useful.fixed = true;

    while(offsetParent) {
        if (offsetParent === document.body) {
            useful.fixed = false;
        }

        if (getComputedStyle(offsetParent, null).getPropertyValue('position') === 'fixed') {
            const offsetParentRect = offsetParent.getBoundingClientRect();
            if ((!keys || keys.indexOf('x') >= 0)) {
                useful.x += offsetParentRect.left;
            }

            if ((!keys || keys.indexOf('y') >= 0)) {
                useful.y += offsetParentRect.top;
            }
        } else {
            if ((!keys || keys.indexOf('x') >= 0)) {
                useful.x += offsetParent.offsetLeft;
            }

            if ((!keys || keys.indexOf('y') >= 0)) {
                useful.y += offsetParent.offsetTop;
            }
        }

        offsetParent = offsetParent.offsetParent;
    }
    // computed里面有x,y不能通过循环赋值
    // useful.x = (!keys || keys.indexOf('x') >= 0) ? element.offsetLeft : 0;
    // useful.y = (!keys || keys.indexOf('y') >= 0) ? element.offsetTop : 0;

    return useful;
};

const computedStyleChanged = (oldStyle, newStyle) => {
    let changed = false;
    for (let index = 0; index < styleKeys.length; index++) {
        const key = styleKeys[index];
        if (oldStyle[key] !== newStyle[key]) {
            changed = true;
            break;
        }
    }
    return changed;
};

const getOptionStyles = (source) => {
    const styles = {};
    styles.fixed = source.fixed;
    styles.x = source.x;
    styles.y = source.y;
    styles.width = source.width;
    styles.height = source.height;
    styles.left = source.borderLeftWidth + source.paddingLeft;
    styles.top = source.borderTopWidth + source.paddingTop;
    styles.right = source.borderRightWidth + source.paddingRight;
    styles.bottom = source.borderBottomWidth + source.paddingBottom;
    styles.color = toHEXColor(source.color);
    styles.fontFamily = source.fontFamily;
    styles.fontSize = source.fontSize;
    styles.fontWeight = source.fontWeight;
    styles.textAlign = source.textAlign;
    styles.opacity = source.opacity;
    styles.hidden = source.display === 'none';
    return styles;
};

const parseDataset = function (dataset) {
    let result = {};
    for (let key in dataset) {
        try {
            result[key] = JSON.parse(dataset[key]);
        } catch (e) {
            result[key] = dataset[key];
        }
    }
    return result;
};

const isRGBReg = /^rgb/i;
const rgbReg = /\d+/gi;
const toHEXColor = (color) => {
    if (isRGBReg.test(color)) {
        const [r, g, b] = color.match(rgbReg);
        return '#' + toHEX(r) + toHEX(g) + toHEX(b);
    }

    return color;
};
const toHEX = (number) => {
    const str = '0123456789ABCDEF';
    let temp = '';
    let cur = number % 16;
    temp = str.substr(cur, 1);
    cur = (number - cur) / 16;
    return str.substr(cur, 1) + temp;
};

const isiOS = /iPad|iPhone|iPod/i.test(navigator.userAgent);
const isAndroid = !isiOS;
const pwdText = isiOS ? '•' : '*';

export default {
    name: 'BInput',
    props: {
        value: {
            type: String,
            default: '',
        },
        type: {
            type: String,
            default: 'text',
        },
        password: {
            type: Boolean,
            default: false,
        },
        placeholder: {
            type: String,
            default: '',
        },
        placeholderStyle: {
            type: String,
            default: '',
        },
        placeholderClass: {
            type: String,
            default: 'input-placeholder'
        },
        disabled: {
            type: Boolean,
            default: false,
        },
        maxlength: {
            type: Number,
            default: 140,
        },
        cursorSpacing: {
            type: Number,
            default: 0,
        },
        focus: {
            type: Boolean,
            default: false,
        },
        confirmType: {
            type: String,
            default: 'done',
        },
        confirmHold: {
            type: Boolean,
            default: false,
        },
        cursor: {
            type: Number,
            default: -1,
        },
        selectionStart: {
            type: Number,
            default: -1,
        },
        selectionEnd: {
            type: Number,
            default: -1,
        },
        adjustPosition: {
            type: Boolean,
            default: true,
        }
    },
    data() {
        return {
            nativeValue: '',
            lastStyle: {},
            lastPlaceholderStyle: {},
            isAndroid: isAndroid,
            showContent: false,
            hasBlured: false,
        };
    },
    computed: {
        focusStatus() {
            return this.focus && !this.hasBlured;
        },
        hasValue() {
            return this.nativeValue.length > 0;
        },
        showText() {
            let result = '';
            if (this.password) {
                for(let i = 0, max = this.nativeValue.length; i < max; i++) {
                    result += pwdText;
                }

                return result;
            }

            return this.nativeValue;
        }
    },
    watch: {
        focus(value) {
            if (value) {
                this.hasBlured = false;
            }
        }
    },
    mounted() {
        this.nativeValue = (this.maxlength >= 0 && this.value) ? this.value.substr(0, this.maxlength) : (this.value || '');
        this.showContent = !this.focus;
        this.hasBlured = !this.focus;

        const PROPS_NEED_WATCH = [
            'value',
            'type',
            'password',
            'disabled',
            'maxlength',
            'adjustPosition',
            'cursorSpacing',
            'confirmType',
            'confirmHold',
            'placeholder',
            'focusStatus',
        ];

        const options = {
            name: 'input',
            id: this._uid,
            destroy: false,
            show: this.focusStatus,
            value: this.nativeValue,
            type: this.type,
            password: this.password,
            disabled: this.disabled,
            maxlength: this.maxlength,
            focus: this.focusStatus,
            adjustPosition: this.adjustPosition,
            cursorSpacing: this.cursorSpacing,
            confirmType: this.confirmType,
            confirmHold: this.confirmHold,
            cursor: this.cursor,
            selectionStart: this.selectionStart,
            selectionEnd: this.selectionEnd,
            placeholder: this.placeholder,
            styles: {},
        };

        PROPS_NEED_WATCH.forEach(prop => {
            this.$watch(prop, (value) => {
                options.value = this.nativeValue;

                if (prop === 'value' && this.value === this.nativeValue) return;

                switch(prop) {
                    case 'nativeValue': {
                        return;
                    }

                    case 'focusStatus': {
                        options.show = value;
                        options.focus = value;
                        break;
                    }

                    case 'value': {
                        if (!this.value) {
                            this.nativeValue = '';
                            return;
                        }
                    }

                    case 'maxlength': {
                        this.nativeValue = (this.maxlength >= 0 && this.value) ? this.value.substr(0, this.maxlength) : this.value;
                        options.value = this.nativeValue;
                        break;
                    }

                    default: {
                        options[prop] = value;
                    }
                }
                SmallApp.nativeComponent(options);
            });
        });

        const helper = () => {
            const newStyle = getComputed(this.$el);
            const newPlaceholderStyle = getComputed(this.$refs.placeholder, ['color']);
            if (computedStyleChanged(this.lastStyle, newStyle) || computedStyleChanged(this.lastPlaceholderStyle, newPlaceholderStyle)) {
                options.value = this.nativeValue;

                this.lastStyle = newStyle;
                options.styles = getOptionStyles(newStyle);

                this.lastPlaceholderStyle = newPlaceholderStyle;
                const { color } = getOptionStyles(newPlaceholderStyle);
                options.placeholderStyle = {
                    color,
                    fontSize: newStyle.fontSize
                };
                SmallApp.nativeComponent(options);
            }
        };

        listeners[this._uid] = helper;


        helper();

        window.addEventListener('resize', () => {
            helper();
        });
    },
    destroyed() {
        const options = {
            name: 'input',
            id: this._uid,
            destroy: true,
            show: false,
            styles: {},
        };
        delete listeners[this._uid];
        SmallApp.nativeComponent(options);
    },
    methods: {
        handleMessage(message) {
            if (message.type === 'native-component') {
                const elementInfo = {id: this.$el.id, dataset: parseDataset(this.$el.dataset)};
                this.$emit(message.event.type, Object.assign({target: elementInfo, currentTarget: elementInfo}, message.event));

                // 处理安卓的blur/focus状态是否显示内容
                switch (message.event.type) {
                    case 'focus': {
                        this.showContent = false;
                        break;
                    }

                    case 'blur': {
                        this.hasBlured = true;
                        this.showContent = true;
                        break;
                    }

                    case 'input': {
                        this.nativeValue = message.event.detail.value;
                        break;
                    }
                }
            }
        },
        focusInput(e) {
            this.$emit('click', e);
            if (!this.disabled) {
                const options = {
                    name: 'input',
                    id: this._uid,
                    destroy: false,
                    show: true,
                    value: this.nativeValue,
                    type: this.type,
                    password: this.password,
                    disabled: this.disabled,
                    maxlength: this.maxlength,
                    focus: this.focusStatus,
                    adjustPosition: this.adjustPosition,
                    cursorSpacing: this.cursorSpacing,
                    confirmType: this.confirmType,
                    confirmHold: this.confirmHold,
                    cursor: this.cursor,
                    selectionStart: this.selectionStart,
                    selectionEnd: this.selectionEnd,
                    placeholder: this.placeholder,
                    styles: {},
                };

                const newStyle = getComputed(this.$el);
                options.styles = getOptionStyles(newStyle);

                const newPlaceholderStyle = getComputed(this.$refs.placeholder, ['color']);
                const { color } = getOptionStyles(newPlaceholderStyle);
                options.placeholderStyle = {
                    color,
                    fontSize: newStyle.fontSize
                };
                SmallApp.nativeComponent(options);
            }
        }
    }
};
</script>
<style>
.b-input {
    cursor:auto;
    display:block;
    height:1.4rem;
    text-overflow:clip;
    overflow:hidden;
    white-space:nowrap;
    min-height:1.4rem;
    position: relative;
}
.b-input .b-input-wrapper {
    width: 100%;
    /* height: 100%; */
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    position: relative;
    top: 50%;
    transform: translateY(-50%);
    visibility: hidden;
}
.b-input .b-input-wrapper.showContent {
    visibility: visible;
}
.b-input .b-input-wrapper span{
    line-height: 1;
}
.b-input .b-input-wrapper .password {
    font-size: 1.8em;
}
.b-input-placeholder {
    color: #808080;
    font-size: inherit !important;
}
</style>
