const DragSort = (($) => {
    const DefaultConfig = {
        node: '.J_layoutModule',                    
        items: 'dl',                                
        itemsNotUp: 'cd-no-sort-up',                  
        itemsNotDown: 'cd-no-sort-down',            
        referUp: '',                                  
        handle: '',                               
        dragingClass: 'cd-dragSort-item',          
        minZIndex: 1,                              
        
        begin: function (e, ui) {
        },
        
        helper: function (e, ui, zIndex) {
            let $dragItem = ui.dragItem,
                $helper = ui.dragItem.clone();
            $helper.css({
                width: $dragItem.width(),
                height: $dragItem.height(),
                border:  $dragItem.css('border'),
                borderRadius:  $dragItem.css('borderRadius'),
                backgroundColor: $dragItem.css('backgroundColor'),
                color: $dragItem.css('color'),
                fontSize: $dragItem.css('fontSize'),
                lineHeight: $dragItem.css('lineHeight'),
                padding: $dragItem.css('padding'),
                textIndent: $dragItem.css('textIndent')
            });
            return $helper;
        },
        
        change: function (e, ui, newIndex){
            console.log('元素被拖到了第' + newIndex + '个位置');
        }
    };
    class DragSort {
        
        constructor(config) {
            this.ui = {};                  
            this._isMouseDown = false;     
            this._mouseDownDom = null;     
            this._isDraging = false;       
            this.config = this._getConfig(config);
            this._random = '.DragSort' + Date.now();
            if (this._dataInit() === true) {
                this._bindEvent();
            }
        }
        
        _getConfig(config) {
            return $.extend(true, {}, DefaultConfig, config);
        }
        
        _dataInit() {
            let _this = this;
            if (!_this.config.node) {
                console.log("没有传入node参数!");
                return;
            }
            _this.element = $(_this.config.node); 
            _this.element.addClass('d-disable-select');     
            _this.ui = {}; 
            if (_this.element.data('initDragSort')) {
                return;
            }
            _this.element.data('initDragSort', true);
            return true;
        }
        
        _bindEvent() {
            let _this = this;
            _this.element.on('mousedown' + _this._random, _this.config.handle || _this.config.items, function (e) {
                let $wrap = $(this).closest(_this.config.items);
                if (_this._isMouseDown === false && !$wrap.hasClass(_this.config.itemsNotUp) && !$wrap.hasClass(_this.config.itemsNotDown)) {
                    _this._isMouseDown = true;
                    _this._mouseDownDom = this;
                    _this._mouseDownStartX = e.clientX;
                    _this._mouseDownStartY = e.clientY;
                    _this._referUps = _this.config.referUp? $(_this.config.referUp, _this.element): null;
                    $(document).on('mousemove' + _this._random, _this._mousemoveEvent.bind(_this)).on('mouseup' + _this._random, _this._mouseupEvent.bind(_this));
                }
            });
            _this.element.on('mousemove' + _this._random, function (jqueryE, e) {
                let ev = e || jqueryE;
                if(_this._isMouseDown && _this._isDraging === false){
                    let xMouseDistance = Math.abs(ev.clientX - _this._mouseDownStartX),
                        yMouseDistance = Math.abs(ev.clientY - _this._mouseDownStartY),
                        hasMove = xMouseDistance >= 3 || yMouseDistance >= 3;   
                    if(hasMove){
                        _this._isDraging = true;
                        _this.ui.dragItem = $(_this._mouseDownDom).closest(_this.config.items);            
                        _this._dragIndex = _this.ui.dragItem.index();       
                        _this._dragParent = _this.ui.dragItem.parent()[0];  
                        _this.ui.helper = _this._createHelper(e);    
                        _this.ui.placeholder = _this._createTarget(e);     
                        _this.ui.dragItem.hide();
                        $.isFunction(_this.config.begin) && _this.config.begin(e, _this.ui);
                    }
                }
                if (_this._isDraging) {
                    if (_this.ui.placeholder === null) {
                        _this.ui.placeholder = _this._createTarget(ev);
                    }
                    let nowY = ev.clientY + $(window).scrollTop(),
                        minVal = 0,
                        originVal = 0,
                        minIndex = 0,
                        $items = _this.element.find(_this.config.items),
                        lastOffsetTop = $items.parent().offset().top;
                    $items = $items.add(_this._referUps);
                    $items.map(function (index) {
                        let offsetTop = $(this).offset().top;
                        if($(this).css('display') === 'none' && offsetTop === 0){
                            offsetTop = lastOffsetTop;
                        }
                        lastOffsetTop = offsetTop;
                        let val = nowY - (offsetTop + $(this).outerHeight() / 2),
                            absVal = Math.abs(val);
                        if (index === 0) {
                            minVal = absVal;
                            originVal = val;
                        } else if (absVal < minVal) {
                            minVal = absVal;
                            originVal = val;
                            minIndex = index;
                        }
                    });
                    let $Box = $items.eq(minIndex),
                        isReferUpNode = _this.config.referUp && _this._referUps.toArray().indexOf($Box[0]) > -1,       
                        canMoveUp = !$Box.closest(_this.config.items).hasClass(_this.config.itemsNotUp),      
                        canMoveDown = !$Box.closest(_this.config.items).hasClass(_this.config.itemsNotDown);      
                    if (originVal < 0 && canMoveUp && isReferUpNode === false) {
                        $Box.before(_this.ui.placeholder);
                    } else if(canMoveDown){
                        $Box.after(_this.ui.placeholder);
                    }else{
                        _this.ui.dragItem.after(_this.ui.placeholder);
                    }
                }
            });
        }
        _mousemoveEvent(e) {
            if(this._isDraging){
                let _this = this;
                _this.ui.helper.css({
                    'top': e.clientY + $(window).scrollTop() - (_this.ui.helper.outerHeight() / 2),
                    'left': e.clientX + $(window).scrollLeft() - (_this.ui.helper.outerWidth() / 2)
                });
                if(!e.isTrigger){
                    _this.element.trigger('mousemove' + _this._random, e);
                }
            }
        }
        _mouseupEvent(e) {
            let _this = this;
            if(_this._isMouseDown){
                _this._isMouseDown = false;
                _this._mouseDownDom = null;
                if(_this._isDraging){
                    _this._isDraging = false;
                    let newIndex = _this.ui.placeholder.prevAll(':visible').length,
                        indexChange = newIndex !== _this._dragIndex || _this._dragParent !== _this.ui.placeholder.parent()[0];  
                    _this._dragEndUiUpdate().then(() => {
                        if(indexChange){
                            _this.config.change(e, _this.ui, newIndex);
                        }
                    });
                    _this.ui.dragItem.removeClass(_this.config.dragingClass);
                    if (!_this.ui.placeholder) _this.ui.helper.remove();
                }
                $(document).off('mousemove' + _this._random).off('mouseup' + _this._random);
            }
        }
        
        _dragEndUiUpdate() {
            this.ui.helper.addClass('d-helper-transition');
            this.ui.helper.css({
                left: this.ui.placeholder.offset().left,
                top: this.ui.placeholder.offset().top
            });
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    if (this.ui.placeholder) {
                        this.ui.placeholder.replaceWith(this.ui.dragItem);
                        this.ui.placeholder.remove();
                        this.ui.dragItem.show();
                    }
                    if (this.ui.helper){
                        this.ui.helper.remove()
                    };
                    resolve();
                }, 200);
            });
        }
        
        _createTarget(event) {
            let target;
            if ($.isFunction(this.config.target)) {
                target = $(this.config.target.apply(this, [event, this.ui]));
            }else {
                let $dragItem = this.ui.dragItem;
                target = $('<div class="d-target-object-area">').css({
                    height: $dragItem.outerHeight(),
                    margin: $dragItem.css('margin'),
                    borderRadius: $dragItem.css('borderRadius')
                });
            }
            return target;
        }
        
        _createHelper(event) {
            let _this = this,
                $item = _this.ui.dragItem,
                boxLeft = $item.offset().left,
                boxTop = $item.offset().top,
                $helper = $.isFunction(this.config.helper) ? $(this.config.helper.apply(this, [event, this.ui, this.config.minZIndex + 1])) : (this.config.helper == 'clone' ? this.ui.dragItem.clone() : this.ui.dragItem);
            $helper.css({
                'position': 'absolute',
                'left': boxLeft,
                'top': boxTop,
                'z-index': this.config.minZIndex + 1,
                'opacity': .75
            }).appendTo('body');
            return $helper;
        }
        
        dispose() {
            this.element.off('mousemove' + this._random);
            this.element.off('mousedown' + this._random);
            this.element.data('initDragSort', false);
            $(document).off('mousemove' + this._random).off('mouseup' + this._random);
            delete this;
        }
    }
    return DragSort;
})($);
export default DragSort;