/**
 * This is a specialized field which shows a {@link Ext.picker.Date} when tapped.
 * If it has a predefined value, or a value is selected in the {@link Ext.picker.Date},
 * it will be displayed like a normal {@link Ext.field.Text} (but not selectable/changable).
 *
 *     Ext.create('Ext.field.Date', {
 *         label: 'Birthday',
 *         value: new Date()
 *     });
 *
 * {@link Ext.field.Date} fields are very simple to implement, and have no required configurations.
 *
 * ## Examples
 *
 * It can be very useful to set a default {@link #value} configuration on
 * {@link Ext.field.Date} fields. In this example, we set the {@link #value} to be
 * the current date.  You can also use the {@link #setValue} method to update the value at any time.
 *
 * ```javascript
 * @example({ framework: 'extjs' })
 *     var form = Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         referenceHolder: true,
 *         items: [{
 *             xtype: 'fieldset',
 *             items: [{
 *                 xtype: 'datefield',
 *                 label: 'Birthday',
 *                 reference: 'birthday',
 *                 value: new Date()
 *             }]
 *         }, {
 *             xtype: 'toolbar',
 *             docked: 'bottom',
 *             items: [{
 *                 text: 'setValue',
 *                 handler: function() {
 *                     var field = form.lookup('birthday'),
 *                         y = Ext.Number.randomInt(1980, 2011),
 *                         m = Ext.Number.randomInt(0, 11),
 *                         d = Ext.Number.randomInt(1, 28);
 *
 *                     field.setValue(new Date(y, m, d));
 *                 }
 *             }]
 *         }]
 *     });
 * ```
 * When you need to retrieve the date from the {@link Ext.field.Date}, you can either use
 * the {@link #getValue} or {@link #getFormattedValue} methods:
 *
 * ```javascript
 * @example({ framework: 'extjs' })
 * var form = Ext.create('Ext.form.Panel', {
 *     fullscreen: true,
 *     referenceHolder: true,
 *     items: [{
 *         xtype: 'fieldset',
 *         items: [{
 *             xtype: 'datefield',
 *             label: 'Birthday',
 *             reference: 'birthday',
 *             value: new Date()
 *         }]
 *     }, {
 *         xtype: 'toolbar',
 *         docked: 'bottom',
 *         items: [{
 *             text: 'getValue',
 *             handler: function() {
 *                 var field = form.lookup('birthday');
 *                 console.log(field.getValue());
 *             }
 *         }, {
 *             text: 'getFormattedValue',
 *             handler: function() {
 *                 var field = form.lookup('birthday');
 *                 console.log(field.getFormattedValue());
 *             }
 *         }]
 *     }]
 * });
 * ```
 * ```javascript
 * @example({framework: 'ext-react', packages:['ext-react']})
 * import React, { Component } from 'react';
 * import { ExtContainer, ExtFormPanel, ExtDatePickerField } from '@sencha/ext-react';
 *
 * export default class MyExample extends Component {
 *     render() {
 *         return (
 *             <ExtContainer layout="center">
 *                 <ExtFormPanel shadow>
 *                     <ExtDatePickerField
 *                         width={150}
 *                         value={new Date()}
 *                         destroyPickerOnHide
 *                         label="Date"
 *                         picker={{
 *                             yearFrom: 1990
 *                         }}
 *                     />
 *                 </ExtFormPanel>
 *             </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 [layout]='"center"'>
 *             <ExtFormPanel [shadow]>
 *                 <ExtDatePickerField
 *                     [width]='150'
 *                     [value]='date'
 *                     [destroyPickerOnHide]
 *                     [label]='"Date"'
 *                     [picker]='{
 *                         yearFrom: 1990
 *                     }'
 *                 >
 *                 </ExtDatePickerField>
 *             </ExtFormPanel>
 *         </ExtContainer>
 *     `
 * })
 * export class AppComponent {
 *     date:Date = new Date();
 * }
 * ```
 * ```html
 *  @example({framework: 'ext-web-components', packages:['ext-web-components'], tab: 1 })
 *  <ext-container layout="center">
 *    <ext-formpanel
 *      layout='{"type": "vbox", "align": "stretch"}'
 *      width="300"
 *      height="100"
 *      bodyPadding="10"
 *      shadow="true"
 *     >
 *      <ext-datepickerfield
 *        destroyPickerOnHide="true"
 *        label="Date"
 *        picker='{
 *          "yearFrom": "1990"
 *        }'
 *        onready="datepickerfield.datePickerFieldReady"
 *      >
 *      </ext-datepickerfield>
 *     </ext-formPanel>
 *  </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-datepickerfield.component';
 * import '@sencha/ext-web-components/dist/ext-formpanel.component';
 *
 * export default class DatePickerFieldComponent {
 *    datePickerFieldReady(event) {
 *        this.datepickerfieldView = event.detail.cmp;
 *        this.datepickerfieldView.setValue(new Date());
 *    }
 * }
 * window.datepickerfield = new DatePickerFieldComponent();
 *```
 */
