import ngModule from '../../module';
import FormInput from '../form-input';
import './style.scss';

/**
 * Flat calendar.
 *
 * @property {Array} defaultDate Array of events
 * @property {Function} hasEvents Determines if an events exists for a day
 * @property {Function} getClass Class to apply to specific day
 * @event selection Emitted when day or weekday is selected
 * @event move Emitted when month changes
 */
export default class Calendar extends FormInput {
    constructor($element, $scope, vnWeekDays) {
        super($element, $scope);
        this.weekDays = vnWeekDays.locales;
        this.defaultDate = new Date();
        this.displayControls = true;
    }

    /**
     * The initial date
     *
     * @return {Date} - Default date
     */
    get defaultDate() {
        return this._defaultDate;
    }

    set defaultDate(value) {
        if (value) {
            value = new Date(value);
            value.setHours(0, 0, 0, 0);
            value.setDate(1);
        }

        this._defaultDate = value;
        this.month = value.getMonth();
        this.repaint();
    }

    /**
     * Returns first day of month from a given date
     *
     * @param {Date} date - Origin date
     * @return {Integer}
     */
    firstDay(date) {
        return new Date(
            date.getFullYear(),
            date.getMonth(),
            1
        );
    }

    /**
     * Repaints the calendar.
     */
    repaint() {
        const firstWeekday = this.firstDay(this.defaultDate).getDay() - 1;
        let weekdayOffset = firstWeekday >= 0 ? firstWeekday : 6;

        let dayIndex = new Date(this.defaultDate.getTime());
        dayIndex.setDate(1 - weekdayOffset);

        this.days = [];

        for (let i = 1; i <= 42; i++) {
            this.days.push(new Date(dayIndex.getTime()));
            dayIndex.setDate(dayIndex.getDate() + 1);
        }
    }

    /**
     * Gets CSS classes to apply to the specified day.
     *
     * @param {Date} day The day
     * @return {Object} The CSS classes to apply
     */
    getDayClasses(day) {
        let wday = day.getDay();
        let month = day.getMonth();

        let classes = {
            weekend: wday === 6 || wday === 0,
            previous: month < this.month,
            current: month == this.month,
            next: month > this.month,
            event: this.hasEvents({$day: day})
        };

        let userClass = this.getClass({$day: day});
        if (userClass) classes[userClass] = true;

        return classes;
    }

    /**
     * Moves to next month(s)
     */
    moveNext() {
        this.move(1);
    }

    /**
     * Moves to previous month(s)
     */
    movePrevious() {
        this.move(-1);
    }

    /**
     * Moves @direction months backwards/forwards.
     *
     * @param {Number} direction Negative to move backwards, positive forwards
     */
    move(direction) {
        let date = new Date(this.defaultDate.getTime());
        date.setMonth(date.getMonth() + direction);
        this.defaultDate = date;

        this.repaint();
        this.emit('move', {$date: date});
    }

    /*
     * Day selection event
     */
    select($event, day) {
        if (!this.editable) return;
        this.change(day);
        this.emit('selection', {
            $event: $event,
            $days: [day],
            $type: 'day'
        });
        this.repaint();
    }

    /*
     * WeekDay selection event
     */
    selectWeekDay($event, weekday) {
        if (!this.editable) return;
        let days = [];
        for (let day of this.days) {
            if (day.getDay() === weekday && day.getMonth() == this.month)
                days.push(day);
        }
        this.field = days[0];
        this.emit('selection', {
            $event: $event,
            $days: days,
            $type: 'weekday',
            $weekday: weekday
        });
        this.repaint();
    }

    hasEvents() {
        return false;
    }

    getClass() {
        return '';
    }

    repeatLast() {
        if (!this.formatDay) return;

        let days = this.element.querySelectorAll('.days > .day');
        for (let i = 0; i < days.length; i++) {
            this.formatDay({
                $day: this.days[i],
                $element: days[i]
            });
        }
    }
}
Calendar.$inject = ['$element', '$scope', 'vnWeekDays'];

ngModule.vnComponent('vnCalendar', {
    template: require('./index.html'),
    controller: Calendar,
    bindings: {
        defaultDate: '=?',
        hasEvents: '&?',
        getClass: '&?',
        formatDay: '&?',
        displayControls: '<?',
        hideYear: '<?',
        hideContiguous: '<?'
    }
});