import React from 'react'
import PropTypes from 'prop-types'
import Component from '../utils/Component'
import classnames from 'classnames'
import {setPhPrefix, getDeviceInfo, preventDefault} from '../utils/Tool'
import Logger from '../utils/logger'

/**
 * 拖拽组件<br/>
 * - 兼容移动端的touch和pc端的mouse事件。
 * - 可通过dragCallback设置抓取的回调函数, 返回抓取在屏幕上的位置, 分别保存在start和move中, 以x和y的形式展示。
 * - 可通过dropCallback设置松开瞬间的回调函数, 返回松开时在屏幕上的位置, 保存在end中, 以x和y的形式展示。
 *
 * 示例:
 * ```code
 *     <Drag dragCallback={::this.dragCallback} dropCallback={::this.dropCallback} style={{height:0}}>
 *         <div className='box' ref={(box)=>{this.box = box}}>Drag</div>
 *     </Drag>
 * ```
 * ```code
 *     dragCallback(event,position){
 *         this.prePosition = position.start;
 *         this.nowPosition = position.move;
 *
 *         this.distanceX = this.preDistanceX + this.nowPosition.x - this.prePosition.x;
 *         this.distanceY = this.preDistanceY + this.nowPosition.y - this.prePosition.y;
 *         console.log(this.distanceX, this.distanceY);
 *     }
 *     dropCallback(event,position){
 *         this.preDistanceX = this.distanceX;
 *         this.preDistanceY = this.distanceY;
 *     }
 * ```
 *
 * @class Drag
 * @module 辅助组件
 * @extends Component
 * @constructor
 * @since 1.0.0
 * @demo drag|drag.js {展示}
 * @show true
 * */

export default class Drag extends Component{

    static propTypes = {
        /**
         * 抓取的执行函数,对应TouchStart/TouchMove
         * @method dragCallback
         * @param {object} event
         * @param {object} position 位置坐标
         * @type Function
         * */
        dragCallback: PropTypes.func,
        /**
         * 放开的执行函数,对应TouchEnd
         * @method dropCallback
         * @param {object} event
         * @param {object} position 位置坐标
         * @type Function
         * */
        dropCallback: PropTypes.func
    };

    static defaultProps = {
        classMapping : {}
    };

    constructor(props, context) {
        super(props, context);

        new Logger('Drag'); 

        this.mobile = getDeviceInfo('mobile');

        this.state = {
            position: {}
        };

        this.onMouseMoveHandle = this.onMouseMove.bind(this);
        this.onMouseEndHandle = this.onMouseEnd.bind(this);   

        this.isMouseDown = false;
    }

    onTouchStart(event){
        let {dragCallback, dragStartCallback} = this.props;
        event.stopPropagation();
        preventDefault(event);

        this.state.position.start = {x:event.touches[0].pageX, y: event.touches[0].pageY};
        this.state.position.move = this.state.position.start;

        if(dragCallback) dragCallback(event, this.state.position);
        if(dragStartCallback) dragStartCallback(event, this.state.position);

        return false;
    }

    onMouseStart(event){
        let {dragCallback, dragStartCallback} = this.props;
        this.isMouseDown = true;
        event.stopPropagation();
        preventDefault(event);

        this.state.position.start = {x:event.pageX, y: event.pageY};
        this.state.position.move = this.state.position.start;

        if(dragCallback) dragCallback(event, this.state.position);
        if(dragStartCallback) dragStartCallback(event, this.state.position);

        if(!this.mobile){
            document.addEventListener('mousemove',this.onMouseMoveHandle,false);
            document.addEventListener('mouseup',this.onMouseEndHandle,false);
        } 

        return false;
    }

    onTouchMove(event){
        event.stopPropagation();
        preventDefault(event);

        this.state.position.move = {x:event.touches[0].pageX, y: event.touches[0].pageY};

        if(this.props.dragCallback) this.props.dragCallback(event, this.state.position);

        return false;
    }

    onMouseMove(event){
        if(!this.isMouseDown) return;
        event.stopPropagation();
        preventDefault(event);

        this.state.position.move = {x:event.pageX, y: event.pageY};

        if(this.props.dragCallback) this.props.dragCallback(event, this.state.position);
        
        return false;
    }

    onTouchEnd(event){
        event.stopPropagation();
        preventDefault(event);

        this.state.position.end = {x:event.changedTouches[0].pageX, y: event.changedTouches[0].pageY};
        this.state.position.start = this.state.position.move;

        if(this.props.dropCallback) this.props.dropCallback(event, this.state.position);

        return false;
    }

    onMouseEnd(event){
        event.stopPropagation();
        preventDefault(event);

        this.state.position.end = {x:event.pageX, y: event.pageY};
        this.state.position.start = this.state.position.move;

        if(this.props.dropCallback) this.props.dropCallback(event, this.state.position);
        this.isMouseDown = false;
        
        if(!this.mobile){
            document.removeEventListener('mousemove',this.onMouseMoveHandle,false);
            document.removeEventListener('mouseup',this.onMouseEndHandle,false);
        }
        return false;
    }

    onTouchCancel(event){
        // 触屏取消:忽然来电话等情况
    }

    componentWillUnmount(){
        if(!this.mobile){
            document.removeEventListener('mousemove',this.onMouseMoveHandle,false);
            document.removeEventListener('mouseup',this.onMouseEndHandle,false);
        }
    }

    renderDrag(){
        return (
            <div {...this.otherProps} className={classnames(setPhPrefix('drag-action'), this.props.className, 'user-none')}
                onTouchStart={(event)=>{this.onTouchStart(event)}}
                onTouchMove={(event)=>{this.onTouchMove(event)}}
                onTouchEnd={(event)=>{this.onTouchEnd(event)}}
                onTouchCancel={(event)=>{this.onTouchCancel(event)}}

                onMouseDown={(event)=>{this.onMouseStart(event)}}

                ref={(dragAction)=>{this.dragAction = dragAction;}}
            >
                {this.props.children}
            </div>
        );
    }

    render(){
        return this.renderDrag()
    }

}