import React from 'react';
import ReactDOM from 'react-dom';
import { VelocityTransitionGroup } from 'velocity-react';
import Button from '../button';
import { startOfDate } from '../../etc/date-helpers';
import { date as formatDate } from '../../etc/format';
import './date-picker.less';

/**\

    title: Shortcut
    category: Atom
    description: A shortcut button for use in the date picker component.
    props:
        name[String]: the name of the button
        buttonType[String]: style passed onto buttons' type property.
        rangeStart[Date]: the start date of the date range. required.
        rangeEnd[Date]: the end date of the date range. optional.
        onClick[Function]:
            the callback when the button is clicked. it is invoked with arg
            { start, end } where start and end correspond to the date range.
            optional.

\**/
class Shortcut extends React.Component {
    static propTypes = {
        name: React.PropTypes.string.isRequired,
        rangeStart: React.PropTypes.instanceOf(Date).isRequired,
        rangeEnd: React.PropTypes.instanceOf(Date),
        onClick: React.PropTypes.func,
        buttonType: React.PropTypes.string
    };

    render() {
        const { name, onClick, rangeStart, rangeEnd, buttonType } = this.props,
            selection = {
                start: startOfDate(rangeStart),
                end: startOfDate(rangeEnd)
            };

        return (
            <Button type={ buttonType }
                click={ onClick.bind(this, selection) }>
                { name }
            </Button>
        );
    }
}

/**\

    title: DatePicker
    category: Atom
    description:
        A date picker that renders child component when open.
        when closed, the component will render a button with currently selected
        date(s). When open, the child component will be rendered.
    props:
        placeholder[String]: text to show when no selection has been made
        buttonType[String]: style passed onto buttons' type property.
        initialRangeStart[Date]:
            the start date of the date range to initialize the date picker with
        initialRangeEnd[Date]:
            the end date of the date range to initialize the date picker with
        shortcuts[Array]:
            an array of shortcuts. Each element should have the format of
            obj { name: '7 Days', rangeStart: <Date>, rangeEnd: <Date> }
        startsOn[Number]: the day that the week starts on (0 = Sunday)
        align[String]: align the dropdown "left" or "right". default "left"
        onOpen[Function]: a callback for when the picker is opened
        onClose[Function]: a callback for when the picker is closed
        onSelectDates[Function]:
            a callback for when dates are selected. the callback is invoked
            with { start, end }, where each are Dates and end may be null
    notes:
        The child component will be passed the following props:
            initialRangeStart[Date]:
                The currently selected dates when Picker is mounted
                This will always be a Date
            initialRangeEnd[Date]:
                The currently selected dates when Picker is mounted
                This will be a Date or may be null
            startsOn[Number]:
                Day of the week that the calendar starts on. 0 being sunday
            onSelectDates[Function]:
                A function invoked with selected dates in the format of
                { start, end } where end = null means a single date selected

\**/
class DatePicker extends React.Component {
    static defaultProps = {
        startsOn: 0,
        shortcuts: [],
        placeholder: 'Pick Date',
        initialRangeStart: null,
        initialRangeEnd: null,
        align: 'left',
        buttonType: 'grayscale'
    };

    static propTypes = {
        placeholder: React.PropTypes.string,
        initialRangeStart: React.PropTypes.instanceOf(Date),
        initialRangeEnd: React.PropTypes.instanceOf(Date),
        shortcuts: React.PropTypes.arrayOf(React.PropTypes.shape({
            name: React.PropTypes.string.isRequired,
            rangeStart: React.PropTypes.instanceOf(Date).isRequired,
            rangeEnd: React.PropTypes.instanceOf(Date)
        })),
        startsOn: React.PropTypes.oneOf([ 0, 1, 2, 3, 4, 5, 6 ]),
        onSelectDates: React.PropTypes.func,
        onOpen: React.PropTypes.func,
        onClose: React.PropTypes.func,
        align: React.PropTypes.oneOf([ 'left', 'right' ]),
        buttonType: React.PropTypes.string
    };

    state = {
        open: false,
        currentRangeStart: null,
        currentRangeEnd: null
    }

    constructor(props) {
        const { initialRangeStart, initialRangeEnd } = props;

        super(props);

        this.state.currentRangeStart = startOfDate(initialRangeStart);
        this.state.currentRangeEnd = startOfDate(initialRangeEnd);
    }

