<template>
    <div class="im-m-picker">
        <div class="toolbar" v-if="showToolbar">
            <button type="button" class="cancel" @click="cancel">
                {{ cancelButtonText }}
            </button>
            <template v-if="$slots.title">
                <slot name="title"></slot>
            </template>
            <template v-else>
                <div class="ellipsis title">{{ title }}</div>
            </template>
            <button type="button" class="confirm" @click="confirm">
                {{ confirmButtonText }}
            </button>
        </div>
        <div class="columns" :style="columnsStyle" @touchmove.prevent>
            <picker-column
                v-for="(item, columnIndex) in formattedColumns"
                :key="columnIndex"
                :valueKey="valueKey"
                :allowHtml="allowHtml"
                :className="item.className"
                :itemHeight="itemPxHeight"
                :defaultIndex="item.defaultIndex || +defaultIndex"
                :swipeDuration="swipeDuration"
                :visibleItemCount="visibleItemCount"
                :initialOptions="item.values"
                @change="onChange(columnIndex)"
            />
            <div class="mask" :style="maskStyle" />
            <!-- BORDER_UNSET_TOP_BOTTOM -->
            <div class="frame" :style="frameStyle" />
        </div>
    </div>
</template>
<script>
import PickerColumn from './picker-column.vue';
const DEFAULT_ITEM_HEIGHT = 44;
export default {
    name: 'im-m-picker',
    components: { 'picker-column': PickerColumn },
    emits: ['change', 'confirm', 'cancel'],
    props: {
        title: String,
        loading: Boolean,
        itemHeight: [Number, String],
        showToolbar: Boolean,
        cancelButtonText: {
            type: String,
            default: 'cancel'
        },
        confirmButtonText: {
            type: String,
            default: 'confirm'
        },
        allowHtml: {
            type: Boolean,
            default: true
        },
        visibleItemCount: {
            type: [Number, String],
            default: 6
        },
        swipeDuration: {
            type: [Number, String],
            default: 1000
        },
        defaultIndex: {
            type: [Number, String],
            default: 0
        },
        columns: {
            type: Array,
            default: () => []
        },
        toolbarPosition: {
            type: String,
            default: 'top'
        },
        valueKey: {
            type: String,
            default: 'text'
        }
    },
    data: function() {
        return {
            children: [],
            formattedColumns: []
        };
    },

    computed: {
        itemPxHeight() {
            return this.itemHeight || DEFAULT_ITEM_HEIGHT;
        },
        columnsStyle() {
            const wrapHeight = this.itemPxHeight * this.visibleItemCount;
            return { height: `${wrapHeight}px` };
        },
        maskStyle() {
            const wrapHeight = this.itemPxHeight * this.visibleItemCount;
            return {
                backgroundSize: `100% ${(wrapHeight - this.itemPxHeight) / 2}px`
            };
        },
        frameStyle() {
            return { height: `${this.itemPxHeight}px` };
        },
        dataType() {
            const { columns } = this;
            const firstColumn = columns[0] || {};

            if (firstColumn.children) {
                return 'cascade';
            }

            if (firstColumn.values) {
                return 'object';
            }

            return 'text';
        }
    },
    watch: {
        columns: {
            handler: 'format',
            immediate: true
        }
    },
    methods: {
        cancel() {
            this.$emit('cancel');
        },
        confirm() {
            this.children.forEach(child => child.stopMomentum());
            this.emit('confirm');
        },
        onCascadeChange(columnIndex) {
            let cursor = { children: this.columns };
            const indexes = this.getIndexes();

            for (let i = 0; i <= columnIndex; i++) {
                cursor = cursor.children[indexes[i]];
            }

            while (cursor && cursor.children) {
                columnIndex++;
                this.setColumnValues(columnIndex, cursor.children);
                cursor = cursor.children[cursor.defaultIndex || 0];
            }
        },
        onChange(columnIndex) {
            if (this.dataType === 'cascade') {
                this.onCascadeChange(columnIndex);
            }

            if (this.dataType === 'text') {
                this.$emit(
                    'change',
                    this,
                    this.getColumnValue(0),
                    this.getColumnIndex(0)
                );
            } else {
                let values = this.getValues();
                if (this.dataType === 'cascade') {
                    values = values.map(item => item[this.valueKey]);
                }

                this.$emit('change', this, values, columnIndex);
            }
        },
        emit(event) {
            if (this.dataType === 'text') {
                this.$emit(
                    event,
                    this.getColumnValue(0),
                    this.getColumnIndex(0)
                );
            } else {
                let values = this.getValues();
                if (this.dataType === 'cascade') {
                    values = values.map(item => item[this.valueKey]);
                }

                this.$emit(event, values, this.getIndexes());
            }
        },
        // @exposed-api
        getIndexes() {
            return this.children.map(child => child.currentIndex);
        },
        // @exposed-api
        setColumnValue(index, value) {
            const column = this.getColumn(index);
            if (column) {
                column.setValue(value);

                if (this.dataType === 'cascade') {
                    this.onCascadeChange(index);
                }
            }
        },
        // @exposed-api
        setColumnValues(index, options) {
            const column = this.children[index];

            if (column) {
                column.setOptions(options);
            }
        },
        // @exposed-api
        getColumnValue(index) {
            const column = this.getColumn(index);
            return column && column.getValue();
        },
        getColumn(index) {
            return this.children[index];
        },
        // @exposed-api
        getColumnIndex(columnIndex) {
            return (this.getColumn(columnIndex) || {}).currentIndex;
        },
        // @exposed-api
        setIndexes(indexes) {
            indexes.forEach((optionIndex, columnIndex) => {
                this.setColumnIndex(columnIndex, optionIndex);
            });
        },
        // @exposed-api
        setColumnIndex(columnIndex, optionIndex) {
            const column = this.getColumn(columnIndex);

            if (column) {
                column.setIndex(optionIndex);

                if (this.dataType === 'cascade') {
                    this.onCascadeChange(columnIndex);
                }
            }
        },
        // @exposed-api
        getColumnValues(index) {
            return (this.children[index] || {}).options;
        },
        // @exposed-api
        getValues() {
            return this.children.map(child => child.getValue());
        },
        // @exposed-api
        setValues(values) {
            values.forEach((value, index) => {
                this.setColumnValue(index, value);
            });
        },
        format() {
            const { columns, dataType } = this;

            if (dataType === 'text') {
                this.formattedColumns = [{ values: columns }];
            } else if (dataType === 'cascade') {
                this.formatCascade();
            } else {
                this.formattedColumns = columns;
            }
        },
        formatCascade() {
            const formatted = [];

            let cursor = { children: this.columns };

            while (cursor && cursor.children) {
                const defaultIndex = cursor.defaultIndex
                    ? cursor.defaultIndex
                    : +this.defaultIndex;

                formatted.push({
                    values: cursor.children,
                    className: cursor.className,
                    defaultIndex
                });

                cursor = cursor.children[defaultIndex];
            }

            this.formattedColumns = formatted;
        }
    }
};
</script>

