import React from 'react'

import './style.less'

var isDrag = false
var isAway = false

var startX
var startY

var disX
var disY

var distDragBack = 200
var distDragMax = 100

var currentDataIndex = 0

var ua = navigator.userAgent
var isAndroid = ua.indexOf("Android") > 0
var isIOS = /iP(ad|hone|od)/.test(ua)
var IE11touch = navigator.pointerEnabled
var IE9_10touch = navigator.msPointerEnabled
var w3ctouch = (function () {
    var supported = isIOS || false
    //http://stackoverflow.com/questions/5713393/creating-and-firing-touch-events-on-a-touch-enabled-browser
    try {
        var div = document.createElement("div")
        div.ontouchstart = function () {
            supported = true
        }
        var e = document.createEvent("TouchEvent")
        e.initUIEvent("touchstart", true, true)
        div.dispatchEvent(e)
    } catch (err) {
    }
    div = div.ontouchstart = null
    return supported
})()

var touchSupported = !!(w3ctouch || IE11touch || IE9_10touch)

var touchNames = ["mousedown", "mousemove", "mouseup", ""]
if (w3ctouch) {
    touchNames = ["touchstart", "touchmove", "touchend", "touchcancel"]
} else if (IE11touch) {
    touchNames = ["pointerdown", "pointermove", "pointerup", "pointercancel"]
} else if (IE9_10touch) {
    touchNames = ["MSPointerDown", "MSPointerMove", "MSPointerUp", "MSPointerCancel"]
}

React.initializeTouchEvents(true)


