import React from 'react';
import PropTypes from 'prop-types';


function normalizeQueries(queries) {
    if (typeof queries === 'string') {
        return queries;
    }

    return queries.join(', ');
}


/**
 * Component that only renders its children if the passed-in media query is currently met. This allows you to, for
 * example, define different layouts on mobile and desktop. The `query` props is a simple media query string. Pre-
 * defined queries for screen size can be found in `global/core/commonjs/screenSize`. Each `MediaQuery` can only
 * have one direct child or an error will be thrown.
 *
 * Example:
 * const {queries} = require('global/core/commonjs/screenSize');
 *
 * ...
 *
 * render() {
 *     const props = this.props;
 *
 *     return (
 *         <div className="layout-container">
 *             <MediaQuery query={queries.mobile}>
 *                 <LayoutMobile {...props} />
 *             </MediaQuery>
 *             <MediaQuery query={queries.tablet}>
 *                 <LayoutTablet {...props} />
 *             </MediaQuery>
 *             <MediaQuery query={queries.desktop}>
 *                 <LayoutDesktop {...props} />
 *             </MediaQuery>
 *         </div>
 *     );
 * }
 *
 * The following will exclude desktop from displaying the layout element:
 *
 * render() {
 *      return (
 *          <div className="layout-container">
 *              <MediaQuery query={queries.desktop} exclude={true}>
 *                  <LayoutNonDesktop {...props} />
 *              </MediaQuery>
 *          </div>
 *      );
 * }
 *
 */
class MediaQuery extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            matches: false
        };

        this._update = this._update.bind(this);
    }

    componentDidMount() {
        this._setQuery(normalizeQueries(this.props.query));
    }

    componentWillReceiveProps(nextProps) {
        this._setQuery(normalizeQueries(nextProps.query));
    }

    componentWillUnmount() {
        this._mql.removeListener(this._update);
    }

    _setQuery(query) {
        if (this._mql) {
            this._mql.removeListener(this._update);
        }

        const mql = this._mql = window.matchMedia(query);

        this._update(mql);
        mql.addListener(this._update);
    }

    _update(mql) {
        const matches = this.props.exclude ? !mql.matches : mql.matches;

        if (this.state.matches === matches) {
            return;
        }

        this.setState({ matches });
    }

    render() {
        if (!this.state.matches) {
            return null;
        }

        return React.Children.only(this.props.children);
    }
}

MediaQuery.propTypes = {
    query: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.arrayOf(PropTypes.string)
    ]),
    children: PropTypes.object.isRequired,
    exclude: PropTypes.bool
};

MediaQuery.defaultProps = {
    query: ''
};

export default MediaQuery;
