import React,{PropTypes} from 'react';
import Component from './utils/Component';
import classnames from 'classnames';
import {setPhoenixPrefix} from './utils/Tool';

import Drag from './Drag';

/**
 * 滑动输入条组件<br/>
 * - 滑动进度条确定当前进度的百分比。
 * - 可通过设置process确定初始进度百分比, 范围从0-100。
 * - 可通过placement设置当前进度提示框的位置, 可选top/bottoom。
 * - 可通过tipStay设置初始和松开按钮时提示是否消失,默认false不显示。
 * - 可通过range制定范围,默认0-100,必需是长度为2的数组,第一个数字表示初始,第二个数字表示终点。
 * - 可通过showRange判断是否在进度条前后显示范围,默认不显示。
 * - 可通过duration设置固定移动的距离,默认1。
 * - 可通过onSliderChange设置拖拽进度条松开时的回调函数。
 * - 可通过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} />`
 * - onSliderChange:拖拽进度条松开时的回调函数 <br/>
 * 如: `<Slider onSliderChange={(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,
         /**
         * 每次移动的固定距离,默认1
         * @property duration
         * @type Number
         * @default 1
         * */
        duration: PropTypes.number,
        /**
         * 初始及松开按钮时是否显示进度
         * @property tipStay
         * @type Boolean
         * @default false
         * */
        tipStay: PropTypes.bool,
        /**
         * 改变进程时的回调函数
         * @method onSliderChange
         * @type Function
         * */
        onSliderChange: PropTypes.func
    };

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

    constructor(props, context) {
        super(props, context);
        
        this.range = this.validateRange();
        this.rangeDiff = this.range[1]-this.range[0];

        this.duration = this.validateDuration();
        this.eachDur = (this.range[1]-this.range[0])/this.duration;
        
        this.state = {
            realProgress: props.progress || this.range[0],
            tipVisible: props.tipStay || false
        }
        
    }

    validateRange(){
        let {range} = this.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;
        } 
        return range;
    }

    validateDuration(){
        let {duration} = this.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(this.state.realProgress != nextProps.progress){
            this.setState({
                realProgress: nextProps.progress
            });

            this.newProgressWidth = this.getNewProgressWidth(nextProps.progress);
            this.setSliderPosition(this.newProgressWidth + 'px');
        }
    }

    getNewProgressWidth(realProgress){ // 保留2位小数
        return this.sliderLength * (Math.round((realProgress-this.range[0])/this.rangeDiff*100)/100);
    }

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

    onDrag(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)
        });
    }

    onDrop(event, position){
        if(!this.props.tipStay){
            this.setState({
                tipVisible: false
            });
        }

        this.newProgressWidth = this.prevProgressWidth;

        if(this.props.onSliderChange) this.props.onSliderChange(this.state.realProgress);
    }

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

    render(){
        let {componentTag:Component, className, showRange} = this.props;

        return (
            <Component {...this.props} className={classnames(
                this.getProperty(true),
                className,
                showRange? setPhoenixPrefix('keep-range'):''
            )}>
                {this.renderSliderRange()}
                <div className={setPhoenixPrefix("slider-line")} ref={(sliderLine)=>{this.sliderLine=sliderLine}}>
                    <div className={setPhoenixPrefix("slider-progress")} ref={(sliderProgress)=>{this.sliderProgress=sliderProgress}}></div>
                    <div className={setPhoenixPrefix("slider-content")} ref={(sliderBtn)=>{this.sliderBtn=sliderBtn}}>
                        <div className={classnames(setPhoenixPrefix("slider-tip"), this.state.tipVisible?'show':'hide')}>{this.state.realProgress}</div>
                        <Drag className={setPhoenixPrefix("slider-btn")} onDrag={::this.onDrag} onDrop={::this.onDrop}></Drag>
                    </div>
                </div>
            </Component>
        );
    }

}