const Dragable = (($)=>{
    const DefaultConfig = {
        moveOpacity:0.8,
        axis:"",           
        cursor:"move",      
        containment:"",     
        defineRepaint:null,     
        startHandle:null,     
        dragHandle:null,      
        stopHandle:null       
    }
    class Dragable{
        constructor(config){
            
            this._config = this._getConfig(config);
            this._target = this._config.target;
            this._handle = this._config.handle;
            this._$target = $(this._target);
            this._$handle = this._handle? $(this._handle): this._$target;
            this.dragRange = null;
            this.dragging = false;
            this.oldOpacity = 1;
            this.offset = {x:0, y:0};
            this.handleSize = null;
            this.targetSize = null;
            this._onMouseUp = (event)=>{
                if(this.dragging){
                    event.stopPropagation();
                    this.dragging = false;
                    const {stopHandle} = this._config;
                    this._config.cursor && this._$target.css('cursor', '');
                    if(stopHandle){
                        stopHandle(event, this._$target);
                    }
                    document.removeEventListener("mousemove", this._docMove);
                    if(this._$target){
                        this._$target.css("opacity", this.oldOpacity);
                    }
                }
            };
            this._docDown =(event)=> {
                if(this.dragging==false){
                    this.dragging = true;
                    this._config.cursor && this._$target.css('cursor', this._config.cursor);
                    const {startHandle} = this._config;
                    if(startHandle){
                        startHandle(event, this._$target);
                    }
                    document.removeEventListener("mousemove", this._docMove);
                    document.addEventListener("mousemove", this._docMove, true);
                    if(this._$target){
                        this.oldOpacity = this._$target.css("opacity");
                        this._$target.css("opacity", this._config.moveOpacity);
                    }
                    this.dragRange = this._getDragRange();
                    this.handleSize = {width:this._$handle.outerWidth(), height:this._$handle.outerHeight()};
                    this.targetSize = {width:this._$target.outerWidth(), height:this._$target.outerHeight()};
                    let mousePos = this.getMouseXY(event),
                        nowPosition = this._$target.css('position'),
                        transform = this._$target.css('transform'),
                        arrTransform = transform.match(/matrix\(([^)]+)/);
                    this._isFlex = this._$target.parent().css('display') === 'flex';
                    if(this._isFlex){
                        let offset = this._$target.offset();
                        this._flexStartLeft = 0 - offset.left;
                        this._flexStartTop = 0 - offset.top;
                    }
                    if(arrTransform && arrTransform.length > 1){
                        let arrTemp = arrTransform[1].replace(/\s/g,'').split(',');
                        this._transformLeft = parseInt(arrTemp[4]);
                        this._transformTop = parseInt(arrTemp[5]);
                    }
                    if(!nowPosition.match(/absolute|relative|fixed/)){
                        this._$target.css('position', 'relative');
                    }
                    let left = this._$target.css('left'),
                        top = this._$target.css('top');
                    this.offset = {x: left === 'auto'? 0: parseInt(left), y: top === 'auto'? 0: parseInt(top)};
                    this._mouseDownX = mousePos.x;
                    this._mouseDownY = mousePos.y;
                    this.lastOffset = Object.assign({}, this.offset);
                }
            };
            this._docMove = (event)=>{
                if (this.dragging) {
                    event.stopPropagation();
                    let mousePos = this.getMouseXY(event);
                    const {dragHandle} = this._config;
                    let newOffset = this.getNewPosition(this._$target, this.offset, mousePos, this.handleSize, this.targetSize, this.dragRange),
                        lastOffset = this.lastOffset;
                    if(typeof this._config.defineRepaint === 'function'){
                        newOffset = this._config.defineRepaint(newOffset);
                    }
                    if((newOffset.x !== undefined && lastOffset.x !== newOffset.x || newOffset.y !== undefined && lastOffset.y !== newOffset.y)){
                        Object.assign(this.lastOffset, newOffset);
                        this._$target.css({"left": this.lastOffset.x, "top": this.lastOffset.y});
                        if(dragHandle && typeof dragHandle === 'function'){
                            dragHandle(event, this._$target, mousePos);
                        }
                    }
                }
            };
            this._addEvent();
        }
        
        _getConfig(config) {
            return $.extend(true, {}, DefaultConfig, config);
        }
        
        _getDragRange(){
            let $containment = $(this._config.containment);
            if($containment.length==0){
                $containment = $(document);
            }
            return {x: 0, y: 0, width: $containment.width(), height: $containment.height()}
        }
        
        getNewPosition($target, offset, mousePos, handleSize, targetSize, dragRange){
            if($target && mousePos){
                let transformLeft = this._transformLeft || 0,
                    transformTop = this._transformTop || 0,
                    originX = this.offset.x + mousePos.x - this._mouseDownX,
                    originY = this.offset.y + mousePos.y - this._mouseDownY;
                let x = 0,
                    y = 0,
                    wrapWidth = 0,
                    wrapHeight = 0;
                if(this._isFlex){
                    wrapWidth = -this._flexStartLeft;
                    wrapHeight = -this._flexStartTop;
                    x = Math.max(this._flexStartLeft, originX);
                    y = Math.max(this._flexStartTop, originY);
                }else{
                    wrapWidth = dragRange.width - targetSize.width - transformLeft;
                    wrapHeight = dragRange.height - targetSize.height - transformTop;
                    x = Math.max(-transformLeft, originX);
                    y = Math.max(-transformTop, originY);
                }
                x = Math.min(x, wrapWidth);
                y = Math.min(y, wrapHeight);
                if(this._config.axis === 'x'){
                    return {x, originX, originY, wrapWidth, wrapHeight};
                }else if(this._config.axis === 'y'){
                    return {y, originX, originY, wrapWidth, wrapHeight};
                }else{
                    return {x, y, originX, originY, wrapWidth, wrapHeight};
                }
            }
        }
        
        
        updateDragRange(x,y, width, height){
            if(x!=undefined && y!=undefined && width!=undefined && height!=undefined){
                this.dragRange = {x:x, y:y, width:width, height:height};
            }
        }
        
        _addEvent(){
            document.removeEventListener("mouseup", this._onMouseUp);
            document.addEventListener("mouseup", this._onMouseUp);
            if(this._$target && this._$handle){
                this._$handle.on("mousedown", this._docDown);
            }
        }
        
        getMouseXY(e){
            let x = 0, y = 0;
            e = e || window.event;
            if (e.pageX != undefined) {
                x = e.pageX;
                y = e.pageY;
            }
            else {
                x = e.clientX + document.body.scrollLeft - document.body.clientLeft;
                y = e.clientY + document.body.scrollTop - document.body.clientTop;
            }
            return {
                x: x,
                y: y
            };
        }
        dispose(){
            document.removeEventListener("mouseup", this._onMouseUp);
            document.removeEventListener("mousemove", this._docMove);
            if(this._$target){
                this._$target.off();
            }
            if(this._$handle){
                this._$handle.off();
            }
            this._$handle = null;
            this._config = null;
            this._$target = null;
            this._target = null;
            this._handle = null;
            this.dragging = false;
            this.dragRange = null;
            this.handleSize = null;
            this.targetSize = null;
            this.offset = null;
        }
    }
    return Dragable;
})($);
export default Dragable;