/**
 * A date picker component which shows a Date Picker on the screen. This class extends from
 * {@link Ext.picker.Picker} and {@link Ext.Sheet} so it is a popup.
 *
 * This component has no required configurations.
 *
 * ## Examples
 *
 * ```javascript
 * @example({ framework: 'extjs' })
 *     var datePicker = Ext.create('Ext.picker.Date');
 *     Ext.Viewport.add(datePicker);
 *     datePicker.show();
 * ```
 *
 * You may want to adjust the {@link #yearFrom} and {@link #yearTo} properties:
 *
 * ```javascript
 * @example({ framework: 'extjs' })
 *     var datePicker = Ext.create('Ext.picker.Date', {
 *         yearFrom: 2000,
 *         yearTo  : 2015
 *     });
 *     Ext.Viewport.add(datePicker);
 *     datePicker.show();
 * ```
 *
 * You can set the value of the {@link Ext.picker.Date} to the current date using `new Date()`:
 *
 * ```javascript
 * @example({ framework: 'extjs' })
 *     var datePicker = Ext.create('Ext.picker.Date', {
 *         value: new Date()
 *     });
 *     Ext.Viewport.add(datePicker);
 *     datePicker.show();
 * ```
 *
 * And you can hide the titles from each of the slots by using the {@link #useTitles} configuration:
 *
 * ```javascript
 * @example({ framework: 'extjs' })
 *     var datePicker = Ext.create('Ext.picker.Date', {
 *         useTitles: false
 *     });
 *     Ext.Viewport.add(datePicker);
 *     datePicker.show();
 * ```
 * ```javascript
 * @example({framework: 'ext-react', packages:['ext-react']})
 * import React, { Component } from 'react';
 * import { ExtContainer, ExtDatePicker, ExtButton } from '@sencha/ext-react';
 *
 * export default class MyExample extends Component {
 *     showPicker = () => this.picker.show();
 *     render() {
 *         return (
 *             <ExtContainer>
 *                 <ExtButton ui="action" handler={this.showPicker} text="Show Picker"/>
 *                 <ExtDatePicker
 *                     ref={picker => this.picker = picker}
 *                 />
 *             </ExtContainer>
 *         )
 *     }
 * }
 * ```
 * ```javascript
 * @example({framework: 'ext-angular', packages:['ext-angular']})
 * import { Component } from '@angular/core'
 * declare var Ext: any;
 *
 * @Component({
 *     selector: 'app-root-1',
 *     styles: [``],
 *     template: `
 *         <ExtContainer>
 *             <ExtButton ui="action" [handler]="this.showPicker"
 *                   text="Show Picker"></ExtButton>
 *             <ExtDatePicker
 *                 (ready)="onPickerReady($event)"
 *             ></ExtDatePicker>
 *         </ExtContainer>
 *     `
 * })
 * export class AppComponent {
 *    pickerComp;
 *
 *    onPickerReady = (event) => {
 *        this.pickerComp = event.detail.cmp;
 *    }
 *
 *    showPicker = () => {
 *        this.pickerComp.show();
 *    }
 * }
 * ```
 * ```html
 * @example({framework: 'ext-web-components', packages:['ext-web-components'], tab: 1 })
 * <ext-container>
 *     <ext-button ui="action" ontap="date.showPicker" text="Show Picker"></ext-button>
 *     <ext-datepicker
 *         onready="datepickerCmp.datepickerReady"
 *     >
 *     </ext-datepicker>
 * </ext-container>
 * ```
 * ```javascript
 * @example({framework: 'ext-web-components', packages:['ext-web-components'], tab: 2 })
 *
 * import '@sencha/ext-web-components/dist/ext-container.component';
 * import '@sencha/ext-web-components/dist/ext-datepicker.component';
 * import '@sencha/ext-web-components/dist/ext-button.component';
 *
 * export default class DatePickerComponent {
 *     datepickerReady = (event) => {
 *         this.pickerCmp = event.detail.cmp;
 *     }
 *
 *     showPicker = () => {
 *         this.pickerCmp.show();
 *     }
 * }
 * window.datepickerCmp = new DatePickerComponent();
 *```
 *
 */
