import React, { PropTypes } from 'react';
import classSet from 'classnames';
import Popup from 'react-bem-components/lib/Popup';

var Suggest = React.createClass({
    displayName: 'Suggest',

    getInitialState() {
        var { value } = this.props;

        return {
            value: value || '',
            focusedIndex: -1,
            sections: []
        };
    },

    componentDidMount() {
        var { input, popup } = this.refs;

        popup.setAnchor(input);
    },

    componentWillUnmount() {
        clearTimeout(this._searchTimeout);
    },

    /**
     * Устанавливает фокус на поле ввода
     * @method focus
     */
    focus() {
        this.refs.input.focus();
    },

    _updateMenuHeight() {
        var { popup, menu } = this.refs;

        var drawingParams = popup.block.calcPossibleDrawingParams();

        var menuWidth = menu.offsetWidth;
        var bestHeight = 0;

        drawingParams.forEach((params) => {
            if (params.width >= menuWidth && params.height > bestHeight) {
                bestHeight = params.height;
            }
        });

        if (bestHeight) {
            menu.style.maxHeight = bestHeight + 'px';
        }
    },

    _openPopup() {
        this._updateMenuHeight();

        this.refs.popup.open();
    },

    _closePopup() {
        this.refs.popup.close();
    },

    _search(value) {
        var { source, timeout } = this.props;
        var { popup } = this.refs;

        clearTimeout(this._searchTimeout);

        this._searchTimeout = setTimeout(() => {
            source(value, (error, sections) => {
                if (this._lastValue !== value) {
                    return;
                }

                if (error) {
                    throw error;
                }

                this._totalCount = sections.reduce((total, section) =>
                    total + section.suggestions.length, 0);

                this.setState({
                    sections,
                    focusedIndex: -1
                }, () => {
                    popup.block.redraw();

                    if (sections.length) {
                        this._openPopup();
                    } else {
                        this._closePopup();
                    }
                });
            });
        }, timeout);
    },

    _selectItem() {
        var { focusedIndex, sections } = this.state;
        var { onSelect } = this.props;


        if (focusedIndex === -1) {
            return;
        }

        var index = 0;

        sections.forEach((section) => {
            section.suggestions.forEach((item) => {
                if (index++ === focusedIndex && onSelect) {
                    onSelect(item);
                }
            });
        });

        this._reset();
    },

    _reset() {
        this.setState({
            focusedIndex: -1,
            sections: [],
            value: ''
        });

        this._closePopup();
    },

    _prevItem() {
        var { focusedIndex } = this.state;

        if (focusedIndex-- <= 0) {
            focusedIndex = this._totalCount - 1;
        }

        this.setState({ focusedIndex });
    },

    _nextItem() {
        var { focusedIndex } = this.state;

        if (focusedIndex++ >= this._totalCount - 1) {
            focusedIndex = 0;
        }

        this.setState({ focusedIndex });
    },

    _showWhen(value) {
        value = String(value).replace(/^\s+/, '');

        return value.length >= this.props.minLength;
    },

    _handleInputKeyDown(event) {
        var shouldPreventDefault = false;

        switch (event.keyCode) {
        case 13:
            this._selectItem();

            shouldPreventDefault = true;

            break;
        case 27:
            this._reset();

            shouldPreventDefault = true;

            break;
        case 38:
            this._prevItem();

            shouldPreventDefault = true;

            break;

        case 40:
            this._nextItem();

            shouldPreventDefault = true;

            break;
        }

        if (shouldPreventDefault) {
            event.preventDefault();
        }

        var { onKeyDown } = this.props;

        if (onKeyDown) {
            onKeyDown(event);
        }
    },

    _handleInputFocus(event) {
        if (this.state.sections.length) {
            this._openPopup();
        }

        var { onFocus } = this.props;

        if (onFocus) {
            onFocus(event);
        }
    },

    _handleInputChange() {
        var { value } = this.refs.input;

        this._lastValue = value;

        var nextState = { value };

        if (this._showWhen(value)) {
            this._search(value);
        } else {
            nextState.sections = [];
            nextState.focusedIndex = -1;
        }

        this.setState(nextState);
    },

    _handleItemClick(item, event) {
        event.preventDefault();

        var { onSelect } = this.props;

        if (onSelect) {
            onSelect(item);
        }

        this._reset();
    },

    _renderSections(sections) {
        var itemIndex = 0;

        return sections.map((section, index) => (
            <div className="suggest-section"
                key={'section' + index}>
                <div className="suggest-section__name">
                    {section.sectionName}
                </div>
                <div className="suggest-section__items">
                    {section.suggestions.map((item) =>
                        this._renderItem(item, itemIndex++))}
                </div>
            </div>
        ));
    },

    _renderItem(item, index) {
        var isActive = this.state.focusedIndex === index;

        var className = classSet({
            'suggest-item': true,
            'suggest-item_focused': isActive
        });

        return (
            <div className={className}
                key={'item' + index}
                onClick={this._handleItemClick.bind(null, item)}>
                {this.props.renderItem(item, isActive)}
            </div>
        );
    },

    render() {
        var { value, sections } = this.state;
        var props = this.props;

        var className = classSet({
            suggest: true,
            [ 'suggest_size_' + props.size ]: props.size,
            [ 'suggest_width_' + props.width ]: true,
            [ 'suggest_transparent' ]: props.transparent
        });

        return (
            <div className={className}>
                <input className="suggest__control"
                    disabled={props.disabled}
                    name={props.name}
                    placeholder={props.placeholder}
                    ref="input"
                    onChange={this._handleInputChange}
                    onKeyDown={this._handleInputKeyDown}
                    onFocus={this._handleInputFocus}
                    onBlur={props.onBlur}
                    value={value}
                    autoComplete="off"
                    role="combobox" />
                <Popup closable={true}
                    ref="popup"
                    target="anchor"
                    viewportOffset={50}>
                    <div className="suggest-menu"
                        ref="menu">
                        {this._renderSections(sections)}
                    </div>
                </Popup>
            </div>
        );
    }
});

// @if NODE_ENV='development'
Suggest.propTypes = {
    disabled: PropTypes.bool,
    defaultValue: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.number
    ]),
    id: PropTypes.string,
    onBlur: PropTypes.func,
    onSelect: PropTypes.func,
    onFocus: PropTypes.func,
    onKeyDown: PropTypes.func,
    placeholder: PropTypes.string,
    renderItem: PropTypes.func,
    source: PropTypes.func.isRequired,
    timeout: PropTypes.number,
    transparent: PropTypes.bool,
    minLength: PropTypes.number,
    name: PropTypes.string,
    size: PropTypes.oneOf([ 's', 'm', 'l', 'xl' ]),
    value: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.number
    ]),
    width: PropTypes.oneOf([ 'available' ])
};
// @endif

Suggest.defaultProps = {
    timeout: 200,
    minLength: 2,
    size: 'm'
};

export default Suggest;
