/** @jsx React.DOM */

'use strict';

var React = require('react');


var AnimatableComponent = React.createClass({
    propTypes: {
        tag: React.PropTypes.component.isRequired,
        animationClass: React.PropTypes.string.isRequired,
        key: React.PropTypes.string,
        shouldStartAnimation: React.PropTypes.bool,
        removeOnComplete: React.PropTypes.bool,
        className: React.PropTypes.string,
        onAnimationStart: React.PropTypes.func,
        onAnimationIteration: React.PropTypes.func,
        onAnimationEnd: React.PropTypes.func
    },
    getDefaultProps: function() {
        return {
            tag: React.DOM.div,
            animationClass: '',
            className: '',
            removeOnComplete: true
        };
    },
    getInitialState: function() {
        return {
            applyAnimationClass: false
        };
    },
    componentWillMount: function() {
        this.isAnimating = false;
        this.animationStartTime = 0;
        this.animationElapsedTime = 0;
        this.animationIterationCount = 0;
    },
    componentDidMount: function() {
        var node = this.refs.el.getDOMNode();
        this.prefixEventHandler(node, 'AnimationStart', this.handleAnimationStart);
        this.prefixEventHandler(node, 'AnimationEnd', this.handleAnimationEnd);
        this.prefixEventHandler(node, 'AnimationIteration', this.handleAnimationIteration);
    },
    componentWillReceiveProps: function(nextProps) {
        this.setState({
            applyAnimationClass: nextProps.shouldStartAnimation
        });
    },
    handleAnimationStart: function(e) {
        if(e.animationClass === this.props.animationClass) {
            this.isAnimating = true;
            this.animationStartTime = Date.now();
            this.animationElapsedTime = 0;
            this.animationIterationCount = 0;
            e.stopPropagation();
            if(this.props.onAnimationStart) {
                this.props.onAnimationStart(this, e);
            }
        }
    },
    handleAnimationIteration: function(e) {
        if(e.animationClass === this.props.animationClass) {
            e.stopPropagation();
            this.animationIterationCount += 1;
            this.animationElapsedTime = e.elapsedTime;
            if(this.props.onAnimationIteration) {
                this.props.onAnimationIteration(this, e);
            }
        }
    },
    handleAnimationEnd: function(e) {
        if(e.animationClass === this.props.animationClass) {
            e.stopPropagation();
            this.isAnimating = false;
            this.animationElapsedTime = e.elapsedTime;
            if(this.props.removeOnComplete) {
                this.setState({ applyAnimationClass: false });
            }
            if(this.props.onAnimationEnd) {
                this.props.onAnimationEnd(this, e);
            }
        }
    },
    prefixEventHandler: function(node, name, handler, remove) {
        var prefixes = ['webkit', 'moz', 'MS', 'o', ''];
        for(var i = 0; i < prefixes.length; i++) {
            var eventName = (prefixes[i] === '') ? name.toLowerCase() : prefixes[i] + name;
            if(!remove) {
                node.addEventListener(eventName, handler);
            }
            else {
                node.removeEventListener(eventName, handler);
            }
        }
    },
    componentWillUnmount: function() {
        var node = this.refs.el.getDOMNode();
        this.prefixEventHandler(node, 'AnimationStart', this.handleAnimationStart, true);
        this.prefixEventHandler(node, 'AnimationEnd', this.handleAnimationEnd, true);
        this.prefixEventHandler(node, 'AnimationIteration', this.handleAnimationIteration, true);
    },
    render: function() {
        var fullClass = this.props.className;
        if(this.state.applyAnimationClass) {
            fullClass += ' ' + this.props.animationClass;
        }
        return ( this.props.tag({
            ref: 'el',
            className: fullClass.trim(),
            key: this.props.key
        }, this.props.children ));
    }
});

module.exports = AnimatableComponent;