Ext.namespace('Pelorous.publishing');
Pelorous.includeScript('/javascript/external/plugins/extjs/Ext.ux.grid.BufferView.js');
        
/**
 * Publish manager
 * 
 */
Pelorous.publishing = function (){
    var cp = new Ext.state.CookieProvider({ expires: null });
    
    var updatesWatcher,
        updatesWatcherTimeout = 5000,
        progressWatcher,
        progressWatcherTimeout = 1000,
        unpublishedWidgetVisible = false,
        currentStatus = {},
        currentServer,
        currentWinView,
        currentUserId = 0,
        selectedBatchId,
        publishingWin,
        doingPublish = false;
        
    var currentServerId = cp.get('serverId', false) || 0;
    var currentUserGenServerId = cp.get('userGenServerId', false) || 0;
    
    var WIN_VIEW_UNPUBLISHED = 'unpublished',
        WIN_VIEW_OPEN_BATCHES = 'openBatches',
        WIN_VIEW_PENDING_BATCHES = 'pendingBatches';
    
    var servers = null,
        userGenServers = null,
        serversCookieId = 'servers',
        userGenServersCookieId = 'userGenServers',
        serversCombo = null;
        serversStore = new Ext.data.ArrayStore({
            fields: [ 
                'serverId', 'name', 'workflowId', 'step', 'final', 
                'disablePublishingConfirmation',
                'allowIndividualUpdates', 'workflowPreviousServerId', 
                'workflowNextServerId' 
            ]
        });
        userGenServersCombo = null;
        userGenServersStore = new Ext.data.ArrayStore({
            fields: [ 
                'serverId', 'name', 'workflowId', 'step', 'final', 
                'disablePublishingConfirmation',
                'allowIndividualUpdates', 'workflowPreviousServerId', 
                'workflowNextServerId'
            ]
        });
        
    var updateTypesCombo;
    var updateTypesStore = new Ext.data.JsonStore({
        url: '/publishing/xhr_get_update_types/',
        root: 'items',
        fields: [ 'value', 'text' ]
    });
    
    var usersStoreLoaded = false, usersCombo;
    var usersStore = new Ext.data.JsonStore({
        url: '/publishing/xhr_get_users/',
        root: 'items',
        fields: [ 'value', 'text', 'selected' ]
    });
    usersStore.on('load', function() {
        usersStore.each(function(record) {
            if (record.get('value') == currentUserId) {
                usersCombo.setValue(record.get('value'));
                return;
            }
        });
        
        if (!usersStoreLoaded) {
            getUnpublishedUpdates();
        }
        usersStoreLoaded = true;
    });
    
    var batchesStore = new Ext.data.JsonStore({
        url: '/publishing/xhr_get_batches/',
        baseParams: { open: 1 },
        fields: [ 
            'batchId', 'serverId', 'name', 'description', 'numItems', 
            'numUpdates', 'createdDate', 'createdBy', 'updatedDate', 'updatedBy' 
        ]
    });
    batchesStore.on('load', function(s) {
        // build batches select
        var select = '<select id="addToBatchSelect">'
                   + '<option value="0"></option>';
            
        var selected;             
        s.each(function(record) {
             selected = ''; 
             if (record.get('batchId') == selectedBatchId) {
                 selected = ' selected="selected"';
             }
             select+= '<option value="' + record.get('batchId') + '"'
                    + selected + '>'
                    + record.get('updatedDate') + ' - '
                    + record.get('name')
                    + '</option>';
        });
        
        select+= '<option value="create">start new</option>'
              + '<option value="0">--------------</option>'
              + '<option value="direct">do not batch</option>'
              + '</select>';
              
        // update selector with new html
        var container = Ext.fly('batchSelector');
        if (container) {
            container.dom.innerHTML = select;
        }
    });
    var batchesSelectionModel = new Ext.grid.CheckboxSelectionModel({
        checkOnly: true
    });
    
    var pendingBatchesStore = new Ext.data.JsonStore({
        url: '/publishing/xhr_get_batches/',
        baseParams: { open: 0 },
        fields: [ 
            'batchId', 'serverId', 'name', 'description', 'numItems', 
            'numUpdates', 'createdDate', 'createdBy', 'updatedDate', 'updatedBy' 
        ]
    });
    var pendingBatchesSelectionModel = new Ext.grid.CheckboxSelectionModel({
        checkOnly: true
    });
    
    
    var batchItemsStore = new Ext.data.JsonStore({
        url: '/publishing/batches/xhr_get_items/',
        fields: [ 
            'updateId', 
            'itemId', 
            'type', 
            'message', 
            'updatedBy',
            'updatedDate'
        ]
    });
    var batchItemsSelectionModel = new Ext.grid.CheckboxSelectionModel({
        checkOnly: true
    });

    var updatesGrid = null;
    var updatesStore = new Ext.data.JsonStore({
        url: '/publishing/xhr_get_unpublished_updates/',
        root: 'items',
        fields: [ 
            'updateId', 
            'itemId', 
            'type', 
            'message', 
            'numUpdates', 
            'updatedBy',
            'updatedDate'
        ],
        totalProperty: 'totalCount'
    });
    
    var sm = new Ext.grid.CheckboxSelectionModel({
        checkOnly: true
    });
    
    /**
     * Fetch list of servers
     * 
     * @param   {Function} callback
     * @return  void
     */
    var fetchServers = function(callback) {
        // make sure we have a valid callback function
        if (typeof callback == 'undefined') {
            callback = function() {};
        }
        
        servers = [];
        
        servers = cp.get(serversCookieId);
        if (servers) {
            serversStore.loadData(servers);
            try {
                callback();
            } catch (e) {
                if (console.log) {
                    console.log(e);
                }
            }
            return;
        }
        
        Ext.Ajax.request({
            url: '/publishing/xhr_get_servers/',
            params: {
                publishing: 1    
            },
            success: function(r) {
                try {
                    var response = Ext.decode(r.responseText);
                    
                    cp.set(serversCookieId, response);
                    servers = response;
                    serversStore.loadData(servers);

                    callback();
                } catch (e) {
                    if (console.log) {
                        console.log(e);
                    }
                }
            }
        });
    };
    
    /**
     * Fetch list of user generated servers
     * 
     * @param   {Function} callback
     * @return  void
     */
    var fetchUserGeneratedServers = function(callback) {
        // make sure we have a valid callback function
        if (typeof callback == 'undefined') {
            callback = function() {};
        }
        
        userGenServers = [];
        
        userGenServers = cp.get(userGenServersCookieId);
        if (userGenServers) {
            userGenServersStore.loadData(userGenServers);
            try {
                callback();
            } catch (e) {
                if (console.log) {
                    console.log(e);
                }
            }
            return;
        }
        
        Ext.Ajax.request({
            url: '/publishing/xhr_get_servers/',
            params: {
                userGenerated: 1    
            },
            success: function(r) {
                try {
                    var response = Ext.decode(r.responseText);
                    
                    cp.set(userGenServersCookieId, response);
                    userGenServers = response;
                    userGenServersStore.loadData(userGenServers);

                    callback();
                } catch (e) {
                    if (console.log) {
                        console.log(e);
                    }
                }
            }
        });
    };
    
    /**
     * Fade publish icon
     * 
     * @return  void
     */
    var publishIconFade = function(times) {
        for (var i = 0; i < times; i++) {
            Ext.select('.publishIcon').fadeOut({
                endOpacity: 0, 
                easing: 'easeOut',
                duration: 0.75,
                remove: false,
                useDisplay: true
            });
            Ext.select('.publishIcon').fadeIn({
                endOpacity: 1, 
                easing: 'easeOut',
                duration: 0.75
            });
        }
    };
    
    /**
     * Check for unpublished updates
     * 
     * @return  void
     */
    var checkForUpdates = function() {
        Ext.Ajax.request({
            url: '/publishing/xhr_check_unpublished/',
            success: function(r) {
                try {
                    var response = Ext.decode(r.responseText);
                    
                    // update widget text with totals
                    var text = '', server;
                    if (response.total > 0) {
                        var i = 0;
                        for (server in response.breakdown) {
                            var data = response.breakdown[server];
                            if (data.count === 0) {
                                continue;
                            }
                            text+= (i > 0) ? ', ' : '';
                            text+= '<a class="plan clickable" '
                                 + 'href="javascript: '
                                 + 'Pelorous.publishing.displayPublishingWin(' 
                                 + data.serverId + ');" '
                                 + 'planid="' + data.serverId + '">' 
                                 + server 
                                 + ' <span class="number">('
                                 + data.count
                                 + ')</span></a>';

                            i++;
                        }
                    }
                    Ext.select('#publishingNotification .items').update(
                        text
                    );
                    
                    // we have unpublished updates and widget is currently
                    // not visible - need to display it
                    if (response.total > 0 && !unpublishedWidgetVisible) {
                        Ext.fly('publishingNotification').move(
                            'down', 30, 
                            { duration: 0.5, easing: 'backBoth' }
                        );
                        unpublishedWidgetVisible = true;
                        
                    // there are no unpublished updates and widget is 
                    // currently visible - need to hide it
                    } else if (response.total === 0 && unpublishedWidgetVisible) {
                        Ext.fly('publishingNotification').move(
                            'up', 30, 
                            { duration: 0.5, easing: 'backBoth' }
                        );
                        unpublishedWidgetVisible = false;
                        
                    // widget is currently visible and there are still
                    // unpublished updates - check to see if numbers have
                    // changed, and if so do "alert" animation
                    } else {
                        var displayAlert = false;
                        
                        for (server in response.breakdown) {
                            if (!currentStatus[server]) {
                                displayAlert = true;
                                break;
                            }
                            if (response.breakdown[server].count != currentStatus[server].count) {
                                displayAlert = true;
                                break;
                            }
                        }
                        
                        if (displayAlert) {
                            publishIconFade(3);
                        }
                    }

                    currentStatus = response.breakdown;
                    if (!publishingWin || !publishingWin.isVisible()) {
                        updatesWatcher = setTimeout(
                            checkForUpdates, updatesWatcherTimeout
                        );
                    }
                } catch (e) {}
            }
        });
    };
    
    /**
     * Get updates types combo
     * 
     * @return  Ext.form.ComboBox
     */
    var getUpdateTypesCombo = function(){
        updateTypesCombo = new Ext.form.ComboBox({
            store: updateTypesStore,
            valueField: 'value',
            displayField: 'text',
            mode: 'remote',
            editable: false,
            triggerAction: 'all',
            forceSelection: true,
            selectOnFocus: true,
            width: 120
        });
        updateTypesCombo.on('select', function(){
            getUnpublishedUpdates();
        });
        
        return updateTypesCombo;
    };
    
    /**
     * Get users combo
     * 
     * @return  Ext.form.ComboBox
     */
    var getUsersCombo = function(){
        usersCombo = new Ext.form.ComboBox({
            store: usersStore,
            valueField: 'value',
            displayField: 'text',
            mode: 'local',
            editable: false,
            triggerAction: 'all',
            forceSelection: true,
            selectOnFocus: true,
            width: 200
        });
        usersCombo.on('select', function(combo, record){
            currentUserId = record.get('value');
            getUnpublishedUpdates();
        });
        
        return usersCombo;
    };
    
    
        
    /**
     * Display publishing win
     * 
     * @param {Number} serverId
     * @return void
     */
    var displayPublishingWin = function(serverId) {
        // set current server 
        currentServerId = serverId;
        serversStore.each(function(record) {
           if (record.get('serverId') == serverId) {
               currentServer = record;
           } 
        });
            
        Ext.QuickTips.init();
        
        batchesStore.setBaseParam('serverId', currentServerId);
        batchesStore.removeAll();
        if (currentServer.get('workflowNextServerId') > 0
            || currentServer.get('workflowPreviousServerId') > 0) {
            batchesStore.load();   
        }
        
        // clear batch selections - use try/catch in case store has not
        // been associated yet
        try {
            batchesSelectionModel.clearSelections();
        } catch (e) {}
        
        pendingBatchesStore.removeAll();
        if (currentServer.get('workflowPreviousServerId') > 0) {
            pendingBatchesStore.setBaseParam(
                'serverId', currentServer.get('workflowPreviousServerId')
            );
        }
        
        // clear pending batch selections - use try/catch in case store has 
        // not been associated yet
        try {
            pendingBatchesSelectionModel.clearSelections();
        } catch (e) {}
    
        var tabPanel = new Ext.TabPanel({
            activeTab: 0,
            border: false,
            defaults: { border: false }
        });

        publishingWin = new Ext.Window({
            id: 'pelorousPublishingWin',
            layout: 'fit',
            width: 800,
            height: 350,
            modal: true,
            shadow: false,
            items: [ tabPanel ],
            bbar: new Ext.Toolbar({
                items: [
                    '<span id="publishStatus"></span>', '->',
                    '<span id="batchSelectorLabel">Batch: </span><span id="batchSelector"></span>', 
                {
                    id: 'addToBatchButton',
                    text: 'Add To Existing Batch',
                    iconCls: 'add-to-batch-icon',
                    handler: function() {
                        displayBatchSelectDialogue(
                            currentServer.get('workflowPreviousServerId'), 
                        {
                            title: 'Add To Existing Batch',
                            buttonText: 'Add',
                            buttonHandler: confirmAddToBatch
                        });
                    },
                    hidden: true
                },{
                    id: 'publishButton',
                    text: 'Publish',
                    iconCls: 'publish-icon',
                    handler: confirmPublish
                },{
                    id: 'closeBatchesButton',
                    text: 'Close Selected',
                    iconCls: 'close-batch-icon',
                    handler: confirmCloseBatches,
                    hidden: true
                },{
                    id: 'reopenBatchesButton',
                    text: 'Reopen Selected',
                    iconCls: 'reopen-batch-icon',
                    handler: confirmReopenBatches,
                    hidden: true
                },{
                    id: 'publishBatchesButton',
                    text: 'Publish Selected',
                    iconCls: 'publish-icon',
                    handler: confirmPublishBatches,
                    hidden: true
                }]
            })
        });
        publishingWin.on('beforeshow', function() {
            updatesStore.removeAll();
        });
        publishingWin.on('show', function() {
            //updateTypesCombo.setValue('');
            
            // first call to getUnpublishedUpdates() is done via
            // "on load" listener on usersStore
            if (usersStoreLoaded) {
                getUnpublishedUpdates();
            }
            
            if (!progressWatcher) {
                getPublishProgress();
            }
    
            var workflowId = currentServer.get('workflowId');
            var step = currentServer.get('step');
            var isFinal = currentServer.get('final');
            var allowIndividualUpdates = currentServer.get('allowIndividualUpdates');
        
            // enable/disable tabs and set initial view
            if (workflowId == 0) {
                tabPanel.add(getUnpublishedChangesTab());
                tabPanel.remove('openBatchesTab', true);
                tabPanel.remove('pendingBatchesTab', true);
                currentWinView = WIN_VIEW_UNPUBLISHED;
                
            } else {
                currentWinView = false;
                
                if (allowIndividualUpdates == 0) {
                    tabPanel.remove('unpublishedChangesTab', true);
                } else {
                    tabPanel.add(getUnpublishedChangesTab());
                    currentWinView = WIN_VIEW_UNPUBLISHED;
                }
                
                if (step == 1) {
                    tabPanel.remove('pendingBatchesTab', true);
                } else {
                    tabPanel.add(getBatchesTab(WIN_VIEW_PENDING_BATCHES));
                    if (!currentWinView) {
                        currentWinView = WIN_VIEW_PENDING_BATCHES;
                    }
                }
                
                if (isFinal == 1) {
                    tabPanel.remove('openBatchesTab', true);
                } else {
                    tabPanel.add(getBatchesTab(WIN_VIEW_OPEN_BATCHES));
                    if (!currentWinView) {
                        currentWinView = WIN_VIEW_OPEN_BATCHES;
                    }
                }
            }
            
            // activate first tab and set buttons
            tabPanel.setActiveTab(0);
            
            setWinButtons();
    
        });
        publishingWin.on('close', function() {
            updatesWatcher = setTimeout(checkForUpdates, 0);
            clearTimeout(progressWatcher);  
            progressWatcher = null;
        });
        
        publishingWin.setTitle('Publish To ' + currentServer.get('name'));
        publishingWin.show();
    };
    
    /**
     * Get unpublished changes tab
     * 
     * @param   {Ext.data.Record} record
     * @return  void
     */
    var displayBatchItemsWin = function(record) {
        batchItemsStore.removeAll();
        batchItemsStore.setBaseParam('batchId', record.get('batchId'));
        
        var serverId = record.get('serverId');
        
        var grid = new Ext.grid.GridPanel({
            border: false,
            store: batchItemsStore,
            sm: batchItemsSelectionModel,
            columns: [
                batchItemsSelectionModel, { 
                    header: 'ID', 
                    dataIndex: 'updateId',
                    width: 50
                }, { 
                    header: 'Date', 
                    dataIndex: 'updatedDate' 
                },{ 
                    header: 'Type', 
                    dataIndex: 'type', 
                    width: 140 
                },{ 
                    id: 'message', 
                    header: 'Message', 
                    dataIndex: 'message' 
                },{ 
                    header: 'Updated By', 
                    dataIndex: 'updatedBy', 
                    width: 200 
                }
            ],
            enableHdMenu: false,
            autoExpandColumn: 'message',
            loadMask: true,
            stripeRows: true,
            view: new Ext.ux.grid.BufferView({
                scrollDelay: false
            })
        });
        grid.on('afterrender', function() {
            batchItemsStore.load(); 
        });
        
        var win = new Ext.Window({
            title: 'Batch Updates &rsaquo;&rsaquo; ' + record.get('name'),
            layout: 'fit',
            width: 800,
            height: 350,
            modal: true,
            shadow: false,
            items: [ grid ],
            bbar: ['->', {
                text: 'Move To Other Batch',
                iconCls: 'add-to-batch-icon',
                handler: function() {
                    displayBatchSelectDialogue(serverId, {
                        title: 'Move To Another Batch',
                        buttonText: 'Move',
                        buttonHandler: confirmMoveToAnotherBatchAction,
                        params: {
                            sourceBatchId: record.get('batchId')
                        }
                    });
                }  
            },{
                text: 'Move Out Of Batch',
                iconCls: 'move-out-of-batch-icon',
                handler: function(){
                    confirmMoveOutOfBatch(record.get('batchId'));
                }
            }]
        });
        win.show();
    };
    
    /**
     * Get unpublished changes tab
     * 
     * @return  Ext.Panel
     */
    var getUnpublishedChangesTab = function() {
        usersStore.load();
        
        var cm = new Ext.grid.ColumnModel({
            defaults: {
                width: 120,
                sortable: false
            },
            columns: [
                sm, { 
                    header: 'ID', 
                    dataIndex: 'updateId',
                    width: 50
                }, { 
                    header: 'Date', 
                    dataIndex: 'updatedDate' 
                },{ 
                    header: 'Type', 
                    dataIndex: 'type', 
                    width: 140 
                },{ 
                    id: 'message', 
                    header: 'Message', 
                    dataIndex: 'message' 
                },{ 
                    header: 'Updated By', 
                    dataIndex: 'updatedBy', 
                    width: 200 
                }
            ]
        });
    
        // updates grid
        updatesGrid = new Ext.grid.GridPanel({
            border: false,
            store: updatesStore,
            sm: sm,
            cm: cm,
            enableHdMenu: false,
            autoExpandColumn: 'message',
            loadMask: true,
            stripeRows: true,
            view: new Ext.ux.grid.BufferView({
                scrollDelay: false
            })
        });
        
        var panel = new Ext.Panel({
            id: 'unpublishedChangesTab',
            title: 'Changes',
            layout: 'fit',
            border: false,
            items: [ updatesGrid ],
            tbar: [ 
                'Type: ', getUpdateTypesCombo(),
                ' ', 'User: ', getUsersCombo(),
                '->',{
                    id: 'discardButton',
                    text: 'Discard',
                    handler: confirmDiscard
                }
            ]
        });
        panel.on('afterrender', function() {
            if (currentServer.get('final') !== 1) {
                batchesStore.load();
            }
        });
        panel.on('activate', function() {
            currentWinView = WIN_VIEW_UNPUBLISHED;
            setWinButtons();
        });
        
        return panel;
    };
    
    /**
     * Confirm batch closure
     * 
     * @return  void
     */
    var confirmPublish = function() {
        var batchId = 0;
        
        if (currentServer.get('workflowNextServerId') > 0) {
            var elem = Ext.fly('addToBatchSelect');
            if (elem) {
                batchId = elem.getValue();
                
                if (batchId == 0) {
                    alert('Please select a batch to continue');
                    return;
                }
                
                // create new batch
                if (batchId == 'create') {
                    displayBatchCreateDialogue(currentServerId, confirmPublish);
                    return;
                }
                
                // direct publish
                if (batchId == 'direct') {
                    batchId = 0;
                }
            } else {
                batchId = 0;
            }
        }
        
        // publish now if confirmation is disabled
        if (currentServer.get('disablePublishingConfirmation') == 1) {
            publish(batchId);
            return;
        }

        $message = 'You are about to publish the selected items to '
                 + '<span class="pubishingConfirmServer">' 
                 + currentServer.get('name') + '</span>.<br /> '
                 + 'Are you sure you want to continue?';
        Ext.MessageBox.confirm('Publish Items', $message, function(btn){
            if (btn == 'yes') {
                publish(batchId);
            }
        });
    };
    
    /**
     * Publish changes to current server and add to batch
     * 
     * @param   {Number} batchId
     * @return  void
     */
    var publish = function(batchId) {
        var params = { 
            serverId: currentServerId,
            batchId: batchId
        };
        
        var i = 0, key, itemId, updateId;
        if (currentWinView == WIN_VIEW_UNPUBLISHED) {
            sm.each(function(record, index){
                key = 'items[' + i + ']';
                params[key + '[itemId]'] = record.get('itemId');
                params[key + '[updateId]'] = record.get('updateId');
                i++;
            });
        }
        
        var refreshPendingBatches = false;
        if (currentWinView == WIN_VIEW_PENDING_BATCHES) {
            pendingBatchesSelectionModel.each(function(record){
                key = 'items[' + i + ']';
                params[key + '[batchId]'] = record.get('batchId');
                i++;
                
                refreshPendingBatches = true;
            });
        }
      
        Ext.Ajax.request({
            url: '/publishing/xhr_publish/',
            params: params, 
            success: function(r) {
                try {
                    var response = Ext.decode(r.responseText);
                    if (response.success) {
                        getUnpublishedUpdates();
                        getPublishProgress();
                        
                        if (refreshPendingBatches) {
                            pendingBatchesStore.load();
                        }
                    } else {
                        Pelorous.statusMessage(response.content);
                    }
                    
                } catch (e) {
                    Pelorous.statusMessage(r.responseText);
                }
            }
        });
    };
    
    /**
     * Confirm "add to batch"
     * 
     * @param   {Ext.Window} win
     * @param   {Number} batchId
     * @return  void
     */
    var confirmAddToBatch = function(win, batchId) {
        $message = 'Are you sure you want to add the selected updates to '
                 + 'this batch?';
        Ext.MessageBox.confirm('Add To Existing Batch', $message, function(btn){
            if (btn == 'yes') {
                addToBatch(win, batchId);
            }
        });
    };
    
    /**
     * Add to batch
     * 
     * @param   {Ext.Window} win
     * @param   {Number} batchId
     * @return  void
     */
    var addToBatch = function(win, batchId) {
        var params = { batchId: batchId };
        
        var i = 0;
        sm.each(function(record, index){
            key = 'updates[' + i + ']';
            params[key] = record.get('updateId');
            i++;
        });
      
        Ext.Ajax.request({
            url: '/publishing/batches/xhr_add_to_batch/',
            params: params,
            success: function(r) {
                try {
                    var response = Ext.decode(r.responseText);
                    if (response.success) {
                        win.close();
                        getUnpublishedUpdates();
                        pendingBatchesStore.load();
                    } else {
                        Pelorous.statusMessage(response.content);
                    }
                    
                } catch (e) {
                    Pelorous.statusMessage(r.responseText);
                }
            }
        });
    };
    
    /**
     * Display batch select dialogue
     * 
     * @param   {Number} serverId
     * @param   {Object} config
     * @return  void
     */
    var displayBatchSelectDialogue = function(serverId, config) {
        // set some defaults
        config.title = config.title || '';
        config.buttonText = config.buttonText || '';
        config.buttonIconCls = config.buttonIconCls || 'create-icon';
        config.buttonHandler = config.buttonHandler || {};
        config.params = config.params || {};
        
        var win = new Ext.Window({
            title: config.title,
            layout: 'fit',
            width: 300,
            height: 120,
            modal: true,
            shadow: false,
            tbar: [{
                text: '<b>' + config.buttonText + '</b>',
                iconCls: config.buttonIconCls,
                handler: function() {
                    var form = Ext.fly('batchSelectForm');
                    var batchId = form.select('#batchId').first().getValue();
                    
                    if (batchId == 'create') {
                        selectedBatchId = null;
                        displayBatchCreateDialogue(serverId, function() {
                            // call button handler
                            config.buttonHandler(win, selectedBatchId, config.params);
                            
                            // re-load batch select window so that it shows
                            // newly created batch
                            win.load({
                                params: { 
                                    serverId: serverId,
                                    batchId: selectedBatchId 
                                },
                                url: '/publishing/xhr_batch_select/'
                            });
                        });
                        return;
                    }
                    
                    config.buttonHandler(win, batchId, config.params);
                }
            }]
        });
        win.on('show', function() {
            win.load({
                params: { 
                    serverId: serverId,
                    batchId: selectedBatchId 
                },
                url: '/publishing/xhr_batch_select/'
            });
        });
        
        win.show();
    };
    
    /**
     * Confirm "move to another batch" action
     * 
     * @param   {Ext.Window} win
     * @param   {Number} batchId
     * @param   {Object} params
     * @return  void
     */
    var confirmMoveToAnotherBatchAction = function(win, batchId, params) {
        $message = 'Are you sure you want to move the selected updates to '
                 + 'this batch?';
        Ext.MessageBox.confirm('Confirm Move', $message, function(btn){
            if (btn == 'yes') {
                moveToAnotherBatch(win,  params.sourceBatchId, batchId);
            }
        });
    };
    
    /**
     * Move updates to another batch
     * 
     * @param   {Ext.Window} win
     * @param   {Number} batchId
     * @param   {Number} destinationBatchId
     * @return  void
     */
    var moveToAnotherBatch = function(win, batchId, destinationBatchId) {
        var params = { 
            batchId: batchId,
            destinationBatchId: destinationBatchId
        };
        
        batchItemsSelectionModel.each(function(record, index){
            key = 'updates[' + record.get('itemId') + ']';
            params[key] = record.get('updateId');
        });
      
        Ext.Ajax.request({
            url: '/publishing/batches/xhr_move_to_another_batch/',
            params: params,
            success: function(r) {
                try {
                    var response = Ext.decode(r.responseText);
                    if (response.success) {
                        win.close();
                        batchItemsStore.load();
                        batchesStore.load();
                        if (pendingBatchesStore.getCount() > 0) {
                            pendingBatchesStore.load();
                        }
                    } else {
                        Pelorous.statusMessage(response.content);
                    }
                    
                } catch (e) {
                    Pelorous.statusMessage(r.responseText);
                }
            }
        });
    };
    
    /**
     * Confirm "move out of batch" action
     * 
     * @param   {Number} batchId
     * @return  void
     */
    var confirmMoveOutOfBatch = function(batchId) {
        $message = 'Are you sure you want to move the selected updates out of '
                 + 'this batch?';
        Ext.MessageBox.confirm('Confirm Move', $message, function(btn){
            if (btn == 'yes') {
                moveOutOfBatch(batchId);
            }
        });
    };
    
    /**
     * Move updates out of batch
     * 
     * @param   {Number} batchId
     * @return  void
     */
    var moveOutOfBatch = function(batchId) {
        var params = { batchId: batchId };
        
        batchItemsSelectionModel.each(function(record, index){
            key = 'updates[' + record.get('itemId') + ']';
            params[key] = record.get('updateId');
        });
      
        Ext.Ajax.request({
            url: '/publishing/batches/xhr_move_out_of_batch/',
            params: params,
            success: function(r) {
                try {
                    var response = Ext.decode(r.responseText);
                    if (response.success) {
                        batchItemsStore.load();
                        batchesStore.load();
                        if (pendingBatchesStore.getCount() > 0) {
                            pendingBatchesStore.load();
                        }
                        getUnpublishedUpdates();
                    } else {
                        Pelorous.statusMessage(response.content);
                    }
                    
                } catch (e) {
                    Pelorous.statusMessage(r.responseText);
                }
            }
        });
    };
    
    /**
     * Display create new batch dialogue
     * 
     * @param   {Number} serverId
     * @param   {Function} callback
     * @return  void
     */
    var displayBatchCreateDialogue = function(serverId, callback) {
        var win = new Ext.Window({
            title: 'Create New Batch',
            layout: 'fit',
            width: 400,
            height: 200,
            modal: true,
            shadow: false,
            tbar: [{
                text: '<b>Create</b>',
                iconCls: 'create-icon',
                handler: function(){
                    createBatch(serverId, callback, win);
                }
            }]
        });
        win.on('show', function() {
            win.load({
                params: { '_noJson': 1 },
                url: '/publishing/batches/xhr_create/'
            });
        });
        
        win.show();
    };
    
    /**
     * Create new batch
     * 
     * @param   {Number} serverId
     * @param   {Function} callback
     * @param   {Ext.Window} win
     * @return  void
     */
    var createBatch = function(serverId, callback, win) {
        callback = callback || {};
        selectedBatchId = null;
        
        var loadMask = new Ext.LoadMask(
            win.getId(), { msg: 'Creating...' }
        );
        loadMask.show();
        Ext.Ajax.request({
            url: '/publishing/batches/xhr_create',
            params: {
                create: 1,
                serverId: serverId
            },
            form: 'createBatchForm',
            success: function(r) {
                loadMask.hide();
                try {
                    var response = Ext.decode(r.responseText);
                    if (response.success) {
                        selectedBatchId = response.batchId;
                        batchesStore.load({
                            callback: callback
                        });
                        win.close();
                    } else {
                        Pelorous.statusMessage(response.content);
                    }
                } catch (e) {
                    Pelorous.statusMessage(r.responseText);
                }
            }
        });
    };
    
    /**
     * Confirm batch closure
     * 
     * @return  void
     */
    var confirmCloseBatches = function() {
        $message = 'Are you sure you want to close the selected batches?';
        Ext.MessageBox.confirm('Close Selected Batches', $message, function(btn){
            if (btn == 'yes') {
                closeBatches();
            }
        });
    };
    
    /**
     * Close selected batches
     * 
     * @return  void
     */
    var closeBatches = function() {
        var params = { serverId: currentServerId };
        
        var i = 0;
        batchesSelectionModel.each(function (record) {
            params['batches[' + i + ']'] = record.get('batchId');
            i++;
        });
      
        Ext.Ajax.request({
            url: '/publishing/batches/xhr_close/',
            params: params, 
            success: function(r) {
                try {
                    var response = Ext.decode(r.responseText);
                    if (response.success) {
                        batchesStore.load();
                        checkForUpdates();
                    } else {
                        Pelorous.statusMessage(response.content);
                    }
                    
                } catch (e) {
                    Pelorous.statusMessage(r.responseText);
                }
            }
        });
    };
    
    /**
     * Confirm batch re-open
     * 
     * @return  void
     */
    var confirmReopenBatches = function() {
        $message = 'Are you sure you want to re-open the selected batches?';
        Ext.MessageBox.confirm('Re-open Selected Batches', $message, function(btn){
            if (btn == 'yes') {
                reopenBatches();
            }
        });
    };
    
    /**
     * Re-open selected batches
     * 
     * @return  void
     */
    var reopenBatches = function() {
        var params = { serverId: currentServerId };
        
        var i = 0;
        pendingBatchesSelectionModel.each(function (record) {
            params['batches[' + i + ']'] = record.get('batchId');
            i++;
        });
      
        Ext.Ajax.request({
            url: '/publishing/batches/xhr_reopen/',
            params: params, 
            success: function(r) {
                try {
                    var response = Ext.decode(r.responseText);
                    if (response.success) {
                        pendingBatchesStore.load();
                        checkForUpdates();
                    } else {
                        Pelorous.statusMessage(response.content);
                    }
                    
                } catch (e) {
                    Pelorous.statusMessage(r.responseText);
                }
            }
        });
    };
    
    /**
     * Confirm batch publish
     * 
     * @return  void
     */
    var confirmPublishBatches = function() {
        $message = 'You are about to publish the selected batches to '
                 + '<span class="pubishingConfirmServer">' 
                 + currentServer.get('name') + '</span>.<br /> '
                 + 'Are you sure you want to continue?';
        Ext.MessageBox.confirm('Publish Batches', $message, function(btn){
            if (btn == 'yes') {
                publish();
            }
        });
    };
    
    /**
     * Confirm batch/update discard
     * 
     * @return  void
     */
    var confirmDiscard = function() {
        var message = 'Are you sure you want to discard the selected ';
        
        var discardFunction;
        if (currentWinView == WIN_VIEW_UNPUBLISHED) {
            message+= 'updates?';
            discardFunction = discardUpdates;
        } else {
            message+= 'batches?';
            discardFunction = discardBatches;
        }

        Ext.MessageBox.confirm('Discard Confirmation', message, function(btn){
            if (btn == 'yes') {
                discardFunction();
            }
        });
    };
    
    
    /**
     * Discard selected updates
     * 
     * @return  void
     */
    var discardUpdates = function() {
        var params = { 
            serverId: currentServerId 
        };
        
        var key;
        sm.each(function(record, index){
            key = 'updates[' + record.get('updateId') + ']';
            params[key] = record.get('itemId');
        });
        
        Ext.Ajax.request({
            url: '/publishing/xhr_discard_updates/',
            params: params, 
            success: function(r) {
                try {
                    var response = Ext.decode(r.responseText);
                    if (response.success) {
                        getUnpublishedUpdates();
                        getPublishProgress();
                    } else {
                        Pelorous.statusMessage(response.content);
                    }
                    
                } catch (e) {
                    Pelorous.statusMessage(r.responseText);
                }
            }
        });
    };
    
    
    /**
     * Discard selected batches
     * 
     * @return  void
     */
    var discardBatches= function() {
        var params = { 
            serverId: currentServerId 
        };
        
        var store, sm, key;
        if (currentWinView == WIN_VIEW_PENDING_BATCHES) {
            store = pendingBatchesStore;
            sm = pendingBatchesSelectionModel;
        } else {
            store = batchesStore;
            sm = batchesSelectionModel;
        }
        sm.each(function(record){
            key = 'batches[' + record.get('batchId') + ']';
            params[key] = record.get('batchId');
        });
        
        Ext.Ajax.request({
            url: '/publishing/xhr_discard_batches/',
            params: params, 
            success: function(r) {
                try {
                    var response = Ext.decode(r.responseText);
                    if (response.success) {
                        getUnpublishedUpdates();
                        getPublishProgress();
                        store.load();
                        
                    } else {
                        Pelorous.statusMessage(response.content);
                    }
                    
                } catch (e) {
                    Pelorous.statusMessage(r.responseText);
                }
            }
        });
    };
        /**
     * Get batches tab
     * 
     * @param   {string} type
     * @return  Ext.Panel
     */
    var getBatchesTab = function(type) {
        var id, title, store, sm;
        
        switch (type) {
            case WIN_VIEW_PENDING_BATCHES:
                id = 'pendingBatchesTab';
                title = 'Pending Batches';
                store = pendingBatchesStore;
                sm = pendingBatchesSelectionModel;
                break;
                
            case WIN_VIEW_OPEN_BATCHES:
                id = 'openBatchesTab';
                title = 'Open Batches';
                store = batchesStore;
                sm = batchesSelectionModel;
                break;
        }
        
        var grid = new Ext.grid.GridPanel({
            border: false,
            store: store,
            columns: [sm,{ 
                header: 'Last Updated', 
                dataIndex: 'updatedDate', 
                width: 150 
            },{ 
                id: 'name',
                header: 'Name', 
                dataIndex: 'name',
                renderer: function (value, cell, record) {
                    cell.attr = 'ext:qtip="' + record.get('description') + '"';
                    return value;
                }
            },{ 
                header: 'Items', 
                dataIndex: 'numItems', 
                width: 60,
                align: 'center'
            },{ 
                header: 'Updates', 
                dataIndex: 'numUpdates', 
                width: 60,
                align: 'center'
            },{ 
                header: 'Updated By', 
                dataIndex: 'updatedBy', 
                width: 200 
            }],
            tbar: ['->',{
                id: 'discardButton',
                text: 'Discard',
                handler: confirmDiscard
            }],
            sm: sm,
            enableHdMenu: false,
            autoExpandColumn: 'name',
            loadMask: true,
            stripeRows: true
        });
        grid.on('rowdblclick', function(g, rowIndex) {
            var record = store.getAt(rowIndex);
            displayBatchItemsWin(record);
        });
        
        var panel = new Ext.Panel({
            id: id,
            title: title,
            layout: 'fit',
            border: false,
            items: [ grid ]
        });  
        panel.on('activate', function() {
            currentWinView = type;
            setWinButtons();
            store.load();
        });
        
        return panel;
    };
    
    /**
     * Set publishing win buttons
     * 
     * @return  void
     */
    var setWinButtons = function() {
        var workflowId = currentServer.get('workflowId');
        var step = currentServer.get('step');
        var isFinal = currentServer.get('final');
        var allowIndividualUpdates = currentServer.get('allowIndividualUpdates');
        
        switch (currentWinView) {
            case WIN_VIEW_UNPUBLISHED:
                if (workflowId == 0 || isFinal == 1) {
                    Ext.fly('batchSelectorLabel').addClass('hidden');
                    Ext.fly('batchSelector').addClass('hidden');
                } else {
                    Ext.fly('batchSelectorLabel').removeClass('hidden');
                    Ext.fly('batchSelector').removeClass('hidden');
                }
                
                if (workflowId > 0 && step > 1) {
                    Ext.getCmp('addToBatchButton').show();
                } else {
                    Ext.getCmp('addToBatchButton').hide();
                }
                
                Ext.getCmp('publishButton').show();
                Ext.getCmp('closeBatchesButton').hide();
                Ext.getCmp('publishBatchesButton').hide();
                Ext.getCmp('reopenBatchesButton').hide();
                break;
                
            case WIN_VIEW_OPEN_BATCHES:
                Ext.fly('batchSelectorLabel').addClass('hidden');
                Ext.fly('batchSelector').addClass('hidden');
                Ext.getCmp('publishButton').hide();
                Ext.getCmp('addToBatchButton').hide();
                Ext.getCmp('closeBatchesButton').show();
                Ext.getCmp('publishBatchesButton').hide();
                Ext.getCmp('reopenBatchesButton').hide();
                break;
            
            case WIN_VIEW_PENDING_BATCHES:
                Ext.fly('batchSelectorLabel').addClass('hidden');
                Ext.fly('batchSelector').addClass('hidden');
                Ext.getCmp('publishButton').hide();
                Ext.getCmp('addToBatchButton').hide();
                Ext.getCmp('closeBatchesButton').hide();
                Ext.getCmp('publishBatchesButton').show();
                Ext.getCmp('reopenBatchesButton').show();
                break;
        }
    };
        
    /**
     * Get list of items to publish
     * 
     * @return  void
     */
    var getUnpublishedUpdates = function() {
        updatesStore.setBaseParam('serverId', currentServerId);
        updatesStore.setBaseParam('userId', usersCombo.getValue());
        updatesStore.setBaseParam('updateType', updateTypesCombo.getValue());

        updatesStore.load();
    };
    
    /**
     * Get publish progress
     * 
     * @return  void
     */
    var getPublishProgress = function() {
        Ext.Ajax.request({
            url: '/publishing/xhr_get_publish_progress',
            params: { serverId: currentServerId },
            success: function(r) {
                if (!publishingWin.isVisible()) {
                    return;
                }
                try {
                    var response = Ext.decode(r.responseText);
                    var elem = Ext.fly('publishStatus');
                    
                    if (response.error) {
                        doingPublish = false;
                        elem.removeClass('publishing');
                        elem.dom.innerHTML = response.error;
                    } else if (response.numItems) {
                        doingPublish = true;
                        elem.addClass('publishing');
                        elem.dom.innerHTML = 'Publishing ' 
                                           + response.published
                                           + ' / ' 
                                           + response.numItems;
                    
                        progressWatcher = setTimeout(
                            getPublishProgress, progressWatcherTimeout
                        );
                    } else {
                        elem.removeClass('publishing');
                        elem.dom.innerHTML = 'No items currently being published';
                        
                        // simply check that publishing has ended so that
                        // we can refresh the counter widget
                        if (doingPublish) {
                            doingPublish = false;
                            checkForUpdates();
                        }
                    }
                } catch (e) {
                    Pelorous.statusMessage(r.responseText);
                }
            }
        });
    };

    return {
        LOOKUP_MATCH_TYPE_ANY: 'any',
        LOOKUP_MATCH_TYPE_ALL: 'all',
        
        /**
         * Init publishing object
         * 
         * @return void
         */
        init: function() {
            var notificationDiv = Ext.fly('publishingNotification');
    
            // if notifcation div does not exist, user can't be logged in
            if (!notificationDiv) {
                // unset cookie and stored publishing plans
                cp.set(serversCookieId, null);
                servers = [];
                return;
            }

            // get publishing plans if not already fetched
            fetchServers(function() {
                if (serversStore.getCount() === 0) {
                    return;
                }
                
                if (updatesWatcher) {
                    return;
                }
                
                updatesWatcher = setTimeout(checkForUpdates, 0);
            });
            
            // get publishing plans if not already fetched
            fetchUserGeneratedServers(function() {
                if (userGenServersStore.getCount() === 0) {
                    return;
                }
            });
        },
        
        /**
         * Display publishing win
         * 
         * @param {Number} serverId
         * @return void
         */
        displayPublishingWin: function(serverId) {
            fetchServers(function() {
                displayPublishingWin(serverId);
            });
        },
        
        /**
         * Get toolbar for use in search panel
         * 
         * @param   {Function} selectHandler
         * @param   {String} label
         * @return  {Ext.form.ComboBox}
         */
        getPublishServersCombo: function(selectHandler, label) {
            var label = label || 'Server';
            var combo = new Ext.form.ComboBox({
                store: serversStore,
                valueField: 'serverId',
                width: 120,
                displayField: 'name',
                typeAhead: true,
                mode: 'local',
                triggerAction: 'all',
                forceSelection: true,
                selectOnFocus: true,
                hidden: true
            });
            
            var comboLabel = new Ext.Toolbar.TextItem({ 
                text: label + ':',
                hidden: true
            });

            // make sure servers have been fetched
            fetchServers(function() {
                if (serversStore.getCount() === 0) {
                    selectHandler();
                    return;
                }
                
                combo.show();
                comboLabel.show();
                
                var index = 0;
                if (currentServerId > 0) {
                    combo.setValue(currentServerId);
                    var stop = false;
                    serversStore.each(function(record) {
                        if (!stop) {
                            index++;
                        }
                        if (record.get('serverId') == currentServerId) {
                            stop = true;
                        }
                    });
                } else {
                    var record = serversStore.getAt(0);
                    combo.setValue(record.get('serverId'));
                    Pelorous.publishing.setCurrentServerId(record.get('serverId'));
                }
                
                selectHandler(
                    combo, 
                    serversStore.getAt(index), 
                    index
                );

                combo.on('select', function(cb, record, index) {
                    var serverId = record.get('serverId');
                    Pelorous.publishing.setCurrentServerId(serverId);
                    
                    selectHandler(cb, record, index);   
                });
            });

            return [ comboLabel, combo ];
        },
        
        /**
         * Get toolbar for use in search panel
         * 
         * @param   {Function} selectHandler
         * @param   {String} label
         * @return  {Ext.form.ComboBox}
         */
        getUserGenServersCombo: function(selectHandler, label) {
            var label = label || 'Server';
            var combo = new Ext.form.ComboBox({
                store: userGenServersStore,
                valueField: 'serverId',
                width: 120,
                displayField: 'name',
                typeAhead: true,
                mode: 'local',
                triggerAction: 'all',
                forceSelection: true,
                selectOnFocus: true,
                hidden: true
            });
            
            var comboLabel = new Ext.Toolbar.TextItem({ 
                text: label + ':',
                hidden: true
            });

            // make sure servers have been fetched
            fetchUserGeneratedServers(function() {
                if (userGenServersStore.getCount() === 0) {
                    selectHandler();
                    return;
                }
                
                combo.show();
                comboLabel.show();
                
                var index = 0;
                if (currentUserGenServerId > 0) {
                    combo.setValue(currentUserGenServerId);
                    var stop = false;
                    userGenServersStore.each(function(record) {
                        if (!stop) {
                            index++;
                        }
                        if (record.get('serverId') == currentUserGenServerId) {
                            stop = true;
                        }
                    });
                } else {
                    var record = userGenServersStore.getAt(0);
                    combo.setValue(record.get('serverId'));
                    Pelorous.publishing.setCurrentUserGenServerId(record.get('serverId'));
                }
                
                selectHandler(
                    combo, 
                    userGenServersStore.getAt(index), 
                    index
                );

                combo.on('select', function(cb, record, index) {
                    var serverId = record.get('serverId');
                    Pelorous.publishing.setCurrentUserGenServerId(serverId);
                    
                    selectHandler(cb, record, index);   
                });
            });

            return [ comboLabel, combo ];
        },
        
        /**
         * Set the currently selected server id
         * 
         * @param   {Number} serverId
         * @return  void
         */
        setCurrentServerId: function(serverId) {
            currentServerId = serverId;
            cp.set('serverId', serverId);
        },
        
        /**
         * Get the currently selected server id
         * 
         * @return  {Number}
         */
        getCurrentServerId: function() {
            return currentServerId;
        },
        
        
        /**
         * Set the currently selected user generated server id
         * 
         * @param   {Number} serverId
         * @return  void
         */
        setCurrentUserGenServerId: function(serverId) {
            currentUserGenServerId = serverId;
            cp.set('userGenServerId', serverId);
        },
        
        /**
         * Get the currently selected user generated server id
         * 
         * @return  {Number}
         */
        getCurrentUserGenServerId: function() {
            return currentUserGenServerId;
        },
        
        /**
         * Get servers
         * 
         * @return  {Ext.data.store}
         */
        getServers: function() {
            return servers;
        },
        
        /**
         * Get userGenServers
         * 
         * @return  {Ext.data.store}
         */
        getUserGenServers: function() {
            return userGenServers;
        },
        
        /**
         * Lock/unlock an item
         */
        toggleItemLock: function(itemId, locked, callback) {
            var title = 'Lock',
                messageText = 'lock';
            
            if (locked) {
                title = 'Unlock';
                messageText = 'unlock';
            }
                
            Ext.MessageBox.confirm(title + ' Item', 'Are you sure you want to ' + messageText + ' this item?', function(btn){
                if (btn == 'yes') {
                    Ext.Ajax.request({
                        url: '/publishing/xhr_toggle_lock/',
                        params: { itemId: itemId },
                        success: callback,
                        failure: callback
                    });
                }
            });
        },
        
        /**
         * Hide/unhide an item
         */
        toggleItemVisibility: function(itemId, hidden, callback) {
            var title = 'Hide',
            messageText = 'hide';
        
            if (hidden) {
                title = 'Unhide';
                messageText = 'unhide';
            }
                
            Ext.MessageBox.confirm(title + ' Item', 'Are you sure you want to ' + messageText + ' this item?', function(btn){
                if (btn == 'yes') {
                    Ext.Ajax.request({
                        url: '/publishing/xhr_toggle_visibility/',
                        params: { itemId: itemId },
                        success: callback,
                        failure: callback
                    });
                }
            });
        },
        
        /**
         * Clear state cookies
         * 
         * @return  void
         */
        clearCookies: function() {
            cp.set('serverId', 0);
            cp.set('userGenServerId', 0);
            cp.set(serversCookieId, null);
            cp.set(userGenServersCookieId, null);
        }
    };
}();
