'use strict';

Object.defineProperty(exports, "__esModule", {
    value: true
});

var _jsx = function () { var REACT_ELEMENT_TYPE = typeof Symbol === "function" && Symbol.for && Symbol.for("react.element") || 0xeac7; return function createRawReactElement(type, props, key, children) { var defaultProps = type && type.defaultProps; var childrenLength = arguments.length - 3; if (!props && childrenLength !== 0) { props = {}; } if (props && defaultProps) { for (var propName in defaultProps) { if (props[propName] === void 0) { props[propName] = defaultProps[propName]; } } } else if (!props) { props = defaultProps || {}; } if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { var childArray = Array(childrenLength); for (var i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 3]; } props.children = childArray; } return { $$typeof: REACT_ELEMENT_TYPE, type: type, key: key === undefined ? null : '' + key, ref: null, props: props, _owner: null }; }; }();

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _reactDom = require('react-dom');

var _reactDom2 = _interopRequireDefault(_reactDom);

var _raf = require('raf');

var _raf2 = _interopRequireDefault(_raf);

var _addEventListener = require('../helpers/addEventListener');

var _addEventListener2 = _interopRequireDefault(_addEventListener);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var PRESETS = {
    x: {
        'outside-left': function outsideLeft(targetRect, placeableRect, rootRect, offsets) {
            return {
                left: targetRect.left - placeableRect.width - rootRect.left + offsets.left
            };
        },
        'outside-right': function outsideRight(targetRect, placeableRect, rootRect, offsets) {
            return {
                left: targetRect.left + targetRect.width - rootRect.left + offsets.left
            };
        },
        'middle': function middle(targetRect, placeableRect, rootRect, offsets) {
            return {
                left: targetRect.left + targetRect.width / 2 - placeableRect.width / 2 - rootRect.left + offsets.left
            };
        },
        'inside-left': function insideLeft(targetRect, placeableRect, rootRect, offsets) {
            return {
                left: targetRect.left - rootRect.left + offsets.left
            };
        },
        'inside-right': function insideRight(targetRect, placeableRect, rootRect, offsets) {
            return {
                left: targetRect.left + targetRect.width - placeableRect.width - rootRect.left + offsets.left
            };
        }
    },
    y: {
        'outside-top': function outsideTop(targetRect, placeableRect, rootRect, offsets) {
            return {
                top: targetRect.top - placeableRect.height - rootRect.top + offsets.top
            };
        },
        'outside-bottom': function outsideBottom(targetRect, placeableRect, rootRect, offsets) {
            return {
                top: targetRect.top + targetRect.height - rootRect.top + offsets.top
            };
        },
        'middle': function middle(targetRect, placeableRect, rootRect, offsets) {
            return {
                top: targetRect.top + targetRect.height / 2 - placeableRect.height / 2 - rootRect.top + offsets.top
            };
        },
        'inside-top': function insideTop(targetRect, placeableRect, rootRect, offsets) {
            return {
                top: targetRect.top - rootRect.top + offsets.top
            };
        },
        'inside-bottom': function insideBottom(targetRect, placeableRect, rootRect, offsets) {
            return {
                top: targetRect.top + targetRect.height - placeableRect.height + -rootRect.top + offsets.top
            };
        }
    }
};

