import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import classSet from 'classnames';
import Popup from 'ui/Popup';

import './index.css';

function getBox(element) {
    const box = {
        left: 0,
        top: 0,
        width: element.offsetWidth,
        height: element.offsetHeight,
    };

    while (element.offsetParent) {
        box.left += element.offsetLeft;
        box.top += element.offsetTop;
        element = element.offsetParent;
    }

    box.right = box.left + box.width;
    box.bottom = box.top + box.height;

    return box;
}

function getMargins(element) {
    const margins = {};
    const style = window.getComputedStyle(element);

    ['top', 'right', 'bottom', 'left'].forEach(type => {
        margins[type] = parseInt(style[`margin-${type}`], 10);
    });

    return margins;
}

function fit(element) {
    resetStyle(element);

    const elementBox = getBox(element);
    const layoutBox = getBox(document.body);

    const overlap = {
        x: elementBox.right - layoutBox.right,
        y: elementBox.bottom - layoutBox.bottom,
    };

    const shift = {};

    if (overlap.x > 0 || overlap.y > 0) {
        const elementMargins = getMargins(element);

        if (overlap.x > 0) {
            shift.x = overlap.x + 4;
            element.style.marginLeft = `${elementMargins.left - shift.x}px`;
        }

        if (overlap.y > 0) {
            shift.y = overlap.y + 4;
            element.style.marginTop = `${elementMargins.top - shift.y - elementBox.height}px`;
        }
    }

    return shift;
}

function resetStyle(element) {
    element.removeAttribute('style');
}

const Hoverable = React.createClass({

    mixins: [PureRenderMixin],

    getInitialState() {
        return { active: false };
    },

    componentDidMount() {
        const container = ReactDOM.findDOMNode(this);
        const popup = ReactDOM.findDOMNode(this.refs.popup);
        const popupContent = ReactDOM.findDOMNode(this.refs.popupContent);

        this._on = event => {
            this._cancel();

            event.preventDefault();
            this.setState({ active: true });

            const shift = fit(popup);

            this.setState({ shift });
        };

        this._off = event => {
            this._hoverTimeout = setTimeout(() => {
                event.preventDefault();
                this.setState({ active: false });
                this._hoverTimeout = null;
            }, 200);
        };

        this._cancel = () => {
            if (this._hoverTimeout) {
                clearTimeout(this._hoverTimeout);
                this._hoverTimeout = null;
            }
        };

        if (container) {
            container.addEventListener('mouseenter', this._on);
            container.addEventListener('mouseleave', this._off);
        }

        if (popupContent) {
            popupContent.addEventListener('mouseenter', this._cancel);
        }
    },

    componentWillUnmount() {
        const container = ReactDOM.findDOMNode(this);
        const popupContent = ReactDOM.findDOMNode(this.refs.popupContent);

        if (container) {
            container.removeEventListener('mouseenter', this._on);
            container.removeEventListener('mouseleave', this._off);
        }

        if (popupContent) {
            popupContent.removeEventListener('mouseenter', this._cancel);
        }
    },

    render() {
        const className = classSet({
            'ui-hoverable': true,
            'ui-hoverable_active': this.state.active,
            [`ui-hoverable_position_${this.props.position}`]: true,
        });

        return (
            <div className={className}>
                <div className="ui-hoverable__content">
                    {this.props.children}
                </div>
                <Popup
                    className="ui-hoverable__popup"
                    ref="popup"
                    tailPosition={this.state.shift}
                >
                    <div className="ui-hoverable__popup-content" ref="popupContent">
                        {this.props.hoverContent}
                    </div>
                </Popup>
            </div>
        );
    },

});

// @if NODE_ENV='development'
Hoverable.propTypes = {
    position: PropTypes.oneOf(['default']),
    hoverContent: PropTypes.any,
};
// @endif

Hoverable.defaultProps = {
    position: 'default',
};

export default Hoverable;
