import React from 'react'
import PropTypes from 'prop-types'
import Component from '../utils/Component'
import classnames from 'classnames'
import Logger from '../utils/logger'

import Drag from '../drag/'

import '../style'
import 'phoenix-styles/less/modules/slider.less'

/**
 * 滑动输入条组件<br/>
 * - 滑动进度条确定当前进度的百分比。
 * - 可通过设置process确定初始进度百分比, 范围从0-100。
 * - 可通过tipMode选择当前查看进度的方式,可选default和tooltip。
 * - 可通过placement设置当前进度提示框的位置, 可选top/bottoom(tipMode为tooltip时生效)。
 * - 可通过tipStay设置初始和松开按钮时提示是否消失,默认false不显示(tipMode为tooltip时生效)。
 * - 可通过range制定范围,默认0-100,必需是长度为2的数组,第一个数字表示初始,第二个数字表示终点。
 * - 可通过showRange判断是否在进度条前后显示范围,默认不显示。
 * - 可通过duration设置固定移动的距离,默认1。
 * - 可通过slideCallback设置拖拽进度条松开时的回调函数。
 * - 可通过disabled设置进度条只读。
 * - 使用Slider前确保父级是有宽度的元素;使用flex需要加一层宽度100%的外壳。
 *
 * 主要属性和接口:
 * - process:初始进度百分比, 默认0 <br/>
 * 如: `<Slider progress={10}/>`
 * - placement:进度提示框的位置, 默认top <br/>
 * 如: `<Slider placement='bottom' />`
 * - tipStay:初始和松开按钮时提示是否消失,默认false <br/>
 * 如: `<Slider tipStay />`
 * - range:范围,默认[0,100]。 <br/>
 * 如: `<Slider range={[20,50]} />`
 * - showRange:是否在进度条前后显示范围,默认不显示。 <br/>
 * 如: `<Slider showRange />`
 * - duration:固定移动的距离,默认1。 <br/>
 * 如: `<Slider duration={20} />`
 * - slideCallback:拖拽进度条松开时的回调函数 <br/>
 * 如: `<Slider slideCallback={(progress)=>{console.log(progress);} />`
 * - disabled:进度条只读, 不可操作 <br/>
 * 如: `<Slider disabled/>`
 *
 * @class Slider
 * @module 操作类组件
 * @extends Component
 * @constructor
 * @since 1.0.0
 * @demo slider|slider.js {展示}
 * @show true
 * */

export default class Slider extends Component{
    
    static propTypes = {
        /**
         * 样式前缀
         * @property classPrefix
         * @type String
         * @default 'slider'
         * */
        classPrefix: PropTypes.string,
        /**
         * 标签tagName
         * @property componentTag
         * @type String
         * */
        componentTag:PropTypes.string,
        /**
         * 初始进程,默认0
         * @property progress
         * @type String
         * */
        progress:PropTypes.number,
        /**
         * 进程提示的位置,默认top
         * @property placement
         * @type String
         * @default 'top'
         * */
        placement: PropTypes.string,
            /**
         * 范围,默认0-100,可传固定范围的数组如:[25,50]
         * @property range
         * @type Array
         * @default [0,100]
         * */
        range: PropTypes.array,
        /**
         * 是否在进度条前后显示范围
         * @property showRange
         * @type Boolean
         * @default false
         * */
        showRange: PropTypes.bool,
        /**
         * 显示提示的模式,可选[default,tooltip]
         * @property tipMode
         * @type String
         * @default 'default'
         * */
        tipMode: PropTypes.string,
            /**
         * 每次移动的固定距离,默认1
         * @property duration
         * @type Number
         * @default 1
         * */
        duration: PropTypes.number,
        /**
         * 初始及松开按钮时是否显示tooltip
         * @property tipStay
         * @type Boolean
         * @default false
         * */
        tipStay: PropTypes.bool,
        /**
         * 改变进程时的回调函数
         * @method slideCallback
         * @param {number} progress 进度
         * @type Function
         * */
        slideCallback: PropTypes.func
    };

    static defaultProps = {
        placement: 'top',
        progress: 0,
        range: [0,100],
        showRange: false,
        duration: 1,
        tipMode: 'default',
        tipStay: false,
        classPrefix:'slider',
        componentTag:'div',
        classMapping : {
            'disabled': 'disabled',
            'top': 'tip-top',
            'bottom': 'tip-bottom'
        }
    };

    constructor(props, context) {
        super(props, context)
        
        new Logger('Slider')
        
        this.init(props);
        
        this.state = {
            realProgress: props.progress || this.range[0],
            tipVisible: props.tipStay || false
        }
        
    }

    init(props){
        this.range = this.validateRange(props);
        this.rangeDiff = this.range[1]-this.range[0];

        this.duration = this.validateDuration(props);
        this.eachDur = (this.range[1]-this.range[0])/this.duration;
    }

