<!-- webview only -->
<template>
    <section :class="['b-textarea', {'auto-height': autoHeight}]" @click.capture="$emit('click', $event)" @touchstart.capture="$emit('touchstart', $event)" @touchmove.capture="$emit('touchmove', $event)" @touchend.capture="$emit('touchend', $event)" @touchcancel.capture="$emit('touchcancel', $event)">
        <section class="b-textarea-wrapper" v-if="autoHeight" :style="{height: `${contentHeight}px`}"></section>
        <span ref="placeholder" :style="placeholderStyle" v-show="false"></span>
    </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', 'lineHeight',
    '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: '',
        lineHeight: 0,
        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 ((!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 || boundingClientRect.x || boundingClientRect.left : 0;
    // useful.y = (!keys || keys.indexOf('y') >= 0) ? element.offsetTop || boundingClientRect.y || boundingClientRect.top : 0;
    let parent = element.parentNode;
    while (parent && parent !== document) {
        if (window.getComputedStyle(parent, null).display === 'none') {
            useful.display = 'none';
            break;
        }
        parent = parent.parentNode;
    }

    return useful;
};

const computedStyleChanged = (oldStyle, newStyle, autoHeight) => {
    let changed = false;
    for (let index = 0; index < styleKeys.length; index++) {
        const key = styleKeys[index];
        if (oldStyle[key] !== newStyle[key] && (!autoHeight || key !== 'height')) {
            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.lineHeight = source.lineHeight;
    styles.textAlign = source.textAlign;
    styles.opacity = source.opacity;
    styles.hidden = source.display === 'none';
    return styles;
};

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;

export default {
    name: 'BTextarea',
    props: {
        value: {
            type: String,
            default: '',
        },
        disabled: {
            type: Boolean,
            default: false,
        },
        maxlength: {
            type: Number,
            default: 140,
        },
        focus: {
            type: Boolean,
            default: false,
        },
        autoHeight: {
            type: Boolean,
            default: false,
        },
        adjustPosition: {
            type: Boolean,
            default: true,
        },
        cursorSpacing: {
            type: Number,
            default: 0,
        },
        showConfirmBar: {
            type: Boolean,
            default: true,
        },
        cursor: {
            type: Number,
            default: -1,
        },
        selectionStart: {
            type: Number,
            default: -1,
        },
        selectionEnd: {
            type: Number,
            default: -1,
        },
        placeholder: {
            type: String,
            default: '',
        },
        placeholderStyle: {
            type: String,
            default: '',
        },
    },
    data() {
        return {
            nativeValue: '',
            lastStyle: {},
            lastPlaceholderStyle: {},
            isAndroid: isAndroid,
            showContent: false,
            contentHeight: 0,
            hasBlured: false,
        };
    },
    computed: {
        focusStatus() {
            return this.focus && !this.hasBlured;
        }
    },
    watch: {
        focus(value) {
            if (value) {
                this.hasBlured = false;
            }
        }
    },
    mounted() {
        // 设置初始值
        this.nativeValue = (this.maxlength >= 0 && this.value) ? this.value.substr(0, this.maxlength) : (this.value || '');
        // 初始化不是focus，永远不是focus
        this.hasBlured = !this.focus;

        // 需要监视变更并通知NA变更的属性
        const PROPS_NEED_WATCH = [
            'value',
            'nativeValue',
            'disabled',
            'maxlength',
            'autoHeight',
            'adjustPosition',
            'cursorSpacing',
            'showConfirmBar',
            'placeholder',
            'focusStatus',
        ];

        // options列表
        const options = {
            name: 'textarea',
            id: this._uid,
            destroy: false,
            value: this.nativeValue,
            disabled: this.disabled,
            maxlength: this.maxlength,
            focus: this.focusStatus,
            autoHeight: this.autoHeight,
            adjustPosition: this.adjustPosition,
            cursorSpacing: this.cursorSpacing,
            showConfirmBar: this.showConfirmBar,
            cursor: this.cursor,
            selectionStart: this.selectionStart,
            selectionEnd: this.selectionEnd,
            placeholder: this.placeholder,
            styles: {},
        };

        // 添加watcher
        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, this.autoHeight) || 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
                };

                this.contentHeight = Math.max(options.styles.lineHeight, this.contentHeight);
                SmallApp.nativeComponent(options);
            }
        };

        listeners[this._uid] = helper;

        helper();

        window.addEventListener('resize', () => {
            helper();
        });
    },
    destroyed() {
        const options = {
            name: 'textarea',
            id: this._uid,
            destroy: true,
            styles: {},
        };
        delete listeners[this._uid];
        SmallApp.nativeComponent(options);
    },
    methods: {
        handleMessage(message) {
            if (message.type === 'native-component') {
                this.$emit(message.event.type, message.event);

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

                    case 'linechange': {
                        this.contentHeight = message.event.detail.height;
                        break;
                    }

                    case 'input': {
                        this.nativeValue = message.event.detail.value;
                        break;
                    }
                }
            }
        }
    }
};
</script>
<style>
.b-textarea {
    display: inline-block;
    font-family: sans-serif;
}
.b-textarea.auto-height {
    height: auto !important;
}
</style>
