import React from 'react';
import './multi-select.less';


/**\

    title: MultiSelectOption
    category: atom
    description: A stateless component to render a multi select option
    props:
        value[Any]:
            The value representing the option. Should be unique among sublings
        title[String]: The title to display for the option
        onClick[Function]: A callback triggered when the option is clicked
        isSelected[Boolean]: Whether or not this option is actively selected

\**/
class MultiSelectOption extends React.Component {
    static propTypes = {
        value: React.PropTypes.any.isRequired,
        title: React.PropTypes.string.isRequired,
        onClick: React.PropTypes.func,
        isSelected: React.PropTypes.bool.isRequired
    };

    onClick = () => {
        const { onClick, value } = this.props;

        /* istanbul ignore else: only used internally */
        if (typeof onClick === 'function') {
            onClick(value);
        }
    }

    render() {
        const { isSelected, title } = this.props;
        let classer = 'option';

        /* istanbul ignore else: just a check */
        if (isSelected) {
            classer += ' selected';
        }

        return (
            <div className={ classer }
                tabIndex={ -1 }
                onClick={ this.onClick }>
                { title }
            </div>
        );
    }
}


/**\

    title: MultiSelect
    category: atom
    description: A multiselect for options
    props:
        className[String]:
            Any arbitrary class names to pass along to the component
        options[Array]:
            an array of options that can be selected with shape { value, title }
        selected[Array]:
            a set of values that should correspond to the values of selected
            options
        onSelection[Function]:
            A callback trigged by a change in selection. It is passed an array
            of values that correspond to option values

\**/
class MultiSelect extends React.Component {
    static propTypes = {
        onSelection: React.PropTypes.func,
        selected: React.PropTypes.oneOfType([
            React.PropTypes.instanceOf(Set),
            React.PropTypes.array
        ]).isRequired,
        options: React.PropTypes.arrayOf(React.PropTypes.shape({
            value: React.PropTypes.any.isRequired,
            title: React.PropTypes.string.isRequired
        })).isRequired
    };

    static defaultProps = {
        options: [],
        selected: new Set()
    };

    state = {
        selected: new Set()
    }

    constructor(props) {
        super(props);
        this.state.selected = new Set(props.selected);
    }

    componentWillReceiveProps(props) {
        /* istanbul ignore else: just a type conversion */
        this.setState({
            selected: new Set(props.selected)
        });
    }

    onClickOption = (value) => {
        const { onSelection } = this.props,
            { selected } = this.state,
            nextSelected = new Set(selected);

        if (nextSelected.has(value)) {
            nextSelected.delete(value);
        } else {
            nextSelected.add(value);
        }

        this.setState({
            selected: nextSelected
        });

        /* istanbul ignore else: just a check */
        if (typeof onSelection === 'function') {
            onSelection(new Set(nextSelected));
        }
    }

    render() {
        const classer = [ 'multi-select' ],
            { className, options } = this.props,
            { selected } = this.state;

        if (className) {
            classer.push(className);
        }

        return (
            <div className={ classer.join(' ') }>
                {
                    options.map((option) =>
                        <MultiSelectOption
                            key={ option.value }
                            value={ option.value }
                            title={ option.title }
                            isSelected={ selected.has(option.value) }
                            onClick={ this.onClickOption } />
                    )
                }
            </div>
        );
    }
}

export default MultiSelect;