    validateRange(props){
        let {range, progress} = props, 
            defaultRange = [0,100];
        if(!range instanceof Array) return defaultRange;
        if(range.length != 2){
            console.error('Invalid prop `range` of length not equal to 2.');
            return defaultRange;
        } 
        if(range[0] >= range[1]){
            console.error('Invalid prop `range[0]` must be less than or equal to `range[1]`.');
            return defaultRange;
        }
        if(progress)
            if(progress<range[0] || progress>range[1]){
                console.error('`Progress prop` have to between `range[0]` and `range[1]`.');
                return range;
            }
        return range;
    }

    validateDuration(props){
        let {duration} = props, 
            defaultDuration = 1;
        
        if(duration<=0) {
            console.error('Invalid prop `duration` have to be Positive.');
            return defaultDuration;
        }
        if((this.range[1] - this.range[0])%duration != 0){ // 不能整除的情况
            console.error('Prop `duration` can not be divided by `range`.');
            return defaultDuration;
        }
        return duration;
    }

    componentDidMount(){
        this.sliderLength = parseInt(this.sliderLine.offsetWidth);
        this.eachSection = this.sliderLength/this.rangeDiff*this.duration;
        // if(this.eachSection<1) this.eachSection = 1; // 最小1px

        this.newProgressWidth = this.getNewProgressWidth(this.state.realProgress);
        this.setSliderPosition(this.newProgressWidth + 'px');
    }

    componentWillReceiveProps(nextProps){
        if(nextProps.progress!=undefined && this.state.realProgress != nextProps.progress){
            this.setState({
                realProgress: nextProps.progress
            });
            this.setNewProgress(nextProps)
        }
        if(nextProps.range!=undefined && this.range != nextProps.range){
            this.init(nextProps)
            this.setNewProgress(nextProps)
        }
    }

    setNewProgress(props){
        this.newProgressWidth = this.getNewProgressWidth(props.progress);
        this.setSliderPosition(this.newProgressWidth + 'px');
    }

    getNewProgressWidth(realProgress){ // 保留2位小数
        let per = Math.round((realProgress-this.range[0])/this.rangeDiff*100)/100
        if(per>=1) per = 1
        if(per<=0) per = 0
        return this.sliderLength * per
    }

    setSliderPosition(distance){
        this.sliderProgress.style.width = distance;
        this.sliderBtn.style.left = distance;
    }

    dragCallback(event, position){
        let newProgress, nowSec;

        this.preX = position.start.x;
        this.X = position.move.x;
        this.distance = this.X - this.preX;

        this.prevProgressWidth = this.newProgressWidth + this.distance;

        if(this.prevProgressWidth <= 0) this.prevProgressWidth = 0;
        if(this.prevProgressWidth >= this.sliderLength) this.prevProgressWidth = this.sliderLength;

        nowSec = Math.round(this.prevProgressWidth/this.eachSection, 0);
        this.prevProgressWidth = this.eachSection*nowSec;

        newProgress = this.prevProgressWidth/this.sliderLength * this.rangeDiff + this.range[0];

        this.setSliderPosition(this.prevProgressWidth + 'px');

        this.setState({
            tipVisible: true,
            realProgress: parseInt(newProgress)
        });
    }

    dropCallback(event, position){
        let {tipStay, slideCallback} = this.props
        if(!tipStay){
            this.setState({
                tipVisible: false
            });
        }

        this.newProgressWidth = this.prevProgressWidth;

        if(slideCallback) slideCallback(this.state.realProgress);
    }

    renderSliderText(showTipMode){
        if(showTipMode){
            return <div className={this.setPhPrefix('text')}>{this.state.realProgress}</div>
        }        
    }

    renderSliderRange(){
        if(this.props.showRange){
            return (
                <div className={this.setPhPrefix('range')}>
                    <strong className={this.setPhPrefix('range-start')}>{this.range[0]}</strong>
                    <strong className={this.setPhPrefix('range-end')}>{this.range[1]}</strong>
                </div>
            );
        }else{
            return '';
        }
    }

    renderSlider(){
        let {componentTag:Component, className, showRange, tipMode} = this.props,
            showTipMode = tipMode=='default';

        return (
            <Component {...this.otherProps} className={classnames(
                this.getProperty(true),
                className,
                showRange? this.setPhPrefix('keep-range',true):''
            )}>
                {this.renderSliderText(showTipMode)}
                {this.renderSliderRange()}
                <div className={this.setPhPrefix('line')} ref={(sliderLine)=>{this.sliderLine=sliderLine}}>
                    <div className={this.setPhPrefix('progress')} ref={(sliderProgress)=>{this.sliderProgress=sliderProgress}}></div>
                    <div className={this.setPhPrefix('content')} ref={(sliderBtn)=>{this.sliderBtn=sliderBtn}}>
                        <div className={classnames(this.setPhPrefix('tip'), this.state.tipVisible && !showTipMode?'show':'hide')}>{this.state.realProgress}</div>
                        <Drag className={classnames(this.setPhPrefix('btn'),'hardware')} dragCallback={this.dragCallback.bind(this)} dropCallback={this.dropCallback.bind(this)}></Drag>
                    </div>
                </div>
            </Component>
        );
    }

    render(){
        return this.renderSlider()
    }

}