/**
 * The Paging Toolbar is a specialized toolbar that is
 * bound to a `Ext.data.Store` and provides automatic paging control.
 *
 * ```javascript
 * @example({ framework: 'extjs' })
 * var store = Ext.create('Ext.data.Store', {
 *     fields: ['fname', 'lname', 'talent'],
 *     pageSize: 3,
 *     data: [
 *         { 'fname': 'Barry',  'lname': 'Allen',      'talent': 'Speedster' },
 *         { 'fname': 'Oliver', 'lname': 'Queen',      'talent': 'Archery'  },
 *         { 'fname': 'Kara',   'lname': 'Zor-El',     'talent': 'All'  },
 *         { 'fname': 'Helena', 'lname': 'Bertinelli', 'talent': 'Weapons Expert'  },
 *         { 'fname': 'Hal',    'lname': 'Jordan',     'talent': 'Willpower'  },
 *     ]
 * });
 *
 * Ext.create('Ext.grid.Grid', {
 *     title: 'DC Personnel',
 *
 *     store: store,
 *     plugins: {
 *         pagingtoolbar: true
 *     },
 *
 *     columns: [
 *         { text: 'First Name', dataIndex: 'fname',  flex: 1 },
 *         { text: 'Last Name',  dataIndex: 'lname',  flex: 1 },
 *         { text: 'Talent',     dataIndex: 'talent', flex: 1 }
 *     ],
 *
 *     height: 230,
 *     layout: 'fit',
 *     fullscreen: true
 * });
 * ```
 * ```html
 * @example({framework: 'ext-web-components', packages:['ext-web-components'], tab: 1 })
 * <ext-container width="100%" height="100%">
 *     <ext-grid
 *         shadow="true"
 *         height="275"
 *         plugins='["pagingtoolbar"]'
 *         onready="paginggrid.onGridReady"
 *     >
 *         <ext-column text="First Name" dataIndex="fname" flex="1" editable="true"></ext-column>
 *         <ext-column text="Last Name" dataIndex="lname" flex="1" editable="true"></ext-column>
 *         <ext-column text="Talent" dataIndex="talent" flex="1" editable="true"></ext-column>
 *     </ext-grid>
 * </ext-container>
 * ```
 * ```javascript
 * @example({framework: 'ext-web-components', tab: 2, packages: ['ext-web-components']})
 * import '@sencha/ext-web-components/dist/ext-container.component';
 * import '@sencha/ext-web-components/dist/ext-grid.component';
 * import '@sencha/ext-web-components/dist/ext-column.component';
 * 
 * Ext.require('Ext.grid.plugin.PagingToolbar');
 * 
 * export default class PagingGridComponent {
 *     constructor() {
 *        this.store = new Ext.data.Store({
 *           pageSize: 3,
 *           data: [
 *               { 'fname': 'Barry',  'lname': 'Allen', 'talent': 'Speedster'},
 *               { 'fname': 'Oliver', 'lname': 'Queen', 'talent': 'Archery'},
 *               { 'fname': 'Kara',   'lname': 'Zor-El', 'talent': 'All'},
 *               { 'fname': 'Helena', 'lname': 'Bertinelli', 'talent': 'Weapons Expert'},
 *               { 'fname': 'Hal',    'lname': 'Jordan', 'talent': 'Willpower'  }
 *           ]
 *        });
 *     }
 * 
 *     onGridReady(event) {
 *         this.pagingGridCmp = event.detail.cmp;
 *         this.pagingGridCmp.setStore(this.store);
 *     }
 * }
 * window.paginggrid = new PagingGridComponent();
 * ```
 * ```javascript
 * @example({framework: 'ext-react', packages:['ext-react']})
 * import React, { Component } from 'react'
 * import { ExtGrid, ExtColumn } from '@sencha/ext-react';
 * 
 * Ext.require('Ext.grid.plugin.PagingToolbar');
 *
 * export default class MyExample extends Component {
 *
 *     store = new Ext.data.Store({
 *         pageSize: 3,
 *         data: [
 *             { 'fname': 'Barry',  'lname': 'Allen', 'talent': 'Speedster'},
 *             { 'fname': 'Oliver', 'lname': 'Queen', 'talent': 'Archery'},
 *             { 'fname': 'Kara',   'lname': 'Zor-El', 'talent': 'All'},
 *             { 'fname': 'Helena', 'lname': 'Bertinelli', 'talent': 'Weapons Expert'},
 *             { 'fname': 'Hal',    'lname': 'Jordan', 'talent': 'Willpower'  }
 *         ]
 *     });
 *
 *    render() {
 *        return (
 *            <ExtGrid
 *                height="180"
 *                store={this.store} 
 *                plugins={['pagingtoolbar']}
 *            >
 *                <ExtColumn 
 *                    text="First Name"
 *                    dataIndex="fname"
 *                    flex={1}
 *                />
 *                <ExtColumn 
 *                    text="Last Name"
 *                    dataIndex="lname"
 *                    flex={1}
 *                />
 *                <ExtColumn 
 *                    text="Talent"
 *                    dataIndex="talent"
 *                    flex={1}
 *                />
 *            </ExtGrid>
 *        )
 *    }
 * }
 * ```
 * ```javascript
 * @example({framework: 'ext-angular', packages:['ext-angular']})
 * import { Component } from '@angular/core'
 * declare var Ext: any;
 *
 * Ext.require('Ext.grid.plugin.PagingToolbar');
 * @Component({
 *     selector: 'app-root-1',
 *     styles: [`
 *             `],
 *     template: `
 *     <ExtContainer>
 *         <ExtGrid
 *             [height]="'180px'"
 *             [store]="this.store"
 *             [plugins]="['pagingtoolbar']"
 *         >
 *             <ExtColumn 
 *                 text="First Name"
 *                 dataIndex="fname"
 *                 flex="1"
 *             ></ExtColumn>
 *             <ExtColumn
 *                 text="Last Name"
 *                 dataIndex="lname"
 *                 flex="1"
 *             ></ExtColumn>
 *             <ExtColumn
 *                 text="Talent"
 *                 dataIndex="talent"
 *                 flex="1"
 *             >
 *             </ExtColumn>
 *         </ExtGrid>
 *     </ExtContainer>
 *     `
 * })
 * export class AppComponent {
 *     store = new Ext.data.Store({
 *         pageSize: 3,
 *         data: [
 *             { 'fname': 'Barry',  'lname': 'Allen', 'talent': 'Speedster'},
 *             { 'fname': 'Oliver', 'lname': 'Queen', 'talent': 'Archery'},
 *             { 'fname': 'Kara',   'lname': 'Zor-El', 'talent': 'All'},
 *             { 'fname': 'Helena', 'lname': 'Bertinelli', 'talent': 'Weapons Expert'},
 *             { 'fname': 'Hal',    'lname': 'Jordan', 'talent': 'Willpower'  }
 *         ]
 *     });
 * }
 * ```
 */