Ext.define('Ext.field.Date', {
    extend: 'Ext.field.Picker',
 
    alternateClassName: [
        'Ext.form.DatePicker',
        'Ext.field.DatePicker'
    ],
 
    xtype: ['datefield', 'datepickerfield'],
 
    requires: [
        'Ext.data.validator.Date',
        'Ext.field.trigger.Date',
        'Ext.picker.Date',
        'Ext.panel.Date',
        'Ext.data.field.Date'
    ],
 
    /**
     * @event change
     * Fires when a date is selected
     * @param {Ext.field.Date} this 
     * @param {Date} newDate The new date
     * @param {Date} oldDate The old date
     */
 
    config: {
        /**
         * @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}.
         *
         * Example: {year: 1989, day: 1, month: 5} = 1st May 1989 or new Date()
         */
 
        /**
         * @cfg {Boolean} destroyPickerOnHide
         * Whether or not to destroy the picker widget on hide. This save memory if it's
         * not used frequently, but increase delay time on the next show due to
         * re-instantiation.
         */
        destroyPickerOnHide: false,
 
        dataType: {
            type: 'date'
        },
 
        /**
         * @cfg {String} [dateFormat=Ext.util.Format.defaultDateFormat] The format to be
         * used when displaying the date in this field. Accepts any valid date format. You
         * can view formats over in the {@link Ext.Date} documentation.
         */
        dateFormat: '',
 
        /**
         * @cfg {String|String[]} altFormats
         * Multiple date formats separated by "|" or an array of date formats
         * to try when parsing a user input value and it doesn't match the defined format.
         * @since 7.0
         */
        altFormats: 'm/d/Y|' +
                    'n/j/Y|' +
                    'n/j/y|' +
                    'm/j/y|' +
                    'n/d/y|' +
                    'm/j/Y|' +
                    'n/d/Y|' +
                    'm-d-y|' +
                    'm-d-Y|' +
                    'm/d|' +
                    'm-d|' +
                    'md|' +
                    'mdy|' +
                    'mdY|' +
                    'd|' +
                    'Y-m-d|' +
                    'n-j|' +
                    'n/j',
 
        /**
         * @cfg {Date/String} [minDate] The minimum allowed date value for this field.
         * String value should conform to {@link #cfg!dateFormat}.
         */
        minDate: null,
 
        /**
         * @cfg {Date/String} [maxDate] The maximum allowed date value for this field.
         * String value should conform to {@link #cfg!dateFormat}.
         */
        maxDate: null,
 
        triggers: {
            expand: {
                type: 'date'
            }
        }
    },
 
    classCls: Ext.baseCSSPrefix + 'datepickerfield',
    matchFieldWidth: false,
    isDateField: true,
 
    /**
     * @property {String} 
     * The error message when the {@link #cfg!minDate} constraint has been violated.
     * @locale
     */
    minDateMessage: "The date in this field must be equal to or after {0}",
 
    /**
     * @property {String} 
     * The error message when the {@link #cfg!maxDate} constraint has been violated.
     * @locale
     */
    maxDateMessage: "The date in this field must be equal to or before {0}",
 
    /**
     * In the absence of a time value, a default value of 12 noon will be used
     * note: 12 noon was chosen because it steers well clear of all DST timezone changes
     * @private
     */
    initTime: '12', // 24 hour format
    initTimeFormat: 'H',
 
    floatedPicker: {
        xtype: 'datepanel',
        autoConfirm: true,
        floated: true,
        listeners: {
            tabout: 'onTabOut',
            select: 'onPickerChange',
            scope: 'owner'
        },
        keyMap: {
            ESC: 'onTabOut',
            scope: 'owner'
        }
    },
 
    edgePicker: {
        xtype: 'datepicker',
        cover: true
    },
 
    parseValidator: 'date',
 
    applyValue: function(value, oldValue) {
        if (!(value || value === 0)) {
            value = null;
        }
 
        value = this.callParent([value, oldValue]);
 
        if (value) {
            if (this.isConfiguring) {
                this.originalValue = value;
            }
 
            // The same date value may not be the same reference, so compare them by time.
            // If we have dates for both, then compare the time. If they're the same we
            // don't need to do anything.
            if (
                Ext.isDate(value) &&
                Ext.isDate(oldValue) &&
                value.getTime() === oldValue.getTime()
            ) {
                return;
            }
        }
 
        return value;
    },
 
    updateValue: function(value, oldValue) {
        // Used picker directly instead of using getter as getter will create picker
        // if it does not exist.
        // We don't want to create the picker in value updater, this might lead to bugs as
        // well as performance challenges.
        var picker = this._picker;
 
        if (picker && picker.isPicker && Ext.isDate(value)) {
            this.updatePickerValue(picker, value);
        }
 
        this.callParent([value, oldValue]);
    },
 
    updatePickerValue: function(picker, value) {
        picker.setValue(value);
    },
 
    applyInputValue: function(value, oldValue) {
        if (Ext.isDate(value)) {
            value = Ext.Date.format(value, this.getDateFormat());
        }
 
        return this.callParent([value, oldValue]);
    },
 
    applyAltFormats: function(altFormats) {
        if (altFormats && !Ext.isArray(altFormats)) {
            altFormats = altFormats.split('|');
        }
 
        return altFormats;
    },
 
    applyDateFormat: function(dateFormat) {
        return dateFormat || Ext.util.Format.defaultDateFormat;
    },
 
    /**
     * Updates the date format in the field.
     * @private
     */
    updateDateFormat: function() {
        var me = this,
            value;
 
        if (!me.isConfiguring && !me.hasFocus) {
            value = me.getValue();
 
            if (Ext.isDate(value)) {
                me.setInputValue(value);
            }
        }
    },
 
    applyMinDate: function(minDate) {
        if (typeof minDate === 'string') {
            minDate = Ext.Date.parse(minDate, this.getDateFormat());
        }
 
        //<debug>
        if (!Ext.isDate(minDate)) {
            Ext.raise("Date object or string in dateFormat required");
        }
        //</debug>
 
        return Ext.Date.clearTime(minDate, true);
    },
 
    applyMaxDate: function(maxDate) {
        if (typeof maxDate === 'string') {
            maxDate = Ext.Date.parse(maxDate, this.getDateFormat());
        }
 
        //<debug>
        if (!Ext.isDate(maxDate)) {
            Ext.raise("Date object or string in dateFormat required");
        }
        //</debug>
 
        return Ext.Date.clearTime(maxDate, true);
    },
 
    /**
     * Returns the value of the field formatted using the specified format. If it is not
     * specified, it will default to {@link #dateFormat} and then
     * {@link Ext.util.Format#defaultDateFormat}.
     * @param {String} format The format to be returned.
     * @return {String} The formatted date.
     */
    getFormattedValue: function(format) {
        var value = this.getValue();
 
        return Ext.isDate(value) ? Ext.Date.format(value, format || this.getDateFormat()) : '';
    },
 
    applyPicker: function(picker, oldPicker) {
        var me = this;
 
        picker = me.callParent([picker, oldPicker]);
 
        if (picker) {
            me.pickerType = picker.xtype === 'datepicker' ? 'edge' : 'floated';
            picker.ownerCmp = me;
        }
 
        return picker;
    },
 
    createFloatedPicker: function() {
        return this.getFloatedPicker();
    },
 
    createEdgePicker: function() {
        var me = this,
            minDate = this.getMinDate(),
            maxDate = this.getMaxDate();
 
        return Ext.merge({
            yearFrom: minDate ? minDate.getFullYear() : (new Date().getFullYear() - 20),
            yearTo: maxDate ? maxDate.getFullYear() : (new Date().getFullYear() + 20)
        }, me.getEdgePicker());
    },
 
    setPickerLocation: function(fromKeyboard) {
        var me = this,
            pickerType = me.pickerType,
            picker = me.getPicker(),
            value = me.getValue(),
            limit;
 
        me.$ignorePickerChange = true;
 
        if (value != null) {
            picker.setValue(value);
        }
        else if (pickerType === 'edge') {
            picker.setValue(new Date());
        }
 
        delete me.$ignorePickerChange;
 
        if (pickerType === 'floated') {
            picker.el.dom.tabIndex = -1;
 
            limit = me.getMinDate();
 
            if (limit) {
                picker.setMinDate(limit);
            }
 
            limit = me.getMaxDate();
 
            if (limit) {
                picker.setMaxDate(limit);
            }
 
            value = value || new Date();
 
            // Ensure the carousel is at the correct position wth no animation.
            picker.navigateTo(value, false);
 
            if (fromKeyboard) {
                picker.focusDate(value);
            }
        }
    },
 
    doValidate: function(value, errors, skipLazy) {
        var me = this,
            format = me.getDateFormat(),
            limit, t;
 
        me.callParent([ value, errors, skipLazy ]);
 
        limit = me.getMinDate();
        t = +value;  // doValidate is only passed values that have been parsed
 
        if (limit && t < +limit) {
            limit = Ext.Date.format(limit, format);
            errors.push(Ext.String.format(me.minDateMessage, limit));
        }
 
        limit = me.getMaxDate();
 
        if (limit && t > +limit) {
            limit = Ext.Date.format(limit, format);
            errors.push(Ext.String.format(me.maxDateMessage, limit));
        }
    },
 
    /**
     * Called when the picker changes its value.
     * @param {Ext.picker.Date} picker The date picker.
     * @param {Object} value The new value from the date picker.
     * @private
     */
    onPickerChange: function(picker, value) {
        var me = this;
 
        if (me.$ignorePickerChange) {
            return;
        }
 
        me.forceSetValue(value);
 
        me.fireEvent('select', me, value);
 
        // Focus the inputEl first and then collapse. We configure
        // the picker not to revert focus which is a normal thing to do
        // for floaters; in our case when the picker is focusable it will
        // lead to unexpected results on Tab key presses.
        // Note that this focusing might happen synchronously during Tab
        // key handling in the picker, which is the way we want it.
        me.onTabOut(picker);
    },
 
    onTabOut: function() {
        // Automatic focus reversion will move focus back to the owning field if necessary.
        this.collapse();
    },
 
    parseValue: function(value, errors) {
        var me = this,
            date = value,
            defaultFormat = me.getDateFormat(),
            altFormats = me.getAltFormats(),
            formats = altFormats ? [defaultFormat].concat(altFormats) : [defaultFormat],
            formatsLength = formats.length,
            i, format;
 
        if (date) {
            if (!Ext.isDate(date)) {
                for (= 0; i < formatsLength; i++) {
                    format = formats[i];
                    date = Ext.Date.parse(
                        value + ' ' + me.initTime,
                        format + ' ' + me.initTimeFormat
                    );
 
                    if (date) {
                        return Ext.Date.clearTime(date);
                    }
                }
            }
 
            if (date !== null) {
                return date;
            }
        }
 
        return this.callParent([value, errors]);
    },
 
    isEqual: function(value1, value2) {
        var v1 = this.transformValue(value1),
            v2 = this.transformValue(value2);
 
        return +v1 === +v2;
    },
 
    transformValue: function(value) {
        if (Ext.isObject(value)) {
            value = new Date(value.year, value.month, value.day);
 
            if (isNaN(value.getTime())) {
                value = null;
            }
        }
 
        return value;
    },
 
    doDestroy: function() {
        var picker = this._picker;
 
        if (picker && picker.isPicker) {
            picker.destroy();
        }
 
        this.callParent();
    },
 
    rawToValue: function(rawValue) {
        var me = this;
 
        return me.parseValue(rawValue) || rawValue || null;
    },
 
    privates: {
        setShowPickerValue: function(picker) {
            this.updatePickerValue(picker, this.getValue() || new Date());
        }
    },
 
    deprecated: {
        '6.5': {
            configs: {
                format: 'dateFormat'
            }
        }
    }
});