<!-- webview only -->
<template>
    <section
        @click.capture="focusInput"
        @touchcancel.capture="$emit('touchcancel', $event)"
        @touchend.capture="$emit('touchend', $event)"
        @touchmove.capture="$emit('touchmove', $event)"
        @touchstart.capture="$emit('touchstart', $event)"
        class="b-input"
    >
        <section :class="['b-input-wrapper', {showContent}]">
            <span :class="{password: !isAndroid && password}" v-show="hasValue">{{showText}}</span>
            <span :class="['b-input-placeholder', placeholderClass]" :style="placeholderStyle" ref="placeholder" v-show="!hasValue">{{placeholder}}</span>
        </section>
        <input :value="showText" class="b-input-hidden" type="hidden">
    </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: '',
        fontStyle: '',
        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];
            }
        }
    }

    const rect = element.getBoundingClientRect();
    useful.y = rect.top;
    useful.x = rect.left;
    useful.fixed = true;

    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 isiOS = /iPad|iPhone|iPod/i.test(navigator.userAgent);
const isAndroid = !isiOS;
const pwdText = isiOS ? '•' : '*';
var isBeforeiOS153 = false;
var ios152153 = false;

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;

    // 兼容ios1.5.3及之前的版本将top取成right的bug
    if (isBeforeiOS153) {
        styles.right = source.borderTopWidth + source.paddingTop + 1;
    }
    else {
        styles.right = source.borderRightWidth + source.paddingRight;
    }

    styles.top = source.borderTopWidth + source.paddingTop;
    styles.top = isiOS ? styles.top + 1 : styles.top;
    styles.bottom = source.borderBottomWidth + source.paddingBottom;
    styles.color = toHEXColor(source.color);
    styles.fontFamily = source.fontFamily;
    styles.fontSize = source.fontSize;
    styles.fontWeight = source.fontWeight;
    styles.fontStyle = source.fontStyle;
    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;
};

export default {
    name: 'Bili_Input',
    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: true,
            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;
                this.showContent = false;
            }
        },
    },
    mounted() {
        if (isiOS) {
            try {
                var systemInfo = JSON.parse(SmallApp._callNative('getSystemInfoSync', '', '')).data;
                if (systemInfo.SDKVersion.indexOf('1.5') > -1) {
                    isBeforeiOS153 = true;
                    ios152153 = true;
                }
            } catch (error) {
                isBeforeiOS153 = true;
            }
        }

        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.showContent,
            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;
                    options.selectionStart = +this.selectionStart;
                    options.selectionEnd = +this.selectionEnd;
                    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;
                }
                }
                if (options.show) {
                    SmallApp.nativeComponent(options);
                }
            });
        });

        const helper = () => {
            setTimeout(() => {
                // 判断，如果this.$refs.placeholder不存在，意味着子组件已经销毁完毕
                if (this && this.$el && this.$refs && this.$refs.placeholder) {
                  const newStyle = getComputed(this.$el);
                  // placeholder客户端只care：color和fontSize，其他样式无需参与对比。
                  const _newPlaceholderStyle = getComputed(this.$refs.placeholder, ['color', 'fontSize'])
                  const { color, fontSize } = getOptionStyles(_newPlaceholderStyle);
                  const newPlaceholderStyle = {
                    color,
                    fontSize
                  };
                  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, fontSize, fontWeight, fontFamily, fontStyle } = getOptionStyles(newPlaceholderStyle);
                    options.placeholderStyle = {
                      color,
                      fontSize
                    };
                    options.show = !this.showContent;

                    // iOS旧版本内存泄漏问题
                    if (options.show) {
                      if (options.styles.width != 0) {
                        SmallApp.nativeComponent(options);
                      }
                    }
                  }
                }
            }, 80);
        };

        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;
                    // iOS旧版本内存泄漏问题
                    isBeforeiOS153 &&
                            SmallApp.nativeComponent({
                                name: 'input',
                                id: this._uid,
                                destroy: true,
                                show: false,
                                styles: {},
                            });
                    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', 'fontSize']);
                const { color, fontSize, fontWeight, fontFamily, fontStyle } = getOptionStyles(newPlaceholderStyle);
                options.placeholderStyle = {
                    color,
                    fontSize
                };

                if (ios152153) {
                    this.showContent = false;
                }
                setTimeout(() => {
                    SmallApp.nativeComponent(options);
                }, 0);
            }
        },
    },
};
</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>
