/**
 * This class is the main calendar panel, it wraps {@link Ext.calendar.view.Multi}.
 *
 * It allows switching between multiple views of the same dataset. It is composed of the
 * other calendar types such as {@link Ext.calendar.panel.Month},
 * {@link Ext.calendar.panel.Week}, and {@link Ext.calendar.panel.Day}.
 *
 * It also provides extra UI features like a switcher button,
 * {@link #cfg-titleBar title bar}, and navigation buttons.
 *
 * Sample Calendar panel
 *
 *     Ext.create({
 *         xtype: 'calendar',
 *         renderTo: Ext.getBody(),
 *         height: 400,
 *         width: 600,
 *         store: {
 *             autoLoad: true,
 *             proxy: {
 *                 type: 'ajax',
 *                 url: 'calendars.php'
 *             },
 *             eventStoreDefaults: {
 *                 proxy: {
 *                     type: 'ajax',
 *                     url: 'events.php'
 *                 }
 *             }
 *         }
 *     });
 *
 * By default, the Calendar panel contains a {@link Ext.calendar.panel.Day Day},
 * {@link Ext.calendar.panel.Week Week}, and {@link Ext.calendar.panel.Month Month} view.
 * Configurations for these views may be passed in the {@link #cfg-views} config option.
 * For example, to display only a 5-day work week instead of the default 7-day week the
 * following `views` config would be used:
 *
 *     Ext.create({
 *         xtype: 'calendar',
 *         renderTo: Ext.getBody(),
 *         height: 400,
 *         width: 600,
 *         store: {
 *             autoLoad: true,
 *             proxy: {
 *                 type: 'ajax',
 *                 url: 'calendars.php'
 *             },
 *             eventStoreDefaults: {
 *                 proxy: {
 *                     type: 'ajax',
 *                     url: 'events.php'
 *                 }
 *             }
 *         },
 *         views: {
 *             week: {
 *                 visibleDays: 5,
 *                 firstDayOfWeek: 1
 *             }
 *         }
 *     });
 *
 * The previous example will result in a Day, Week, and Month view in the Calendar panel
 * with the Week view displaying only 5 days.  Set a default view to `null` to prevent it
 * from being included in the Calendar panel.
 *
 *     Ext.create({
 *         xtype: 'calendar',
 *         renderTo: Ext.getBody(),
 *         height: 400,
 *         width: 600,
 *         store: {
 *             autoLoad: true,
 *             proxy: {
 *                 type: 'ajax',
 *                 url: 'calendars.php'
 *             },
 *             eventStoreDefaults: {
 *                 proxy: {
 *                     type: 'ajax',
 *                     url: 'events.php'
 *                 }
 *             }
 *         },
 *         views: {
 *             month: null,  // now only the Week and Day calendars will be included
 *             week: {
 *                 visibleDays: 5,
 *                 firstDayOfWeek: 1
 *             }
 *         }
 *     });
 *
 * See the {@link #cfg-views} config for additional configuration options.
 *
 * The default view is "month".  This can be set using the {@link #cfg-defaultView}
 * config option.
 *
 * The following Multi view configs will be applied to any calendar panel in the views
 * config:
 *
 *  - {@link #cfg-compact}
 *  - {@link #cfg-compactOptions}
 *  - {@link #cfg-store}
 *  - {@link #cfg-timezoneOffset}
 *  - {@link #cfg-value}
 */