Ext.define('Ext.grid.plugin.PagingToolbar', {
    extend: 'Ext.plugin.Abstract',
    alias: ['plugin.pagingtoolbar', 'plugin.gridpagingtoolbar'],
    mixins: ['Ext.mixin.Hookable'],
 
    requires: [
        'Ext.grid.PagingToolbar',
        'Ext.util.DelayedTask'
    ],
 
    config: {
        /**
         * @cfg grid
         * @private
         */
        grid: null,
        currentPage: 1,
 
        /**
         * @cfg pageSize
         * @inheritdoc Ext.data.AbstractStore#cfg!pageSize
         */
        pageSize: 0,
 
        totalCount: 0,
        totalPages: 0,
        loadPages: null,
 
        /**
         * @cfg {Number|'dragend'} buffer
         * If a number, this is the number of milliseconds to delay after dragging stops
         * but the drag has not ended. If it is 'dragend', fetches from the remote server
         * will be suspended until dragging is completed.
         */
        buffer: 250,
 
        toolbar: {
            xtype: 'pagingtoolbar',
            docked: 'bottom'
        }
    },
 
    init: function(grid) {
        this.setGrid(grid);
        grid.add(this.getToolbar());
    },
 
    destroy: function() {
        this.setBuffer(null);
        this.setGrid(null);
        this.callParent();
    },
 
    updateGrid: function(grid, oldGrid) {
        var me = this;
 
        me.gridListeners = me.storeListeners = me.scrollListeners = Ext.destroy(
            me.gridListeners, me.storeListeners, me.scrollListeners
        );
 
        if (grid) {
            me.gridListeners = grid.on({
                updatevisiblecount: 'onUpdateVisibleCount',
                storechange: 'onStoreChanged',
                destroyable: true,
                scope: me
            });
            me.scrollListeners = grid.getScrollable().on({
                scrollend: 'checkPageChange',
                buffer: 100,
                scope: me
            });
 
            me.bindStore(grid.getStore());
        }
    },
 
    bindStore: function(store) {
        var me = this;
 
        Ext.destroy(me.storeListeners);
        me.getToolbar().setDisabled(!!store);
 
        if (!store) {
            return;
        }
 
        me.storeListeners = store.on({
            add: 'onTotalCountChange',
            remove: 'onTotalCountChange',
            refresh: 'onTotalCountChange',
            clear: 'onTotalCountChange',
            destroyable: true,
            scope: me
        });
 
        /* we have two scenarios:
         1. pageSize = 0, which means that we have the entire data in the store
         and we just need to show current page in the toolbar
 
         2. we have pageSize > 0 which means that we probably don't have the
         entire data in the store and we need to load it page by page
         */
        me.setLoadPages(store.pageSize > 0);
 
        me.cancelBufferTask();
 
        if (store.isLoaded()) {
            me.onTotalCountChange(store);
        }
    },
 
    onStoreChanged: function(grid, store) {
        this.bindStore(store);
    },
 
    /**
     * @private
     */
    getPageData: function() {
        var grid = this.getGrid(),
            store = grid.getStore(),
            totalCount = store.getTotalCount() || store.getCount(),
            pageSize = this.getLoadPages() ? store.pageSize : grid.visibleCount,
            pageCount = Math.ceil(totalCount / pageSize);
 
        return {
            totalCount: totalCount,
            totalPages: Ext.Number.isFinite(pageCount) ? pageCount : 1,
            currentPage: store.currentPage,
            pageSize: pageSize
        };
    },
 
    checkPageChange: function() {
        var me = this,
            grid = me.getGrid(),
            pageSize = me.getPageSize(),
            currentPage = me.getCurrentPage(),
            topVisibleIndex = grid.topVisibleIndex,
            // on the first page topVisibleIndex is 0
            newPage = Math.ceil((topVisibleIndex + pageSize) / pageSize);
 
        if (grid.getStore() && !me.getLoadPages() && newPage > 0 && newPage !== currentPage) {
            me.preventGridScroll = true;
            me.setCurrentPage(newPage);
            me.preventGridScroll = false;
        }
    },
 
    updateBuffer: function(buffer) {
        var me = this,
            bufferTask = me.bufferTask;
 
        if (Ext.isNumber(buffer)) {
            me.bufferTask = bufferTask || new Ext.util.DelayedTask(me.bufferTaskRun, me);
            me.cancelBufferTask();
        }
        else if (bufferTask) {
            bufferTask.cancel();
            me.bufferTask = null;
        }
    },
 
    cancelBufferTask: function() {
        if (this.bufferTask) {
            this.bufferTask.cancel();
        }
    },
 
    loadCurrentPage: function() {
        this.getGrid().getStore().loadPage(this.getCurrentPage());
    },
 
    bufferTaskRun: function() {
        this.loadCurrentPage();
    },
 
    applyToolbar: function(toolbar, oldToolbar) {
        return Ext.factory(toolbar, Ext.Toolbar, oldToolbar);
    },
 
    updateToolbar: function(toolbar) {
        var me = this;
 
        if (toolbar) {
            toolbar.getSliderField().on({
                change: 'onPageChange',
                dragstart: 'onPageSliderDrag',
                drag: 'onPageSliderDrag',
                dragend: 'onPageSliderDragEnd',
                scope: me
            });
 
            toolbar.getNextButton().on({
                tap: 'onNextPageTap',
                scope: me
            });
 
            toolbar.getPrevButton().on({
                tap: 'onPreviousPageTap',
                scope: me
            });
        }
    },
 
    onPageChange: function(field, value) {
        this.setCurrentPage(value);
    },
 
    onPageSliderDrag: function(field, slider, value) {
        this.isDragging = true;
        this.setCurrentPage(Ext.isArray(value) ? value[0] : value);
    },
 
    onPageSliderDragEnd: function() {
        var me = this;
 
        me.isDragging = false;
 
        if (me.getBuffer() === 'dragend' || me.bufferTask.Id) {
            me.cancelBufferTask();
            me.loadCurrentPage();
        }
    },
 
    onNextPageTap: function() {
        var nextPage = this.getCurrentPage() + 1;
 
        if (nextPage <= this.getTotalPages()) {
            this.setCurrentPage(nextPage);
        }
    },
 
    onPreviousPageTap: function() {
        var previousPage = this.getCurrentPage() - 1;
 
        if (previousPage > 0) {
            this.setCurrentPage(previousPage);
        }
    },
 
    onTotalCountChange: function(store) {
        var me = this,
            data = me.getPageData();
 
        me.bulkConfigs = true;
        me.setConfig(data);
        me.bulkConfigs = false;
        me.syncSummary();
    },
 
    onUpdateVisibleCount: function(grid, visibleCount) {
        var store = grid.getStore(),
            totalCount;
 
        if (store && !this.getLoadPages()) {
            visibleCount -= 1;
            this.setPageSize(visibleCount);
            totalCount = store.getTotalCount() || store.getCount();
            this.setTotalPages(Math.ceil(totalCount / visibleCount));
        }
    },
 
    updateTotalPages: function() {
        if (!this.isConfiguring) {
            this.syncSummary();
        }
    },
 
    updateCurrentPage: function(page) {
        var me = this,
            isDragging = me.isDragging,
            bufferTask = me.bufferTask,
            buffer = me.getBuffer();
 
        if (!me.isConfiguring) {
            if (me.getLoadPages()) {
                if (bufferTask && Ext.isNumber(buffer) && isDragging) {
                    bufferTask.delay(buffer);
                }
                else if (buffer !== 'dragend' || !isDragging) {
                    me.getGrid().getStore().loadPage(page);
                }
            }
            else {
                me.syncSummary();
            }
        }
    },
 
    updateTotalCount: function(totalCount) {
        if (!this.isConfiguring) {
            this.syncSummary();
        }
    },
 
    getPageTopRecord: function(page) {
        var grid = this.getGrid(),
            store = grid && grid.getStore(),
            pageSize = this.getPageSize(),
            pageTopRecordIndex = (page - 1) * pageSize;
 
        return store && store.getAt(pageTopRecordIndex);
    },
 
    privates: {
        syncSummary: function() {
            var me = this,
                grid = me.getGrid(),
                toolbar = me.getToolbar(),
                sliderField = toolbar.getSliderField(),
                currentPage = me.getCurrentPage(),
                totalPages = me.getTotalPages(),
                pageTopRecord;
 
            if (me.bulkConfigs) {
                return;
            }
 
            // TODO: Calling setHtml causes a performance issue while live scrolling,
            // this might be worth looking into.
            toolbar.getSummaryComponent().element.dom.innerHTML = currentPage + ' / ' + totalPages;
 
            sliderField.setMaxValue(totalPages || 1);
            sliderField.setValue(currentPage);
            sliderField.setDisabled(totalPages <= 1);
 
            pageTopRecord = me.getPageTopRecord(currentPage);
 
            if (grid && !me.preventGridScroll && pageTopRecord) {
                grid.scrollToRecord(pageTopRecord);
            }
 
            toolbar.getNextButton().setDisabled(currentPage === totalPages);
            toolbar.getPrevButton().setDisabled(currentPage === 1);
        }
    }
});