<style lang="scss">
$picker-background-color: #fff;
$picker-toolbar-height: 44px;
$picker-title-font-size: 16px;
$picker-title-line-height: 20px;
$picker-action-padding: 0 8px;
$picker-action-font-size: 14px;
$picker-confirm-action-color: #576b95;
$picker-cancel-action-color: #969799;
$picker-option-disabled-opacity: 0.3;
$picker-loading-icon-color: #1989fa;
$picker-loading-mask-color: rgba(255, 255, 255, 0.9);
$active-opacity: 0.7;
$font-weight-bold: 500;
.im-m-picker {
    .toolbar {
        display: flex;
        align-items: center;
        justify-content: space-between;
        height: $picker-toolbar-height;
    }
    .cancel,
    .confirm {
        height: 100%;
        padding: $picker-action-padding;
        font-size: $picker-action-font-size;
        background-color: transparent;
        border: none;
        cursor: pointer;

        &:active {
            opacity: $active-opacity;
        }
    }

    .confirm {
        color: $picker-confirm-action-color;
    }

    .cancel {
        color: $picker-cancel-action-color;
    }
    .title {
        max-width: 50%;
        font-weight: $font-weight-bold;
        font-size: $picker-title-font-size;
        line-height: $picker-title-line-height;
        text-align: center;
    }
    .ellipsis {
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }
    .loading {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: 2;
        display: flex;
        align-items: center;
        justify-content: center;
        color: $picker-loading-icon-color;
        background-color: $picker-loading-mask-color;
    }

    .frame {
        position: absolute;
        top: 50%;
        right: 8px;
        left: 8px;
        z-index: 3;
        transform: translateY(-50%);
        pointer-events: none;
        &::after {
            position: absolute;
            box-sizing: border-box;
            content: ' ';
            pointer-events: none;
            top: -50%;
            bottom: -50%;
            left: -50%;
            right: -50%;
            transform: scale(0.5);
            border-top: 1px solid #ebedf0;
            border-bottom: 1px solid #ebedf0;
        }
    }

    .mask {
        position: absolute;
        top: 0;
        left: 0;
        z-index: 2;
        width: 100%;
        height: 100%;
        background-image: linear-gradient(
                180deg,
                hsla(0, 0%, 100%, 0.9),
                hsla(0, 0%, 100%, 0.4)
            ),
            linear-gradient(
                0deg,
                hsla(0, 0%, 100%, 0.9),
                hsla(0, 0%, 100%, 0.4)
            );
        background-repeat: no-repeat;
        background-position: top, bottom;
        backface-visibility: hidden;
        pointer-events: none;
    }
    .columns {
        position: relative;
        display: flex;
        cursor: grab;
    }
}
</style>