    componentDidMount() {
        this.node = ReactDOM.findDOMNode(this);
        this.attachListeners();
    }

    attachListeners() {
        /* istanbul ignore next: tested manually */
        const keydownListener = (event) => {
            if (event.key === 'Escape' || event.keyCode === 27) {
                this.closeDatePicker();
            }
        };

        /* istanbul ignore next: tested manually */
        const clickOutsideListener = (event) => {
            if (!this.node.contains(event.target)) {
                this.closeDatePicker();
            }
        };

        window.addEventListener('keydown', keydownListener, true);
        window.addEventListener('click', clickOutsideListener, true);
    }

    onSelectDates(dates) {
        const { onSelectDates } = this.props;

        this.setState({
            currentRangeStart: dates.start,
            currentRangeEnd: dates.end
        });

        this.closeDatePicker();

        /* istanbul ignore else: checked by proptypes */
        if (typeof onSelectDates === 'function') {
            onSelectDates(dates);
        }
    }

    openDatePicker() {
        const { onOpen } = this.props;

        /* istanbul ignore if: shouldn't happen */
        if (this.state.open) {
            return;
        }

        this.setState({
            open: true
        });

        if (typeof onOpen === 'function') {
            onOpen();
        }
    }

    closeDatePicker() {
        const { onClose } = this.props;

        /* istanbul ignore if: shouldn't happen */
        if (!this.state.open) {
            return;
        }

        this.setState({
            open: false
        });

        if (typeof onClose === 'function') {
            onClose();
        }
    }

    renderClosed() {
        const { currentRangeStart: start, currentRangeEnd: end } = this.state,
            { buttonType, placeholder } = this.props;
        let format = '%b %c', // "Jan 15"
            classer = 'current',
            text;

        if (!end) {
            format = '%B %c, %Y'; // "January 15, 2016"
        } else if (start.getFullYear() !== end.getFullYear()) { // in diff year
            format = '%n/%d/%y'; // "01/15/16"
            classer += ' sm';
        }

        text = start ? formatDate(start, format) : placeholder;

        if (end) {
            text += ` - ${formatDate(end, format)}`;
        }

        return (
            <div className={ classer }>
                <Button
                    key="open"
                    type={ buttonType }
                    click={ this.openDatePicker.bind(this) }>
                    { text }
                </Button>
            </div>
        );
    }

    renderControls() {
        const { shortcuts, buttonType } = this.props;
        let dateRangeShortcuts,
            type = 'exit';

        if (shortcuts) {
            dateRangeShortcuts = shortcuts.map((shortcut, key) => {
                return (
                    <Shortcut key={ key }
                        buttonType={ buttonType }
                        onClick={ this.onSelectDates.bind(this) }
                        { ...shortcut } />
                );
            });
        }

        if (buttonType) {
            type += ` ${buttonType}`;
        }

        return (
            <div className="controls">
                <Button
                    key="exit"
                    type={ type }
                    click={ this.closeDatePicker.bind(this, null) } />
                <div className="shortcuts">
                    { dateRangeShortcuts }
                </div>
            </div>
        );
    }

    renderPicker() {
        const { currentRangeStart, currentRangeEnd } = this.state,
            { startsOn } = this.props;

        // takes child component and passes in several props
        const picker = React.cloneElement(
            React.Children.only(this.props.children),
            {
                initialRangeStart: currentRangeStart,
                initialRangeEnd: currentRangeEnd,
                startsOn: startsOn,
                onSelectDates: this.onSelectDates.bind(this)
            }
        );

        return (
            <div className="custom">
                { picker }
            </div>
        );
    }

    render() {
        const { open } = this.state;
        let classer = `date-picker ${this.props.align}`;

        if (open) {
            classer += ' open';
        }

        return (
            <div className={ classer }>
                { open ? this.renderControls() : this.renderClosed() }
                <VelocityTransitionGroup
                    enter={ { animation: 'fadeIn', duration: 250 } }
                    leave={ { animation: 'fadeOut', duration: 250 } }>
                    { open ? this.renderPicker() : null }
                </VelocityTransitionGroup>
            </div>
        );
    }
}

export default DatePicker;