var PlacerWrapper = function (_React$Component) {
    _inherits(PlacerWrapper, _React$Component);

    function PlacerWrapper(props) {
        var _ref;

        _classCallCheck(this, PlacerWrapper);

        for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
            args[_key - 1] = arguments[_key];
        }

        var _this = _possibleConstructorReturn(this, (_ref = PlacerWrapper.__proto__ || Object.getPrototypeOf(PlacerWrapper)).call.apply(_ref, [this, props].concat(args)));

        _this.state = { currentPreset: null };
        _this._onChildrenMount = _this._onChildrenMount.bind(_this);

        if (!props.presets) {
            throw new Error('Placer component: presets property is required');
        }
        return _this;
    }

    _createClass(PlacerWrapper, [{
        key: 'componentDidMount',
        value: function componentDidMount() {
            var _this2 = this;

            var props = this.props;

            this._selfDOMNode = _reactDom2.default.findDOMNode(this);
            props.onDidMount && props.onDidMount(this);

            this._isMount = true;

            if (typeof window !== 'undefined') {
                this._resizeEventListener = (0, _addEventListener2.default)(window, 'resize', function () {
                    if (_this2._isMount) {
                        _this2.updatePosition();
                    }
                });

                this._scrollEventListener = (0, _addEventListener2.default)(window, 'scroll', function () {
                    if (_this2._isMount) {
                        _this2.updatePosition();
                    }
                });
            }
        }
    }, {
        key: 'componentWillUnmount',
        value: function componentWillUnmount() {
            this._isMount = false;
            this._resizeEventListener && this._resizeEventListener.remove();
            this._scrollEventListener && this._scrollEventListener.remove();
        }
    }, {
        key: 'getDOMNode',
        value: function getDOMNode() {
            return this._selfDOMNode || null;
        }
    }, {
        key: 'getBoundingClientRect',
        value: function getBoundingClientRect() {
            return this._selfDOMNode ? this._selfDOMNode.getBoundingClientRect() : null;
        }
    }, {
        key: 'setStyles',
        value: function setStyles(styles) {
            var _this3 = this;

            (0, _raf2.default)(function () {
                return Object.assign(_this3.getDOMNode().style, styles);
            });
        }
    }, {
        key: 'updatePosition',
        value: function updatePosition() {
            var _this4 = this;

            var args = this._getArgumentsForPositionCalculation();
            var bestPreset = this._getBestPreset.apply(this, _toConsumableArray(args));
            var position = calculatePreset.apply(undefined, [bestPreset].concat(_toConsumableArray(args)));

            var xPosition = position.left ? position.left + 'px' : 0;
            var yPosition = position.top ? position.top + 'px' : 0;
            var newStyles = {
                transform: 'translate3d(' + xPosition + ', ' + yPosition + ', 0)',
                visibility: 'visible'
            };

            if (!this._isNewPreseEqualEqula(bestPreset)) {
                this.setState({
                    currentPreset: bestPreset
                }, function () {
                    return _this4.setStyles(newStyles);
                });
            } else {
                this.setStyles(newStyles);
            }
        }
    }, {
        key: '_isNewPreseEqualEqula',
        value: function _isNewPreseEqualEqula(nextPreset) {
            var preset = this.state.currentPreset || {};

            return preset.xAxis === nextPreset.xAxis && preset.yAxis === nextPreset.yAxis && preset.offsetX === nextPreset.offsetX && preset.offsetY === nextPreset.offsetY;
        }
    }, {
        key: '_getBestPreset',
        value: function _getBestPreset(targetRect, placeableRect, rootRect) {
            var _props = this.props,
                presets = _props.presets,
                viewportAccuracyFactor = _props.viewportAccuracyFactor;


            var resultPreset = presets[0];
            var bestViewportAccuracyFactor = 0;

            if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== undefined && presets.length > 1) {
                var windowWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
                var windowHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);

                presets.some(function (preset) {
                    var position = calculatePreset(preset, targetRect, placeableRect, rootRect);
                    var rect = Object.assign({ width: placeableRect.width, height: placeableRect.height }, position);

                    var left = Math.max(rect.left, 0);
                    var right = Math.min(rect.left + rect.width, windowWidth);

                    var top = Math.max(rect.top, 0);
                    var bottom = Math.min(rect.top + rect.height, windowHeight);

                    var rectArea = rect.width * rect.height;
                    var visibleRectArea = (right - left) * (bottom - top);
                    var currentViewportAccuracyFactor = visibleRectArea / rectArea;

                    if (currentViewportAccuracyFactor >= viewportAccuracyFactor) {
                        resultPreset = preset;

                        return true;
                    }

                    if (currentViewportAccuracyFactor > bestViewportAccuracyFactor) {
                        bestViewportAccuracyFactor = currentViewportAccuracyFactor;
                        resultPreset = preset;
                    }
                });
            }

            return resultPreset;
        }
    }, {
        key: '_getArgumentsForPositionCalculation',
        value: function _getArgumentsForPositionCalculation() {
            var placeableRect = this._getPlaceableRect();
            var rootRect = this.context.teleport.getRootDOMNode().getBoundingClientRect();

            return [this.props.getTargetRect(), placeableRect, rootRect];
        }
    }, {
        key: '_getPlaceableRect',
        value: function _getPlaceableRect() {
            return this._placeableDOMNode ? this._placeableDOMNode.getBoundingClientRect() : null;
        }
    }, {
        key: '_onChildrenMount',
        value: function _onChildrenMount(component) {
            this._placeableDOMNode = _reactDom2.default.findDOMNode(component);
            component && this.updatePosition();
        }
    }, {
        key: 'render',
        value: function render() {
            var props = this.props;
            var childrenProps = { ref: this._onChildrenMount };

            if (typeof props.children.type === 'function') {
                childrenProps.currentPreset = this.state.currentPreset;
            }

            return _jsx('div', {
                style: {
                    zIndex: this.context.teleport.getContextLevel() + props.zIndex,
                    visibility: 'hidden',
                    position: 'absolute',
                    transform: 'translate3d(0, 0, 0)',
                    top: 0,
                    left: 0
                }
            }, void 0, _react2.default.cloneElement(_react2.default.Children.only(props.children), childrenProps));
        }
    }]);

    return PlacerWrapper;
}(_react2.default.Component);

PlacerWrapper.contextTypes = {
    teleport: _react.PropTypes.shape({
        move: _react.PropTypes.func,
        remove: _react.PropTypes.func,
        update: _react.PropTypes.func,
        isAdded: _react.PropTypes.func,
        getRootDOMNode: _react.PropTypes.func,
        getBoundingClientRect: _react.PropTypes.func,
        getContextLevel: _react.PropTypes.func
    })
};
exports.default = PlacerWrapper;


var calculatePreset = function calculatePreset(preset, targetRect, placeableRect, rootRect) {
    var resultOffset = { left: preset.offsetX || 0, top: preset.offsetY || 0 };

    return Object.assign({}, PRESETS.x[preset.xAxis](targetRect, placeableRect, rootRect, resultOffset), PRESETS.y[preset.yAxis](targetRect, placeableRect, rootRect, resultOffset));
};