Ext.define('Ext.picker.Date', {
    extend: 'Ext.picker.Picker',
    xtype: 'datepicker',
    alternateClassName: 'Ext.DatePicker',
    requires: ['Ext.util.InputBlocker'],
 
    /**
     * @event change
     * Fired when the value of this picker has changed and the done button is pressed.
     * @param {Ext.picker.Date} this This Picker
     * @param {Date} value The date value
     */
 
    config: {
        /**
         * @cfg {Number} yearFrom
         * The start year for the date picker. If {@link #yearFrom} is greater than
         * {@link #yearTo} then the order of years will be reversed.
         * @accessor
         */
        yearFrom: 1980,
 
        /**
         * @cfg {Number} [yearTo=new Date().getFullYear()]
         * The last year for the date picker. If {@link #yearFrom} is greater than
         * {@link #yearTo} then the order of years will be reversed.
         * @accessor
         */
        yearTo: new Date().getFullYear(),
 
        /**
         * @cfg {String} monthText
         * The label to show for the month column.
         * @accessor
         */
        monthText: 'Month',
 
        /**
         * @cfg {String} dayText
         * The label to show for the day column.
         * @accessor
         */
        dayText: 'Day',
 
        /**
         * @cfg {String} yearText
         * The label to show for the year column.
         * @accessor
         */
        yearText: 'Year',
 
        /**
         * @cfg {Array} slotOrder
         * An array of strings that specifies the order of the slots.
         * @accessor
         */
        slotOrder: ['month', 'day', 'year'],
 
        /**
         * @cfg {Object/Date} value
         * Default value for the field and the internal {@link Ext.picker.Date} component. Accepts
         * an object of 'year', 'month' and 'day' values, all of which should be numbers, or a
         * {@link Date}.
         *
         * Examples:
         *
         * - `{year: 1989, day: 1, month: 5}` = 1st May 1989
         * - `new Date()` = current date
         * @accessor
         */
 
        /**
         * @cfg {Array} slots
         * @hide
         * @accessor
         */
 
        /**
         * @cfg {String/Mixed} doneButton
         * Can be either:
         *
         * - A {String} text to be used on the Done button.
         * - An {Object} as config for {@link Ext.Button}.
         * - `false` or `null` to hide it.
         * @accessor
         */
        doneButton: true
    },
 
    initialize: function() {
        var me = this;
 
        me.callParent();
 
        me.on({
            scope: me,
            delegate: '> slot',
            slotpick: me.onSlotPick
        });
    },
 
    setValue: function(value, animated) {
        var me = this;
 
        if (Ext.isDate(value)) {
            value = {
                day: value.getDate(),
                month: value.getMonth() + 1,
                year: value.getFullYear()
            };
        }
 
        me.callParent([value, animated]);
 
        if (me.rendered) {
            me.onSlotPick();
        }
 
        return me;
    },
 
    getValue: function(useDom) {
        var values = {},
            items = this.getItems().items,
            ln = items.length,
            daysInMonth, day, month, year, item, i;
 
        for (= 0; i < ln; i++) {
            item = items[i];
 
            if (item.isSlot) {
                values[item.getName()] = item.getValue(useDom);
            }
        }
 
        // if all the slots return null, we should not return a date
        if (values.year === null && values.month === null && values.day === null) {
            return null;
        }
 
        year = Ext.isNumber(values.year) ? values.year : 1;
        month = Ext.isNumber(values.month) ? values.month : 1;
        day = Ext.isNumber(values.day) ? values.day : 1;
 
        if (month && year && month && day) {
            daysInMonth = this.getDaysInMonth(month, year);
        }
 
        day = (daysInMonth) ? Math.min(day, daysInMonth) : day;
 
        return new Date(year, month - 1, day);
    },
 
    /**
     * Updates the yearFrom configuration
     */
    updateYearFrom: function() {
        if (this.initialized) {
            this.createSlots();
        }
    },
 
    /**
     * Updates the yearTo configuration
     */
    updateYearTo: function() {
        if (this.initialized) {
            this.createSlots();
        }
    },
 
    /**
     * Updates the monthText configuration
     */
    updateMonthText: function(newMonthText, oldMonthText) {
        var innerItems = this.getInnerItems,
            ln = innerItems.length,
            item, i;
 
        // loop through each of the current items and set the title on the correct slice
        if (this.initialized) {
            for (= 0; i < ln; i++) {
                item = innerItems[i];
 
                if ((typeof item.title === "string" && item.title === oldMonthText) ||
                    (item.title.html === oldMonthText)) {
                    item.setTitle(newMonthText);
                }
            }
        }
    },
 
    /**
     * Updates the {@link #dayText} configuration.
     */
    updateDayText: function(newDayText, oldDayText) {
        var innerItems = this.getInnerItems,
            ln = innerItems.length,
            item, i;
 
        // loop through each of the current items and set the title on the correct slice
        if (this.initialized) {
            for (= 0; i < ln; i++) {
                item = innerItems[i];
 
                if ((typeof item.title === "string" && item.title === oldDayText) ||
                    (item.title.html === oldDayText)) {
                    item.setTitle(newDayText);
                }
            }
        }
    },
 
    /**
     * Updates the yearText configuration
     */
    updateYearText: function(yearText) {
        var innerItems = this.getInnerItems,
            ln = innerItems.length,
            item, i;
 
        // loop through each of the current items and set the title on the correct slice
        if (this.initialized) {
            for (= 0; i < ln; i++) {
                item = innerItems[i];
 
                if (item.title === this.yearText) {
                    item.setTitle(yearText);
                }
            }
        }
    },
 
    /**
     * @private
     */
    constructor: function() {
        this.callParent(arguments);
        this.createSlots();
    },
 
    /**
     * Generates all slots for all years specified by this component, and then sets them on the
     * component.
     * @private
     */
    createSlots: function() {
        var me = this,
            slotOrder = me.getSlotOrder(),
            yearsFrom = me.getYearFrom(),
            yearsTo = me.getYearTo(),
            years = [],
            days = [],
            months = [],
            slots = [],
            reverse = yearsFrom > yearsTo,
            ln, i, daysInMonth;
 
        while (yearsFrom) {
            years.push({
                text: yearsFrom,
                value: yearsFrom
            });
 
            if (yearsFrom === yearsTo) {
                break;
            }
 
            if (reverse) {
                yearsFrom--;
            }
            else {
                yearsFrom++;
            }
        }
 
        daysInMonth = me.getDaysInMonth(1, new Date().getFullYear());
 
        for (= 0; i < daysInMonth; i++) {
            days.push({
                text: i + 1,
                value: i + 1
            });
        }
 
        for (= 0, ln = Ext.Date.monthNames.length; i < ln; i++) {
            months.push({
                text: Ext.Date.monthNames[i],
                value: i + 1
            });
        }
 
        slotOrder.forEach(function(item) {
            slots.push(me.createSlot(item, days, months, years));
        });
 
        me.setSlots(slots);
    },
 
    /**
     * Returns a slot config for a specified date.
     * @private
     */
    createSlot: function(name, days, months, years) {
        var me = this,
            result;
 
        switch (name) {
            case 'year':
                result = {
                    name: 'year',
                    align: 'center',
                    data: years,
                    title: me.getYearText(),
                    flex: 3
                };
                break;
 
            case 'month':
                result = {
                    name: name,
                    align: 'right',
                    data: months,
                    title: me.getMonthText(),
                    flex: 4
                };
                break;
 
            case 'day':
                result = {
                    name: 'day',
                    align: 'center',
                    data: days,
                    title: me.getDayText(),
                    flex: 2
                };
        }
 
        if (me._value) {
            result.value = me._value[name];
        }
 
        return result;
    },
 
    onSlotPick: function() {
        var me = this,
            addDays = [],
            value, daySlot, valueField, dayStore, dayData,
            daysInMonth, slotCount, year, month, i, spliceArgs;
 
        if (me.isConfiguring) {
            return;
        }
 
        value = me.getValue(true);
        daySlot = me.getDaySlot();
 
        me.callParent(arguments);
 
        // This method only deals with number of days adjustments
        // if we have no daySlot there is nothing to be done.
        if (!daySlot) {
            return;
        }
 
        valueField = daySlot.getValueField();
        dayStore = daySlot.getStore();
        dayData = dayStore.getData();
        slotCount = dayStore.getCount();
        year = value.getFullYear();
        month = value.getMonth();
 
        // Get the new days of the month for this new date
        daysInMonth = me.getDaysInMonth(month + 1, year);
 
        // We don't need to update the slot days unless it has changed
        if (slotCount === daysInMonth) {
            return;
        }
 
        // Need a few more days, eg, we've gone from Feb to Mar
        // We need to create 29, 30 and 31
        if (daysInMonth > slotCount) {
            for (= slotCount; i < daysInMonth; i++) {
                addDays.push(dayStore.createModel({
                    text: i + 1,
                    value: i + 1
                }));
            }
 
            spliceArgs = [slotCount, 0, addDays];
        }
        else {
            spliceArgs = [daysInMonth, 5];
        }
 
        // Splice day store to correct length
        dayData.splice.apply(dayData, spliceArgs);
 
        // Now we have the correct amount of days for the day slot, lets update it
        i = dayStore.find(valueField, value.getDate());
 
        if (> -1) {
            daySlot.navigateToItem(daySlot.mapToItem(i));
            daySlot.setValue(daySlot.getValue(true));
        }
    },
 
    getDaySlot: function() {
        var innerItems = this.getInnerItems(),
            ln = innerItems.length,
            i, slot;
 
        if (this.daySlot) {
            return this.daySlot;
        }
 
        for (= 0; i < ln; i++) {
            slot = innerItems[i];
 
            if (slot.isSlot && slot.getName() === "day") {
                this.daySlot = slot;
 
                return slot;
            }
        }
 
        return null;
    },
 
    /**
     * @private
     */
    getDaysInMonth: function(month, year) {
        var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
 
        return month === 2 && this.isLeapYear(year) ? 29 : daysInMonth[month - 1];
    },
 
    /**
     * @private
     */
    isLeapYear: function(year) {
        return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
    },
 
    onDoneButtonTap: function() {
        var me = this,
            oldValue = me._value,
            newValue = me.getValue(true),
            ownerField = me.ownerField,
            testValue = newValue;
 
        if (Ext.isDate(newValue)) {
            testValue = newValue.toDateString();
        }
 
        if (Ext.isDate(oldValue)) {
            oldValue = oldValue.toDateString();
        }
 
        if (testValue !== oldValue) {
            if (ownerField) {
                ownerField.onPickerChange(me, newValue);
            }
 
            me.fireEvent('change', me, newValue);
        }
 
        me.hide();
 
        Ext.util.InputBlocker.unblockInputs();
    }
});