<template>
    <div class="m-picker" @click="showPicker">
        <slot></slot>
        <transition name="fade">
            <div class="mask" v-if="visible" @click.stop="cancel"></div>
        </transition>
        <transition name="slide">
            <div class="bl-picker" v-if="visible" @click.stop>
                <div class="picker-title">
                    <span class="pt-cancel" @click="cancel">取消</span>
                    <span class="pt-submit" @click="confirm">确定</span>
                </div>
                <div class="picker-panel">
                    <div class="picker-mask-top"></div>
                    <div class="picker-mask-bottom"></div>
                    <div class="picker-wheel-wrapper" ref="wheelWrapper">
                        <div class="picker-wheel" v-for="(wheel, index0) in pickerData" :key="index0">
                            <ul class="wheel-scroll">
                                <li class="wheel-item" v-for="(item, index1) in wheel" :key="index1" :class="{'wheel-disabled-item': getDisabledItem(index0, index1)}">{{info.mode === 'region' ? item.value : item}}</li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </transition>
    </div>
</template>
<script>
import BScroll from 'better-scroll';
import AREA from './area';

export default {
    data() {
        return {
            visible: false,
            info: {},
            pickerData: [],
            wheels: [],
            newValue: [],
            originValue: []
            // dataChange: false
        };
    },
    props: {
        mode: {
            type: String,
            default: 'selector',
            validator(value) {
                return ['selector', 'multiSelector', 'time', 'date', 'region'].indexOf(value) >= 0;
            }
        },
        range: {
            type: Array,
            default() {
                return []
            }
        },
        rangeKey: {
            type: String,
            default: '',
        },
        value: {
            type: [Number, Array],
            default: 0,
        },
        start: {
            type: String,
            default: '',
        },
        end: {
            type: String,
            default: '',
        },
        fields: {
            type: String,
            default: 'day',
        },
        customItem: {
            type: String,
            default: '全部'
        },
        disabled: {
            type: Boolean,
            default: false,
        }
    },
    methods: {
        showPicker(e) {
            if (this.disabled) return;

            this.$emit('click', e);

            this.show(this.$props);
        },
        getDisabledItem(index0, index1) {
            let disabled = false;
            let start = [], end = [], minutes = 0;

            let startTime = 0, endTime = 0, itemTime = 0;

            switch (this.info.mode) {
                case 'time':
                    start = (this.info.start || '00:00').split(':').map(ele => parseInt(ele, 10));
                    end = (this.info.end || '23:59').split(':').map(ele => parseInt(ele, 10));
                    minutes = index0 === 0 ? index1 * 60 + this.newValue[1] : this.newValue[0] * 60 + index1;
                    disabled = minutes < (start[0] * 60 + start[1] || minutes > (end[0] * 60 + end[1]));
                    break;
                case 'date':
                    start = (this.info.start || '1900-01-01').split('-').map(ele => parseInt(ele, 10));
                    end = (this.info.end || '2100-12-31').split('-').map(ele => parseInt(ele, 10));
                    startTime = new Date(start[0], (start[1] || 1) - 1, start[2] || 1).getTime();
                    endTime = new Date(end[0], (end[1] || 1) - 1, end[2] || 1).getTime();
                    if (index0 == 0) { // YYYY
                        itemTime = new Date(index1 + 1900, this.newValue[1] || 0, (this.newValue[2] || 0) + 1).getTime();
                    } else if (index0 == 1) { // mm
                        itemTime = new Date(this.newValue[0] + 1900, index1, (this.newValue[2] || 0) + 1).getTime();
                    } else { // day
                        itemTime = new Date(this.newValue[0] + 1900, this.newValue[1], index1 + 1).getTime();
                    }
                    disabled = itemTime < startTime || itemTime > endTime;

                    break;
                default:
                    break;
            }
            return disabled;
        },
        setPickerData() {
            switch (this.info.mode) {
                case 'selector':
                    this.pickerData = [this.info.range];
                    this.newValue = [this.info.value];
                    break;
                case 'multiSelector':
                    this.pickerData = this.info.range;
                    this.newValue = this.info.value;
                    break;
                case 'time':
                    this.initTime();
                    break;
                case 'date':
                    this.initDate();
                    break;
                case 'region':
                    this.initRegion();
                    break;
                default:
                    break;
            }
            this.originValue = [...this.newValue];
        },
        initTime() {
            let startPicker = [], endPicker = [], minutes = 0;

            let time = (this.info.value || '00:00').split(':').map(ele => parseInt(ele, 10)),
                start = (this.info.start || '00:00').split(':').map(ele => parseInt(ele, 10)),
                end = (this.info.end || '23:59').split(':').map(ele => parseInt(ele, 10));

            minutes = time[0] * 60 + time[1];

            for (let i = 0; i < 24; i ++) {
                startPicker.push(i < 10 ? `0${i}` : i);
            }
            for (let i = 0; i < 60; i ++) {
                endPicker.push(i < 10 ? `0${i}` : i);
            }
            this.pickerData = [startPicker, endPicker];

            if (minutes < (start[0] * 60 + start[1])) {
                this.newValue = [start[0], start[1]];
            } else if (minutes > (end[0] * 60 + end[1])) {
                this.newValue = [end[0], end[1]];
            } else {
                this.newValue = [time[0], time[1]];
            }
        },
        generateDayPicker(year, month) {
            let startDate = new Date(year, month, 1),
                endDate = new Date(year, month + 1, 1),
                days = (endDate - startDate) / (1000 * 60 * 60 * 24);

            let dayPicker = [];
            for (let i = 1; i <= days; i++) {
                dayPicker.push( `${i < 10 ? '0' : ''}${i}日`);
            }
            return dayPicker;
        },
        initDate() {
            let now = new Date(),
                nowYear = now.getFullYear(),
                start = (this.info.start || '1900-01-01').split('-').map(ele => parseInt(ele, 10)),
                end = (this.info.end || '2100-12-31').split('-').map(ele => parseInt(ele, 10)),
                date = (this.info.value || `${nowYear}-${now.getMonth() + 1}-${now.getDate()}`).split('-').map(ele => parseInt(ele, 10)),
                startTime = new Date(start[0], (start[1] || 1) - 1, start[2] || 1).getTime(),
                endTime = new Date(end[0], (end[1] || 1) - 1, end[2] || 1).getTime(),
                dateTime = new Date(date[0], (date[1] || 1) - 1, date[2] || 1);

            const generateYearPicker = () => {
                let yearPicker = [];
                for (let i = 1900; i <= 2100; i++) {
                    yearPicker.push(`${i}年`);
                }
                return yearPicker;
            };
            const generateMonthPicker = () => {
                let yearPicker = [];
                for (let i = 1; i <= 12; i++) {
                    yearPicker.push(`${i < 10 ? '0' : ''}${i}月`);
                }
                return yearPicker;
            };

            let value = [];
            if (dateTime < startTime) {
                value = [start[0] - 1900, (start[1] || 1) - 1, (start[2] || 1) - 1];
            } else if (dateTime > endTime) {
                value = [end[0] - 1900, (end[1] || 1) - 1, (end[2] || 1) - 1];
            } else {
                value = [date[0] - 1900, (date[1] || 1) - 1, (date[2] || 1) - 1];
            }

            switch(this.info.fields) {
                case 'year':
                    this.pickerData = [generateYearPicker()];

                    this.newValue = [value[0]];
                    break;
                case 'month':
                    this.pickerData = [generateYearPicker(), generateMonthPicker()];
                    this.newValue = [value[0], value[1]];
                    break;
                case 'day':
                    this.pickerData = [generateYearPicker(), generateMonthPicker(), this.generateDayPicker(value[0] + 1900, value[1] + 1)];
                    this.newValue = value;
                    break;
                default:
                    break;
            }
        },
        initRegion() {
            let provincePicker = [...AREA],
                cityPicker = [],
                areaPicker = [],
                province = {},
                city = {},
                area = {},
                value = [];

            if (this.info.customItem) {
                provincePicker.unshift({ value: this.info.customItem });
                cityPicker.unshift({ value: this.info.customItem });
                areaPicker.unshift({ value: this.info.customItem });
            }

            if (!this.info.value || !this.info.value[0]) {
              this.info.value = [this.info.customItem || provincePicker[0].value, this.info.customItem || provincePicker[0].children[0].value, this.info.customItem || provincePicker[0].children[0].children[0].value];
            }

            province = provincePicker.find((ele, index) => {
                if (ele.value === this.info.value[0]) {
                    value.push(index); // 省份
                    return true;
                } else {
                    return false;
                }
            });

            if (!province.children) {
                value = [0, 0, 0];
            } else {
                cityPicker.push(...province.children);
                city = cityPicker.find((ele, index) => {
                    if (ele.value === this.info.value[1]) {
                        value.push(index); // 市
                        return true;
                    } else {
                        return false;
                    }
                });

                if (!city.children) {
                    value.push(0, 0);
                } else {
                    areaPicker.push(...city.children);
                    area = areaPicker.find((ele, index) => {
                        if (ele.value === this.info.value[2]) {
                            value.push(index); // 市辖区
                            return true;
                        } else {
                            return false;
                        }
                    });
                    if (!area) {
                        value.push(0);
                    }
                }
            }

            this.pickerData = [provincePicker, cityPicker, areaPicker];
            this.newValue = value;
        },
        regionChange(i) {
            let province = this.pickerData[0][this.newValue[0]],
                city = [],
                area = [];
            if (this.info.customItem) {
                city.unshift({ value: this.info.customItem });
                area.unshift({ value: this.info.customItem });
            }

            switch(i) {
                case 0:
                    province.children && city.push(...province.children);
                    this.$set(this.pickerData, 1, city);
                    this.$set(this.newValue, 1, 0);
                    this.newValue[1] = 0;
                    this.wheels[1].wheelTo(0);
                    this.$nextTick(() => {
                        this.wheels[1].refresh();
                        this.regionChange(1);
                    });
                    break;
                case 1:
                    this.$nextTick(() => {
                        city = this.pickerData[1][this.wheels[1].getSelectedIndex()];
                        city.children && area.push(...city.children);
                        this.$set(this.pickerData, 2, area);
                        this.$set(this.newValue, 2, 0);
                        this.wheels[2].wheelTo(0);
                        this.$nextTick(() => {
                            this.wheels[2].refresh();
                        });
                    });

                    break;
                default:
                    break;
            }
        },
        createWheel (wheelWrapper, i) {
            if (!this.wheels[i]) {
                const wheel = this.wheels[i] = new BScroll(wheelWrapper.children[i], {
                    probeType: 2,
                    wheel: {
                        wheelWrapperClass: 'wheel-scroll',
                        wheelItemClass: 'wheel-item',
                        selectedIndex: this.newValue[i]
                    },
                    mouseWheel: true,
                    swipeTime: 1800
                });

                let oldValue, newValue;
                wheel.on('beforeScrollStart', () => {
                    oldValue = this.newValue[i];
                });
                wheel.on('scrollEnd', () => {
                    this.getCurrentValue();
                    newValue = this.wheels[i].getSelectedIndex();
                    if (oldValue === newValue) {
                        return;
                    }

                    switch (this.info.mode) {
                        case 'multiSelector':
                            this.columnChange({
                                column: i,
                                value: this.wheels[i].getSelectedIndex()
                            });
                            break;
                        case 'date':
                            if (this.info.fields === 'day' && (i == 0 || i == 1)) {
                                this.$set(this.pickerData, 2, this.generateDayPicker(
                                    this.newValue[0] + 1900,
                                    this.newValue[1]
                                ));
                                this.$nextTick(() => {
                                    this.wheels[2].refresh();
                                });
                            }
                            break;
                        case 'region':
                            this.regionChange(i);
                            break;
                    }


                });
            } else {
                this.wheels[i].refresh();
            }
            return this.wheels[i];
        },
        destroyExtraWheels () {
            const dataLength = this.pickerData.length;
            if (dataLength < this.wheels.length) {
                const extraWheels = this.wheels.splice(dataLength);
                extraWheels.forEach((wheel) => {
                    wheel.destroy();
                });
            }
        },
        wheelToAnchor () {
            this.wheels.forEach((wheel, i) => {
                wheel.wheelTo(this.originValue[i] || 0);
            });
            this.dataChange = true;
        },
        getCurrentValue() {
            this.wheels.forEach((wheel, i) => {
                const j = wheel.getSelectedIndex();
                this.$set(this.newValue, i, j);
            });
        },
        getValue() {
            let value = 0;
            let result = this.wheels.map(wheel => wheel.getSelectedIndex());
            let province, city, area;

            switch (this.info.mode) {
                case 'selector':
                    value = result[0];
                    break;
                case 'multiSelector':
                    value = result;
                    break;
                case 'time':
                    value = `${result[0] < 10 ? '0' : ''}${result[0]}:${result[1] < 10 ? '0' : ''}${result[1]}`;
                    break;
                case 'date':
                    value = result.map((ele, index) => this.pickerData[index][ele]).join('-').replace(/[年月日]/g, '');
                    break;
                case 'region':
                    province = this.pickerData[0][result[0]].value;
                    city = this.pickerData[1][result[1]].value;
                    area = this.pickerData[2][result[2]].value;
                    value = [province, city, area];
                    break;
                default:
                    break;
            }
            return value;

        },
        show(info) {
            this.info = Object.assign({}, info);
            this.visible = true;
            this.setPickerData();
            this.$nextTick(() => {
                const wheelWrapper = this.$refs.wheelWrapper;
                this.pickerData.forEach((item, index) => {
                    this.createWheel(wheelWrapper, index).enable();
                });
                this.destroyExtraWheels();
                this.wheelToAnchor();
            });
        },
        columnChange(detail) {
            if (this.info.mode !== 'multiSelector') {
                return;
            }
            this.dataChange = false;

            this.$emit('columnchange', {
              detail
            });
        },
        hide(event, native = true) {
            this.visible = false;
            this.wheels.forEach(wheel => wheel.destroy());
            this.wheels = [];

            if (!native) {
                return;
            }

            this.$emit(event.type, {
              detail: event.detail || { value: {} }
            });
        },
        cancel() {
            this.hide({
                type: 'cancel'
            });
        },
        confirm() {
            this.hide({
                type: 'change',
                detail: {
                    value: this.getValue()
                }
            });
        }
    }
};
</script>
<style lang="less" scoped>
.m-picker {
    .fade-enter, .fade-leave-to {
        opacity: 0;
    }
    .fade-enter-active, .fade-leave-active {
        transition: all .3s ease;
    }
    .mask {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: 9999;
        background: rgba(0,0,0,.2);
    }
    /* slide */
    .slide-enter, .slide-leave-to {
        opacity: 0.5;
        transform: translate3d(0, 270px, 0)
    }

    .slide-enter-active, .slide-leave-active {
        transition: all .3s ease;
    }
    .bl-picker {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        height: 270px;
        z-index: 10000;
        background: #fff;
        .picker-title {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 12px;
            font-size: 16px;
            color: #333;
            border-bottom: 1px solid #efefef;
            .pt-cancel {
                color: #999;
                cursor: pointer;
            }
            .pt-submit {
                color: #ff5687;
                cursor: pointer;
            }
        }
        .picker-panel {
            position: relative;
            height: 226px;
            padding: 12px 12px;
            box-sizing: border-box;

            .picker-mask-top, .picker-mask-bottom {
                position: absolute;
                left: 0;
                right: 0;
                height: 86px;
                background: #fff;
                transform: translateZ(0);
                z-index: 1;
                pointer-events: none;
            }

            .picker-mask-top {
                top: 12px;
                background: linear-gradient(to bottom, rgba(255,255,255,.9), rgba(255,255,255,.5));

                &:after {
                    content: '';
                    display: block;
                    position: absolute;
                    left: 0;
                    right: 0;
                    bottom: 0;
                    border-bottom: 1px solid #efefef;
                    transform: scaleY(.5);
                }
            }

            .picker-mask-bottom {
                bottom: 12px;
                background: linear-gradient(to top, rgba(255,255,255,.9), rgba(255,255,255,.5));

                &:before {
                    content: '';
                    display: block;
                    position: absolute;
                    left: 0;
                    right: 0;
                    top: 0;
                    border-bottom: 1px solid #efefef;
                    transform: scaleY(.5);
                }
            }
            .picker-wheel-wrapper {
                display: flex;
                align-items: stretch;
                height: 100%;

                .picker-wheel {
                    flex: 1;
                    overflow: hidden;
                }

                .wheel-scroll {
                    margin-top: 86px;

                    .wheel-item {
                        height: 30px;
                        line-height: 30px;
                        font-size: 14px;
                        text-align: center;
                        overflow: hidden;
                        text-overflow: ellipsis;
                        white-space: nowrap;
                        color: #333;
                    }
                }
            }
        }
    }
}
</style>