Ext.define('Ext.calendar.panel.Panel', {
    extend: 'Ext.calendar.panel.AbstractPanel',
    xtype: 'calendar',

    mixins: ['Ext.mixin.ConfigState'],
    alternateStateConfig: 'compactOptions',

    requires: [
        'Ext.calendar.panel.Day',
        'Ext.calendar.panel.Week',
        'Ext.calendar.panel.Month',
        'Ext.calendar.List',
        'Ext.calendar.view.Multi',
        'Ext.calendar.date.Util'
    ],

    /**
     * @cfg referenceHolder
     * @inheritdoc
     */
    referenceHolder: true,

    platformConfig: {
        '!desktop': {
            compact: true
        }
    },

    config: {
        /**
         * @cfg {Object} calendarList
         * The config for creating the {@link Ext.calendar.List calendar list}.
         */
        calendarList: {
            xtype: 'calendar-list',
            reference: 'list',
            flex: 1
        },

        /**
         * @cfg compact
         * @inheritdoc Ext.calendar.view.Multi#cfg-compact
         */
        compact: false,

        /**
         * @cfg compactOptions
         * @inheritdoc Ext.calendar.view.Multi#cfg-compactOptions
         */
        compactOptions: {},

        /**
         * @cfg {Object} createButton
         * The config for creating the create button.  Clicking / tapping the create
         * button shows the {@link Ext.calendar.form.Add add event form}.  To prevent the
         * create button from being created use `null`.
         *
         *     createButton: null
         *
         * To configure the add form or prevent a user from adding events via calendar
         * interactions see calendar view's
         * {@link Ext.calendar.view.Base#cfg-addForm addForm} config option.
         */
        createButton: {
            xtype: 'button',
            cls: Ext.baseCSSPrefix + 'calendar-panel-create-button',
            text: 'Create'
        },

        /**
         * @cfg {String} createButtonPosition
         * The position for the create button. Can be "sideBar" or "titleBar".
         */
        createButtonPosition: 'sideBar',

        /**
         * @cfg {Object} dateTitle
         * The config for the date title.
         *
         * **Note:** The date title template is configurable within the
         * {@link #cfg-views} config option for each view
         */
        dateTitle: {
            xtype: 'component',
            reference: 'calTitle',
            cls: Ext.baseCSSPrefix + 'calendar-panel-title',
            margin: '0 0 0 10'
        },

        /**
         * @cfg {Boolean} highlightToday
         * `true` to highlight today's date cell.
         * `false` not to highlight today's date.
         * @since 7.0
         */
        highlightToday: true,

        /**
         * @cfg {Object} nextButton
         * The configuration for the next navigation button.
         */
        nextButton: {
            xtype: 'button',
            text: '>'
        },

        /**
         * @cfg {Object} previousButton
         * The configuration for the previous navigation button.
         */
        previousButton: {
            xtype: 'button',
            text: '<'
        },

        /**
         * @cfg {Object} sideBar
         * The configuration for the sidebar. Extra items can be added/inserted into
         * the sidebar by adding the items configuration. Items will be sorted by a `weight`
         * property. Existing items in the sidebar have weights `0-100` with an increment of 10
         * for each item. Use a number less than 0 to insert at the front. Use a number
         * larger than 100 to insert at the end.
         */
        sideBar: {
            xtype: 'panel',
            cls: Ext.baseCSSPrefix + 'calendar-sidebar'
        },

        /**
         * @cfg store
         * @inheritdoc Ext.calendar.view.Multi#cfg-store
         */
        store: null,

        /**
         * @cfg {Object} switcher
         * The switcher is a Segmented Button used to switch the calendar views within
         * the Calendar Panel.  The buttons items themselves are added when the Calendar
         * Panel is created using the configured {@link #cfg-views views}.  The views'
         * keys (names) and weight determine the appearance of tbe switcher buttons.
         * 
         * For example, using the following views config:
         * 
         *     day: {
         *         xtype: 'calendar-day',
         *         titleTpl: '{start:date("l F d, Y")}',
         *         controlStoreRange: false,
         *         label: 'Day',
         *         weight: 10,
         *         dayHeader: null
         *     },
         *     week: {
         *         xtype: 'calendar-week',
         *         dayHeaderFormat: 'D d',
         *         controlStoreRange: false,
         *         titleTpl: '{start:date("j M")} - {end:date("j M Y")}',
         *         label: 'Week',
         *         weight: 20
         *     },
         *     month: {
         *         xtype: 'calendar-month',
         *         titleTpl: '{start:date("F Y")}',
         *         label: 'Month',
         *         weight: 30
         *     }
         * 
         * The following buttons would be created in the following order:
         * Day > Week > Month
         */
        switcher: {
            xtype: 'segmentedbutton',
            reference: 'switcher',
            cls: Ext.baseCSSPrefix + 'calendar-panel-switcher',
            allowMultiple: false
        },

        /**
         * @cfg {String} switcherPosition
         * The position for the create button. Can be `sideBar` or `titleBar`.
         */
        switcherPosition: 'titleBar',

        /**
         * @cfg timezoneOffset
         * @inheritdoc Ext.calendar.view.Multi#cfg-timezoneOffset
         */
        timezoneOffset: undefined,

        /**
         * @cfg {Object} titleBar
         * The configuration for the titleBar. Extra items can be added/inserted into
         * the title bar by adding the items configuration. Items will be sorted by a
         * `weight` property. Existing items in the title bar have weights `0-100` with
         * an increment of 10 for each item. Use a number less than 0 to insert at the
         * front. Use a number larger than 100 to insert at the end.
         */
        titleBar: {
            xtype: 'toolbar'
        },

        /**
         * @cfg {Object} todayButton
         * The configuration for the today button.
         */
        todayButton: {
            xtype: 'button',
            text: 'Today',
            margin: '0 10 0 0'
        },

        /**
         * @cfg value
         * @inheritdoc Ext.calendar.view.Multi#cfg-value
         */
        value: undefined,

        /**
         * @cfg {Object} views
         * The calendar views to have available.  Each item in this configuration
         * (labelled by a key) is to contain the configuration for the view, a class that
         * extends {@link Ext.calendar.panel.Base}. There are also other configurations
         * available only when used in conjunction with this panel:
         *
         * - `label` - A label to display on the switcher control
         * - `weight` - A number to indicate the order in which items are
         * displayed in the switcher.  Lower numbers are displayed first.
         * - `titleTpl` - A template string for displaying the current date title.  The
         * values passed are the start and end dates.
         *
         * The default configuration:
         *
         *     views: {
         *         day: {
         *             xtype: 'calendar-day',
         *             titleTpl: '{start:date("l F d, Y")}',
         *             controlStoreRange: false,
         *             label: 'Day',
         *             weight: 10,
         *             dayHeader: null
         *         },
         *         week: {
         *             xtype: 'calendar-week',
         *             dayHeaderFormat: 'D d',
         *             controlStoreRange: false,
         *             titleTpl: '{start:date("j M")} - {end:date("j M Y")}',
         *             label: 'Week',
         *             weight: 20
         *         },
         *         month: {
         *             xtype: 'calendar-month',
         *             titleTpl: '{start:date("F Y")}',
         *             label: 'Month',
         *             weight: 30
         *         }
         *     }
         *
         * Any view configuration options passed will override the default configuration.
         * For example, to change the week view to show 5 days with the week beginning on
         * Monday:
         *
         *     views: {
         *         week: {
         *             visibleDays: 5,
         *             firstDayOfWeek: 1,
         *             label: 'Work Week'
         *         }
         *     }
         *
         * With the above config the default day and month views will be rendered along
         * with a modified week view with the text of "Work Week" in the view switcher
         * control.
         *
         * To prevent a default view from being displayed set the view config to `null`.
         * For example, to hide the Month view:
         *
         *     views: {
         *         month: null
         *     }
         *
         * Any calendar views configured with keys other than "day", "week", and "month"
         * will be included in addition to the default views.  For example, to show the
         * modified work week view we configured above in addition to the default Day,
         * Week, and Month views we would pass in the same config with a key of
         * "workweek" (the can be anything you choose) along with a weight indicating the
         * placement of the "Work Week" button in the switcher.
         *
         *     views: {
         *         workweek: {
         *             visibleDays: 5,
         *             firstDayOfWeek: 1,
         *             label: 'Work Week',
         *             weight: 25
         *         }
         *     }
         *
         * Using the above config the Calendar would now have 4 views available and would
         * display "Work Week" in the switcher control after "Week" and before "Month".
         *
         * The configuration key will be the string used when specifying the
         * {@link #cfg-defaultView} as well as that passed to the {@link #method-setView}
         * method.  For example, using the last views config example the "worweek" would
         * be shown using:
         *
         *     CalendarInstance.setView('workweek');
         * @locale
         */
        views: {
            day: {
                xtype: 'calendar-day',
                titleTpl: '{start:date("l F d, Y")}',
                controlStoreRange: false,
                label: 'Day',
                weight: 10,
                dayHeader: null
            },
            week: {
                xtype: 'calendar-week',
                dayHeaderFormat: 'D d',
                controlStoreRange: false,
                titleTpl: '{start:date("j M")} - {end:date("j M Y")}',
                label: 'Week',
                weight: 20
            },
            month: {
                xtype: 'calendar-month',
                titleTpl: '{start:date("F Y")}',
                label: 'Month',
                weight: 30
            }
        }
    },

    /**
     * @cfg defaultView
     * @inheritdoc Ext.calendar.view.Multi#cfg-defaultView
     */
    defaultView: 'month',

    /**
     * @cfg cls
     * @inheritdoc
     */
    cls: Ext.baseCSSPrefix + 'calendar-panel',

    /**
     * @method getCalendarList
     * @hide
     */

    /**
     * @method setCalendarList
     * @hide
     */

    /**
     * @method getCreateButton
     * @hide
     */

    /**
     * @method setCreateButton
     * @hide
     */

    /**
     * @method getNextButton
     * @hide
     */

    /**
     * @method setNextButton
     * @hide
     */

    /**
     * @method getPreviousButton
     * @hide
     */

    /**
     * @method setPreviousButton
     * @hide
     */

    /**
     * @method getSideBar
     * @hide
     */

    /**
     * @method setSideBar
     * @hide
     */

    /**
     * @method getSwitcher
     * @hide
     */

    /**
     * @method setSwitcher
     * @hide
     */

    /**
     * @method getTitleBar
     * @hide
     */

    /**
     * @method setTitleBar
     * @hide
     */

    /**
     * @method getTodayButton
     * @hide
     */

    /**
     * @method setTodayButton
     * @hide
     */

    /**
     * @method setViews
     * @hide
     */

    /**
     * Moves the active view forward. The amount moved
     * depends on the current view.
     */
    moveNext: function() {
        this.getView().moveNext();
    },

    /**
     * Moves the active view backward. The amount moved
     * depends on the current view.
     */
    movePrevious: function() {
        this.getView().movePrevious();
    },

    /**
     * Move the current view by an amount based of the current {@link #value}.
     * @param {Number} amount The number of intervals to move.
     * @param {String} [interval=Ext.Date.DAY] The interval to navigate by. See {@link Ext.Date}
     * for valid intervals.
     */
    navigate: function(amount, interval) {
        this.getView().navigate(amount, interval);
    },

    /**
     * @method setView
     * @inheritdoc Ext.calendar.view.Multi#method-setView
     */
    setView: function(view) {
        this.doSetView(view);
    },

    // Appliers/Updaters
    updateCompact: function(compact, oldCompact) {
        var me = this;

        me.toggleCls(Ext.baseCSSPrefix + 'compact', compact);

        me.toggleConfigState(compact);
        me.callParent([compact, oldCompact]);
        me.setViewCfg('setCompact', compact);
    },

    updateCompactOptions: function() {
        if (!this.isConfiguring && this.getCompact()) {
            this.toggleConfigState(true);
        }
    },

    applyStore: function(store) {
        if (store) {
            store = Ext.StoreManager.lookup(store, 'calendar-calendars');
        }

        return store;
    },

    updateStore: function(store) {
        var list = this.lookup('list');

        this.setViewCfg('setStore', store);

        if (list) {
            list.setStore(store);
        }
    },

    applyTimezoneOffset: function(timezoneOffset) {
        this.autoOffset = false;

        if (timezoneOffset === undefined) {
            timezoneOffset = Ext.calendar.date.Util.getDefaultTimezoneOffset();
            this.autoOffset = true;
        }

        return timezoneOffset;
    },

    updateTimezoneOffset: function(timezoneOffset) {
        this.setViewCfg('setTimezoneOffset', timezoneOffset);
    },

    applyValue: function(value, oldValue) {
        value = Ext.Date.clearTime(value || Ext.calendar.date.Util.getLocalNow(), true);

        if (oldValue && oldValue.getTime() === value.getTime()) {
            value = undefined;
        }

        return value;
    },

    updateValue: function(value) {
        this.setViewCfg('setValue', value);
        this.refreshCalTitle();
    },

    getValue: function() {
        var view = this.getView();

        return view ? view.getValue() : this.callParent();
    },

    /**
     * @protected
     * Returns the {@link Ext.calendar.view.Multi} container used to house the configured
     * calendar {@link #cfg-views}
     * @return {Ext.calendar.view.Multi} The multi calendar view container
     */
    getView: function() {
        return this.lookup('view');
    },

    privates: {
        weightStart: 0,
        weightIncrement: 10,

        createCalendarList: function(cfg) {
            return Ext.apply({
                store: this.getStore()
            }, this.getCalendarList());
        },

        createCreateButton: function(cfg) {
            cfg = cfg || {};
            cfg = Ext.apply(cfg, this.getCreateButton());

            return Ext.apply({
                handler: 'onCreateTap',
                scope: this
            }, cfg);
        },

        createContainerWithChildren: function(defaults, cfg, items) {
            cfg = Ext.apply({}, cfg);

            // eslint-disable-next-line vars-on-top
            var me = this,
                cfgItems = cfg.items,
                weight = me.weightStart,
                incr = me.weightIncrement,
                len, i, item;

            if (cfgItems) {
                if (!Ext.isArray(cfgItems)) {
                    cfgItems = [cfgItems];
                }

                items = Ext.Array.clone(items);

                for (i = 0, len = items.length; i < len; ++i) {
                    item = items[i];

                    if (item.weight == null) {
                        items[i] = Ext.apply({
                            weight: weight
                        }, item);
                    }

                    weight += incr;
                }

                items = items.concat(cfgItems);
                Ext.Array.sort(items, me.weightSorter);
                delete cfg.items;
            }

            cfg.items = items;

            return Ext.apply(cfg, defaults);
        },

        createDateTitle: function(cfg) {
            cfg = cfg || {};

            return Ext.apply(cfg, this.getDateTitle());
        },

        createNextButton: function() {
            return Ext.apply({
                handler: 'onNextTap',
                scope: this
            }, this.getNextButton());
        },

        createPreviousButton: function() {
            return Ext.apply({
                handler: 'onPrevTap',
                scope: this
            }, this.getPreviousButton());
        },

        createSwitcher: function(cfg) {
            var me = this,
                view = me.getView();

            cfg = Ext.apply({
                value: (view && view.getActiveKey()) || me.defaultView,
                listeners: {
                    scope: me,
                    change: 'onSwitcherChange'
                },
                items: me.getSwitcherItems()
            }, cfg);

            return Ext.apply(cfg, me.getSwitcher());
        },

        createTodayButton: function() {
            return Ext.apply({
                handler: 'onTodayTap',
                scope: this
            }, this.getTodayButton());
        },

        createView: function() {
            var me = this;

            return {
                xtype: 'calendar-multiview',
                reference: 'view',
                compact: me.getCompact(),
                defaultView: me.defaultView,
                store: me.getStore(),
                timezoneOffset: me.autoOffset ? undefined : me.getTimezoneOffset(),
                value: me.getValue(),
                views: me.getViews(),
                highlightToday: me.getHighlightToday(),
                listeners: {
                    scope: me,
                    valuechange: 'onValueChange'
                }
            };
        },

        doSetView: function(view, fromSwitcher) {
            if (!fromSwitcher) {
                this.setSwitcherValue(view);

                return;
            }

            this.getView().highlightToday = this.getHighlightToday();
            this.getView().setView(view);
            this.refreshCalTitle();
        },

        updateHighlightToday: function(highlightToday) {
            if (!this.isConfiguring) {
                this.getView().updateHighlightToday(highlightToday);
            }
        },

        getSwitcherItems: function() {
            var views = this.getViews(),
                items = [],
                key, o;

            for (key in views) {
                o = views[key];

                if (o) {
                    items.push({
                        text: o.label,
                        value: key,
                        weight: o.weight
                    });
                }
            }

            items.sort(this.weightSorter);

            return items;
        },

        onCreateTap: function() {
            this.getView().showAddForm();
        },

        onNextTap: function() {
            this.moveNext();
        },

        onPrevTap: function() {
            this.movePrevious();
        },

        onValueChange: function(view, context) {
            this.setValue(context.value);
        },

        onTodayTap: function() {
            this.setValue(new Date());
        },

        refreshCalTitle: function() {
            var me = this,
                view = me.getView(),
                calTitle = me.lookup('calTitle'),
                tpl;

            if (view && calTitle) {
                view = view.activeView;
                tpl = view.lookupTpl('titleTpl');

                if (tpl) {
                    calTitle.setHtml(tpl.apply(view.getDisplayRange()));
                }
            }
        },

        setViewCfg: function(setterName, value) {
            var view;

            if (!this.isConfiguring) {
                view = this.getView();

                if (view) {
                    view[setterName](value);
                }
            }
        },

        weightSorter: function(a, b) {
            return a.weight - b.weight;
        }
    }
});