const ElasticStack = React.createClass({

    propTypes: {
        data: React.PropTypes.array,
        onChange: React.PropTypes.func
    },

    statics: {
        getDefaultData(){
            return [
                "1111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n1111111111111111111111111111111111\n11111111111111111\n11111111111111111\n111111111111111111",
                "2",
                "3",
                "4",
                "5",
                "6",
                "7",
                "8",
                "9",
                "10",
                "11"
            ]
        }
    },

    getInitialState(){
        return {
            item0rder: [1, 2, 3]
        }
    },

    render: function () {

        var handler = {}

        if (touchNames[0] === 'mousedown') {
            handler['onMouseDown'] = this.onMouseDown
        } else {
            handler['onTouchStart'] = this.onMouseDown
        }
        return (
            <div className='elasticstack' ref='elasticstack'
                {...handler}>
                <div className="elasticstack-inner">
                    {this.renderInner()}
                </div>
            </div>
        )
    },

    renderInner(){
        return this.state.item0rder.map((item, index) => {
            return (
                <div ref={'item' + (index+1)} className="item">
                    <div className="item-warp">
                    </div>
                </div>
            )
        })
    },

    onMouseDown(event){

        var item1 = React.findDOMNode(this.refs.item1)
        var item2 = React.findDOMNode(this.refs.item2)
        var item3 = React.findDOMNode(this.refs.item3)

        item1.classList.remove('ohide')
        item1.classList.remove('animate')
        item1.classList.remove('move-back')

        item2.classList.remove('animate')
        item2.classList.remove('move-back')

        item3.classList.remove('animate')
        item3.classList.remove('move-back')

        startX = getCoordinates(event).x
        startY = getCoordinates(event).y

        isDrag = true
    },

    onMouseMove(event){

        if (isDrag) {
            var item1 = React.findDOMNode(this.refs.item1)
            var item2 = React.findDOMNode(this.refs.item2)
            var item3 = React.findDOMNode(this.refs.item3)

            disX = getCoordinates(event).x - startX
            disY = getCoordinates(event).y - startY

            if ((touchNames[0] !== 'mousedown') && (Math.abs(disX) <= 20 || Math.abs(disY) > 20)) {
                return
            }

            if (this._outOfBounds()) {
                this._moveAway()
            } else {

                if (item1) {
                    setTransformStyle(item1, 'translate3d(' + ( disX) + 'px,' + ( disY) + 'px, 0) rotateZ(0deg)')
                }
                if (item2) {
                    setTransformStyle(item2, 'translate3d(' + ( disX * .6 ) + 'px,' + ( disY * .6 ) + 'px, 0) rotateZ(2deg)')
                }
                if (item3) {
                    setTransformStyle(item3, 'translate3d(' + ( disX * .3 ) + 'px,' + ( disY * .3 ) + 'px, 0) rotateZ(-3deg)')
                }
            }
        }
    },

    onMouseUp(){
        this.executeBack()
    },
    componentDidMount(){

        currentDataIndex = 0

        var node = React.findDOMNode(this.refs.elasticstack)

        node.ownerDocument.addEventListener(touchNames[1], this.onMouseMove)
        node.ownerDocument.addEventListener(touchNames[2], this.onMouseUp)

        var item1 = React.findDOMNode(this.refs.item1)

        var element = item1.querySelector('.item-warp')

        if (this.props.data) {
            this.loadDataForItem(element)
        }

        this.props.onFirstRender && this.props.onFirstRender.call(this, element)

    },

    loadDataForItem(element){

        var data = this.props.data || ElasticStack.getDefaultData()

        if (currentDataIndex === data.length) {
            currentDataIndex = 0
        }

        element.innerHTML = data[currentDataIndex]
        currentDataIndex++
    },

    executeBack(){

        isDrag = false

        if (!isAway) {
            var item1 = React.findDOMNode(this.refs.item1)
            var item2 = React.findDOMNode(this.refs.item2)
            var item3 = React.findDOMNode(this.refs.item3)

            item1.classList.add('animate')
            item1.classList.add('move-back')

            if (item1) {
                setTransformStyle(item1, 'translate3d(0,0,0) rotateZ(0deg)')
            }

            if (item2) {
                item2.classList.add('animate')
                item2.classList.add('move-back')
                setTransformStyle(item2, 'translate3d(0,0,0) rotateZ(2deg)')
            }

            if (item3) {
                item3.classList.add('animate')
                item3.classList.add('move-back')

                setTransformStyle(item3, 'translate3d(0,0,0) rotateZ(-3deg)')
            }
        }

    },

    _moveAway(){

        isDrag = false
        isAway = true

        var item1 = React.findDOMNode(this.refs.item1)
        var item2 = React.findDOMNode(this.refs.item2)
        var item3 = React.findDOMNode(this.refs.item3)

        var Yval = this._getTranslateVal()

        item1.classList.add('animate')

        item2.classList.add('animate')
        item2.classList.add('move-back')

        item3.classList.add('animate')
        item3.classList.add('move-back')

        if (item1) {
            item1.classList.add('ohide')

            setTransformStyle(item1, 'translate3d(' + Yval.x + 'px,' + Yval.y + 'px, 0) rotateZ(0deg)')
        }

        if (item2) {
            setTransformStyle(item2, 'translate3d(0,0,0) rotateZ(2deg)')
        }

        if (item3) {
            setTransformStyle(item3, 'translate3d(0,0,0) rotateZ(-3deg)')
        }

        var onEndTransFn = function () {
            item1.removeEventListener('webkitTransitionEnd', onEndTransFn);

            isAway = false

            setTransformStyle(item1, 'translate3d(0,0,0) rotateZ(0deg)')

            item1.classList.remove('animate')
            item1.classList.remove('move-back')
            item1.classList.remove('ohide')

            var element = item1.querySelector('.item-warp')

            if (this.props.data) {
                this.loadDataForItem(element)
            }

            this.props.onChange && this.props.onChange.call(this, element)

        }.bind(this);

        item1.addEventListener('webkitTransitionEnd', onEndTransFn);

    },

    _outOfBounds() {
        return Math.abs(disX) > distDragMax || Math.abs(disY) > distDragMax
    },

    _getTranslateVal(){
        var h = Math.sqrt(Math.pow(disX, 2) + Math.pow(disY, 2)),
            a = Math.asin(Math.abs(disY) / h) / ( Math.PI / 180 ),
            hL = h + distDragBack,
            dx = Math.cos(a * ( Math.PI / 180 )) * hL,
            dy = Math.sin(a * ( Math.PI / 180 )) * hL,
            tx = dx - Math.abs(disX),
            ty = dy - Math.abs(disY);

        return {
            x: disX > 0 ? tx : tx * -1,
            y: disY > 0 ? ty : ty * -1
        }
    },

    componentWillUnmount(){
        var node = React.findDOMNode(this.refs.elasticstack)

        node.ownerDocument.removeEventListener(touchNames[1], this.onMouseMove)
        node.ownerDocument.removeEventListener(touchNames[2], this.onMouseUp)
    }
})


function getCoordinates(event) {
    var touches = event.touches && event.touches.length ? event.touches : [event];
    var e = event.changedTouches ? event.changedTouches[0] : touches[0]
    return {
        x: e.clientX,
        y: e.clientY
    }
}


function setTransformStyle(el, tval) {
    el.style.WebkitTransform = tval;
    el.style.msTransform = tval;
    el.style.transform = tval;
}

export default ElasticStack