﻿(function(){
    var oldRender = Ext.form.Field.prototype.onRender;
    
    Ext.override(Ext.form.Field, {
        onRender: function(){
            oldRender.apply(this, arguments);
            
            if(this.typingIndicator){
                var id = Ext.id();
                
                Ext.DomHelper.append(this.el.parent(), {
                    tag: 'div',
                    id: id,
                    cls: 'x-text apexchat-typing-indicator'
                });
                /*
                Ext.QuickTips.register({
                    target: id,
                    title: this.help.title || '',
                    text: this.help.text || '',
                    dismissDelay: 0
                });
                
                this.on('destroy', function(){
                    Ext.QuickTips.unregister(id);
                });*/
            }
        }
    });
})();

Ext.onReady(function(){
    window.ApexChat = window.ApexChat || {};
    
    var dateRegex = /\/Date\(-?(\d+[\-|\+]?\d{0,4})\)\//;
    var quoteRegex = /\"/g;
    var client = fm.websync.client;
    var serverOffset = 0;
    
    var HIGHLIGHT_DURATION = 1; // # seconds to take for the highlight fade operation
    var HIGHLIGHT_INTERVAL = 2; // # seconds after which to re-highlight
    var NEW_MESSAGE_THRESHOLD = 29; // # seconds to blink red background color
    var IDLE_WARN_THRESHOLD = 30; // # seconds after which the idle time goes orange
    var IDLE_WAIT_THRESHOLD = 60; // # seconds after which the idle time goes red
    var IDLE_TYPING_THRESHOLD = 3; // # seconds after which a user is considered "no longer typing"
    
    
    ApexChat.Definitions = {
        ChatRoom: [
            { name: 'chatId' },
            { name: 'status' },
            { name: 'companyId' },
            { name: 'companyName' },
            { name: 'createdOn' },
            { name: 'pickedUpOn' },
            { name: 'lastActivityDate' },
            { name: 'lastMessage' },
            { name: 'lastUser' },
            { name: 'lastUserId' },
            { name: 'generallyAvailable' },
            { name: 'participants' },
            { name: 'typingIndicator' },
            { name: 'location' },
            { name: 'referrer' },
            { name: 'pickedUpBy' },
            { name: 'pickedUpByUsername' }
        ],
        User: [
            { name: 'id' },
            { name: 'username' },
            { name: 'displayName' },
            { name: 'online' },
            { name: 'active' },
            { name: 'sessionCount' },
            { name: 'modifiedOn' },
            { name: 'skillLevel' },
            { name: 'activeChats' },
            { name: 'firstActiveToday' },
            { name: 'lastLogoutToday' },
            { name: 'inactiveSince' },
            { name: 'activeMinutesToday' },
            { name: 'activeMinutesTimestamp' },
            { name: 'totalLeadsCreatedToday' },
            { name: 'totalChatsAssignedToday' },
            { name: 'totalChatsMissedToday' },
            { name: 'blankChatsToday' },
            { name: 'averageResponseTimeToday' },
            { name: 'averagePickupTimeToday' },
            { name: 'liveStats' }
        ]
    };
    
    ApexChat.ChatStatus = {
        AwaitingPickup: 1,
        Active: 2,
        Inactive: 3
    };
    
    ApexChat.LeadType = {
        Sales: 1,
        Service: 2,
        Other: 3
    };
    ApexChat.LeadNotificationState = {
        NotSent: 1,
        Sending: 2,
        Sent: 3,
        SendingFailed: 4
    };
    ApexChat.CompanyType = {
        Default: 1,
        Dealership: 2
    };
    ApexChat.UserType = {
        Default: 1,
        Agent: 2,
        Operator: 3
    };
    
    ApexChat.ParseDate = function(value){
        if(!value){ return value; }
        if(typeof value.getDate !== "undefined"){ return value; }
        var ticksAndZone = value.match(dateRegex)[1];
        var delim = ticksAndZone.indexOf('-') > -1 ? '-' : '+';
        var parts = ticksAndZone.split(delim);
        var ticks = parseInt(parts[0], 10);
        return new Date(ticks);
    };
    
    ApexChat.LiveStatsRenderer = function(value, meta, record){
        if(!record.get('liveStats')){
            return '-';
        }
        return ApexChat.ValueRenderer(value, meta, record);
    }
    
    ApexChat.DateRenderer = function(value){
        if(value && (typeof value.getDate === "undefined")){
            value = ApexChat.ParseDate(value);
        }        
        if(value){
            return value.format('m/d/y h:i:s a');
        }
    };
    
    ApexChat.TimeRenderer = function(value, meta, record){
        if(value && (typeof value.getDate === "undefined")){
            value = ApexChat.ParseDate(value);
        }        
        if(value){
            return value.format('h:i:s a');
        }
    };
    
    String.prototype.format = function(){
        var string = this;
        for(var i = 0; i < arguments.length; i++){
            string = string.replace("{" + i + "}", arguments[i]);
        }
        return string;
    };
    
    var padLeft = function(s){
        while((s + '').length < 2){
            s = '0' + s;
        }
        return s;
    };
    
    var formatPercent = function(value1, value2){
        if(value2 === 0){
            return 0;
        }
        return ((value1 / value2 * 1.0) * 100).toFixed(1);
    };
    
    ApexChat.SecondsRenderer = function(value){
        return value + 's';
    };
    
    ApexChat.MinutesRenderer = function(value, meta, record){
        var hours = parseInt(Math.floor(value / 60));
        var mins = parseInt(Math.floor(value - (hours * 60)));
        var totalSecs = value * 60;
        var secs = parseInt(Math.floor(totalSecs - (hours * 60 * 60) - (mins * 60)));
        return padLeft(hours) + ':' + padLeft(mins) + ':' + padLeft(secs);
    };
    
    ApexChat.ActiveRenderer = function(value, meta, record){
        var minutes = record.get('activeMinutesToday');
        var currentlyActive = record.get('active') && record.get('online') && record.get('liveStats');
        if(currentlyActive){
            var seconds = ApexChat.timeSince(ApexChat.ParseDate(record.get('activeMinutesTimestamp')));
            return ApexChat.MinutesRenderer((minutes * 60 + seconds) / 60);
        }else{
            return ApexChat.MinutesRenderer(minutes);
        }
    };
    
    ApexChat.IdleRenderer = function(value, meta, record){
        if(!record.get('online')){
            return '<em style="font-style:italic">Offline</em>';
        }else if(record.get('active')){
            return '<em style="font-style:italic">Active</em>';
        }else if(!record.get('inactiveSince')){
            return '<em style="font-style:italic">Inactive</em>';
        }
        var s = ApexChat.timeSince(ApexChat.ParseDate(record.get('inactiveSince')));
        return ApexChat.MinutesRenderer(s / 60);
    };
    
    ApexChat.ValueRenderer = function(value, meta, record){
        return ApexChat.TipRenderer(value);
    };
    
    ApexChat.CheckRenderer = function(value, meta, record){
        if(value){
            return '<img src="/images/tick.png" />';
        }else{
            return '<img src="/images/cross.png" />';
        }
    };
    
    ApexChat.TipRenderer = function(title, qtip){
        title = (title === null ? '' : title);
        if(arguments.length == 1){
            qtip = title;
        }
        if(qtip && qtip.replace){
            // escape quotes
            qtip = qtip.replace(quoteRegex, '\\"');
        }
        return '<span ext:qtip="' + qtip + '">' + title + '</span>';
    };
    
    var streamMask = null;
    ApexChat.StreamFailure = function(args){
        if(!streamMask){
            streamMask = new Ext.LoadMask(Ext.getBody(), { msg: 'Connection lost, attempting to reconnect...' });
        }
        streamMask.show();
    };
    
    ApexChat.StreamReconnect = function(args){
        streamMask.hide();
    };
    
    ApexChat.SearchableTree = Ext.extend(Ext.tree.TreePanel, {
        autoScroll: true,
        containerScroll: true,
        animate: true,
        initComponent: function(){                        
            var currentItems = this.tbar ? this.tbar.items || [] : [];
            var tree = this;

            Ext.apply(this, {
                hiddenNodes: [],
                tbar: {
                    hideBorders: true,
                    items: currentItems.concat([
                        '->',
                        {
                            xtype: 'textfield',
                            name: 'searchText',
                            emptyText: 'Enter search text',
                            listeners: {
                                specialkey: function(txt, e){
                                    if(e.getKey() == e.ENTER){
                                        tree.performSearch(txt.getValue());
                                    }
                                }
                            }
                        },
                        { 
                            xtype: 'button',
                            iconCls: 'x-button-search',
                            handler: function(b, e){
                                var txt = b.findParentByType('toolbar').find('name','searchText')[0];
                                tree.performSearch(txt.getValue());
                            }
                        }
                    ])
                }
            });
            ApexChat.SearchableTree.superclass.initComponent.apply(this, arguments);
        },
        performSearch: function(text){
            Ext.each(this.hiddenNodes, function(n){
			    n.ui.show();
		    });
		    if(!text){
			    this.filter.clear();
			    this.root.collapse(true, false);
			    return;
		    }
		    this.expandAll();
		    
            var re = new RegExp(Ext.escapeRe(text), 'i');
            this.filter.filterBy(function(n){
                return re.test(n.text) || n.hasChildNodes();
            });
            
            // hide empty nodes that weren't filtered
		    this.hiddenNodes = [];
		    this.root.cascade(function(n){
			    if(n.ui.ctNode.offsetHeight < 3 && n.hasChildNodes() && !re.test(n.text)){
				    n.ui.hide();
				    this.hiddenNodes.push(n);
			    }
		    }, this);
        },
        onRender: function(){
            this.filter = new Ext.tree.TreeFilter(this, {
                clearBlank: true,
                autoClear: true
            });
            
            ApexChat.SearchableTree.superclass.onRender.apply(this, arguments);
        }
    });
    
    Ext.reg('apexchat.searchabletree', ApexChat.SearchableTree);
    
    ApexChat.ActivateChatRoom = function(roomData, find){
        var container = Ext.getCmp('chatrooms');
        var room = Ext.getCmp('chat_' + roomData.chatId);
        if(!room){
            room = new ApexChat.ExtChatRoom(ApexChat.extend({
                id: 'chat_' + roomData.chatId, 
                chatId: roomData.chatId
            }, roomData));
            
            container.add(room);
            container.doLayout();
        }
        
        if(find){
            var t = (room.el.getOffsetsTo(container.body)[1]) + container.body.dom.scrollTop;
            container.body.scrollTo('top', t, true);
            
            // highlight the log so it's easier to see;
            room.expand();
            window.setTimeout(room.focus.bind(room), 50);
        }
        
        return room;
    };
    
    ApexChat.ChatStatusRenderer = function(value, meta, record){
        if(value === ApexChat.ChatStatus.AwaitingPickup){
            return '<span class="waiting">Waiting</span>';
        }else if(value === ApexChat.ChatStatus.Active){
            return '<span class="active">Active</span>';
        }
    };
    
    ApexChat.TimeSinceLastMessageRenderer = function(value, meta, record){
        // the value is already in local time; the server saves times in UTC, but JS doesn't know anything 
        // about UTC for the most part, so it's automatically converted to local time
        if(record.get('lastUserId')){
            return '-';
        }
        
        if(!value.getTime){
            return '-';
        }
        var s =  ApexChat.timeSince(value); //parseInt(((new Date().getTime()) - value.getTime()) / 1000);
        return ApexChat.getTimerElement('div',s);
    };
    
    ApexChat.getTimerElement = function(elementType, s){
        var cls = (s <= NEW_MESSAGE_THRESHOLD ? ' newmessage' : '');
        if(s % 2 == 0){
            cls += ' alt1';
        }else{
            cls += ' alt2';
        }
        
        if(s >= IDLE_WARN_THRESHOLD && s < IDLE_WAIT_THRESHOLD){
            return '<' + elementType + ' class="warning' + cls + '">' + s + 's</' + elementType + '>';
        }else if(s >= IDLE_WAIT_THRESHOLD){
            return '<' + elementType + ' class="waiting' + cls + '">' + s + 's</' + elementType + '>';
        }else{
            return '<' + elementType + ' class="' + cls + '">' + s + 's</div>';
        }
    };
    
    ApexChat.LastMessageRenderer = function(value, meta, record){
        if(!value){
            return '-';
        }
        return ApexChat.TipRenderer(record.get('lastUser') + ': ' + value);
    };
    
    ApexChat.TypingArrived = function(chatId, indicator){
        var grids = [/*'generalQueue',*/'incomingChats','allActiveChats','myActiveChats'];
        for(var i = 0; i < grids.length; i++){
            var grid = Ext.getCmp(grids[i]);
            var record = grid.findChatRecord(chatId);
            if (record) {
                // update the indicator
                record.set('typingIndicator', indicator);
                record.commit();
            }
        }
    };
    
    ApexChat.MessageArrived = function(chatId, message){
        var grids = [/*'generalQueue',*/'incomingChats','allActiveChats','myActiveChats'];
        for(var i = 0; i < grids.length; i++){
            var grid = Ext.getCmp(grids[i]);
            var record = grid.findChatRecord(chatId);
            if (record) {
                // update the last activity date if the message came from a visitor
                if(message.userId === 0){
                    record.set('lastActivityDate', message.createdOn); //new Date());
                }
                record.set('lastUser', message.participantDisplayName);
                record.set('lastUserId', message.userId);
                record.set('lastMessage', message.text);
                record.commit();
            }
        }
        
        // play a sound and highlight the chat if it's available and the message is from a visitor
        // then focus the window
        var room = Ext.getCmp('chat_' + chatId);
        if(room && message.userId === 0){
            // visitor sent a message
            ApexChat.Sound.notifyNewMessage();
            ApexChat.Title.flash();
            
            room.expand();
            window.focus();
        }
    };
    
    
    Ext.QuickTips.init();
    
    ApexChat.LocalGrid = Ext.extend(Ext.grid.GridPanel, {
        initComponent: function(){  
            if(!this.recordDef){
                var names = [];
                for(var i = 0; i < this.colModel.config.length; i++){
                    var dataIndex = this.colModel.config[i].dataIndex;
                    if(dataIndex){
                        names.push({ name: dataIndex });
                    }
                }
                this.recordDef = Ext.data.Record.create(names);
            }
            var store = new Ext.data.Store({
                remoteSort: false,
                reader: new Ext.data.JsonReader({
                    idProperty: this.idProperty || null
                }, this.recordDef),
                sortInfo: this.sortInfo
            });
            
            Ext.apply(this, {
                loadMask: true,
                store: store,
                colModel: this.colModel || []
            });
            ApexChat.LocalGrid.superclass.initComponent.apply(this, arguments);
        },
        refresh: function(){
            this.store.loadData(this.data || []);
        },
        onRender: function() {
            this.setData(this.data);
            ApexChat.LocalGrid.superclass.onRender.apply(this, arguments);
        },
        setData: function(data){
            this.data = data;
            this.store.loadData(data || []);
        },
        remove: function(name, value){
            // if only one value is specified, use the 'id' field
            if(arguments.length == 1){
                value = name;
                name = 'id';
            }
            var idx = this.store.find(name, value);
            this.store.removeAt(idx);
        }
    });
    Ext.reg('apexchat.localgrid', ApexChat.LocalGrid);
    
    ApexChat.SimpleGrid = Ext.extend(Ext.grid.GridPanel, {
        initComponent: function(){
            var proxy = this.proxy || new Ext.data.HttpProxy({
                method: 'GET',
                url: this.url
            });
            this.pageSize = this.pageSize || 10;
            
            if(!this.recordDef){
                var names = [];
                for(var i = 0; i < this.colModel.config.length; i++){
                    var dataIndex = this.colModel.config[i].dataIndex;
                    if(dataIndex){
                        names.push({ name: dataIndex });
                    }
                }
                this.recordDef = Ext.data.Record.create(names);
            }
            
            var store = this.store || new Ext.data.Store({
                remoteSort: true,
                proxy: proxy,
                baseParams: { limit: this.pageSize },
                reader: new Ext.data.JsonReader({
                    idProperty: this.idProperty || null,
                    totalProperty: 'totalSize',
                    root: 'data'
                }, this.recordDef)
            });

            
            var pager = this.pager || new Ext.PagingToolbar({
                pageSize: this.pageSize,
                displayInfo: (this.displayPagingInfo === false ? false : true),
                displayMsg: '{0} - {1} of {2}',
                emptyMsg: 'No results',
                store: store
            });
            
            Ext.apply(this, {
                loadMask: true,
                store: store,
                bbar: pager,
                colModel: this.colModel
            });
            
            ApexChat.SimpleGrid.superclass.initComponent.apply(this, arguments);
            
            var me = this;
            this.getSelectionModel().on('rowselect', function(sm, rowIndex, record){
                me.fireEvent('rowselect', me, rowIndex, record);
            });
        },
        refresh: function(){
            this.store.reload();
        },
        onRender: function() {
            ApexChat.SimpleGrid.superclass.onRender.apply(this, arguments);
            window.setTimeout(function(){
                if(!this.deferLoad){
                    this.store.load({ params: { start: 0 } });
                }
                this.store.on('exception', function(proxy, type, action, options, response, arg){
                    if(response.status == 200){
                        var result = Ext.decode(response.responseText);
                        Ext.Msg.alert('Error', result.error);
                    }else{
                        //TODO: add option to report/add details/etc
                        Ext.Msg.alert('Error', 'The store failed to load properly; the server responded with error code ' + response.status);
                    }
                    
                });
            }.createDelegate(this), 1);
        }
    });

    ApexChat.SearchableGrid = Ext.extend(ApexChat.SimpleGrid, {
        performSearch: function(searchText){
            // set it as a "base" parameter so it applies to pagination and refreshes too
            this.store.setBaseParam('searchText',searchText);
            this.store.load();
        },
        initComponent: function(){
            var grid = this;
            var currentItems = this.tbar ? this.tbar.items || [] : [];
            Ext.apply(this, {
                tbar: {
                    hideBorders: true,
                    items: currentItems.concat([
                        '->',
                        {
                            xtype: 'textfield',
                            name: 'searchText',
                            emptyText: 'Enter search text',
                            listeners: {
                                specialkey: function(txt, e){
                                    if(e.getKey() == e.ENTER){
                                        grid.performSearch(txt.getValue());
                                    }
                                }
                            }
                        },
                        { 
                            xtype: 'button',
                            iconCls: 'x-button-search',
                            handler: function(b, e){
                                var txt = b.findParentByType('toolbar').find('name','searchText')[0];
                                grid.performSearch(txt.getValue());
                            }
                        }
                    ])
                }
            });
            ApexChat.SearchableGrid.superclass.initComponent.apply(this, arguments);
        }
    });
    Ext.reg('apexchat.searchablegrid', ApexChat.SearchableGrid);
    
    ApexChat.SendLeadForm = function(config){
        ApexChat.SendLeadForm.superclass.constructor.call(this, config);
    };
    ApexChat.SendLeadForm = Ext.extend(ApexChat.SendLeadForm, Ext.Panel, {
        chatId: 0,
        successText: 'Chat lead sent successfully!',
        sendingText: 'Lead is being sent, please wait...',
        labelWidth: 65,
        initComponent: function(config){
            var me = this;
            Ext.apply(this, {
                border: false,
                items: [{
                    xtype: 'toolbar',
                    items: [{
                        xtype: 'button',
                        iconCls: 'x-icon-refresh',
                        text: 'Refresh',
                        handler: function(){
                            Ext.Msg.confirm('Are you sure?','Any changes you\'ve made to this lead will be replaced. Continue?', function(btn){
                                me.refresh(true);
                            });
                        }
                    },{
                        xtype: 'button',
                        iconCls: 'x-icon-send-email',
                        name: 'send',
                        text: 'Send',
                        handler: function(btn){
                            // validate the lead
                            var forms = me.find('xtype','form');
                            var valid = true;
                            for(var i = 0; i<forms.length;i++){
                                valid = valid && forms[i].getForm().isValid();
                            }
                            if(!valid){
                                return;
                            }
                            
                            // build the lead data
                            var data = {
                                companyId: me.companyId,
                                chatId: me.chatId,
                                name: me.find('name','name')[0].getValue(),
                                phone: me.find('name','phone')[0].getValue(),
                                email: me.find('name','email')[0].getValue(),
                                leadType: me.find('name','leadType')[0].getValue(),
                                notes: me.find('name','notes')[0].getValue(),
                                year: me.find('name','year')[0].getValue() || 1900,
                                make: me.find('name','make')[0].getValue(),
                                model: me.find('name','model')[0].getValue(),
                                used: me.find('name','used')[0].getValue()
                            };
                            
                            if(!data.email && !data.phone){
                                Ext.Msg.alert('Error','You must specify either a phone number or an email address.');
                                return;
                            }
                            
                            btn.disable();
                            btn.setText('Sending...');
                            
                            var channel = '/chatroom/' + me.chatId + '/lead';
                            var status = me.find('name','status')[0];
                            
                            // first, subscribe to a channel about notifications so when the lead email status changes, we get notified 
                            fm.websync.client.subscribe({
                                channel: channel,
                                onReceive: function(args){
                                    if(args.data.success){
                                        status.update(me.successText);
                                    }else{
                                        status.update(args.error);
                                    }
                                    
                                    me.find('name','send')[0].setText('Re-send');
                                    
                                    // whatever happened, unsubscribe from this channel
                                    fm.websync.client.unsubscribe({
                                        channel: channel
                                    });
                                    
                                    btn.setText('Re-send');
                                    btn.enable();
                                }
                            });
                            
                            // set the status to "sending"
                            status.update(me.sendingText);
                            
                            // attempt to send out the email
                            fm.network.xhr({
                                retries: 0,
                                url: '/Services/ApexChatService.svc/leads',
                                data: data,
                                onSuccess: function(args){
                                    // error => update the status field
                                    if(!args.success){
                                        Ext.Msg.alert('Error',args.error);
                                        status.update(args.error);
                                        btn.setText('Send');
                                        btn.enable();
                                        return;
                                    }
                                    
                                    // otherwise, just keep going; we've already set the status to "sending", so
                                    // now we're just waiting for the notification that the email has gone out
                                },
                                onFailure: function(args){
                                    Ext.Msg.alert('Error',args.error);
                                    btn.enable();
                                }
                            });
                        }
                    },'-',{
                        xtype: 'tbtext',
                        name: 'status'
                    }]
                },{
                    height: 280,
                    padding: 10,
                    border: false,
                    defaults: {
                        border: false
                    },
                    items: [{
                        // top item is a single text field
                        layout: 'column',
                        defaults: { border: false },
                        items: [{
                            columnWidth: 0.664,
                            xtype: 'form',
                            labelWidth: this.labelWidth,
                            defaults: { anchor: '97.5%' },
                            items: [{
                                xtype: 'textfield',
                                readOnly: true,
                                name: 'companyName',
                                fieldLabel: 'Send To'
                            }]
                        },{
                            columnWidth: 0.332,
                            xtype: 'form',
                            labelWidth: this.labelWidth,
                            defaults: { anchor: '95%' },
                            items: [{
                                xtype: 'combo',
                                name: 'leadType',
                                allowBlank: false,
                                fieldLabel: 'Lead Type',
                                triggerAction: 'all',
                                mode: 'local',
                                forceSelection: true,
                                editable: false,
                                store: new Ext.data.ArrayStore({
                                    id: 0,
                                    fields: ['id','text'],
                                    data: [
                                        [ApexChat.LeadType.Sales, 'Sales'],
                                        [ApexChat.LeadType.Service, 'Service'],
                                        [ApexChat.LeadType.Other, 'Other']
                                    ]
                                }),
                                valueField: 'id',
                                displayField: 'text'
                            }]
                        }]
                    },{
                        // next is 3 items, in a form
                        layout: 'column',
                        defaults: { border: false },
                        items: [{
                            xtype: 'form',
                            labelWidth: this.labelWidth,
                            columnWidth: 0.332,
                            defaults: { anchor: '95%' },
                            items: [{
                                xtype: 'textfield',
                                allowBlank: false,
                                name: 'name',
                                fieldLabel: 'Name'
                            },{
                                xtype: 'numberfield',
                                allowBlank: true,
                                name: 'year',
                                fieldLabel: 'Year'
                            },{
                                xtype: 'checkbox',
                                name: 'used',
                                fieldLabel: 'Used'
                            }]
                        },{
                            xtype: 'form',
                            labelWidth: this.labelWidth,
                            columnWidth: 0.332,
                            defaults: { anchor: '95%' },
                            items: [{
                                xtype: 'textfield',
                                allowBlank: true,
                                name: 'phone',
                                fieldLabel: 'Phone'
                            },{
                                xtype: 'textfield',
                                allowBlank: true,
                                name: 'make',
                                fieldLabel: 'Make'
                            }]
                        },{
                            xtype: 'form',
                            labelWidth: this.labelWidth,
                            columnWidth: 0.332,
                            defaults: { anchor: '95%' },
                            items: [{
                                xtype: 'textfield',
                                allowBlank: true,
                                name: 'email',
                                fieldLabel: 'Email'
                            },{
                                xtype: 'textfield',
                                allowBlank: true,
                                name: 'model',
                                fieldLabel: 'Model'
                            }]
                        }]
                    },{
                        layout: 'form',
                        xtype: 'form',
                        name: 'editorcontainer',
                        labelWidth: this.labelWidth,
                        items: [{
                            xtype: 'textarea',
                            allowBlank: false,
                            name: 'notes',
                            fieldLabel: 'Comments',
                            anchor: '97.5%',
                            height: 150
                        }]
                    }]
                }],
                listeners: {
                    activate: function(){
                        me.refresh(false);
                    }
                }
            });
            ApexChat.SendLeadForm.superclass.initComponent.apply(this, arguments);
        },
        refresh: function(overwrite){
            if(this.loadedOnce && !overwrite){
                return;
            }
            this.loadedOnce = true;
            // TODO: don't overwrite changes made by the agent unless the overwrite flag is set
            var me = this;
            var myMask = new Ext.LoadMask(this.body, {msg:"Please wait..."});
            myMask.show();
            
            // load up the data
            fm.network.xhr({
                retries: 0,
                url: '/Services/ApexChatService.svc/chatrooms/convert/' + this.chatId,
                onSuccess: function(args){
                    try{
                        if(!args.success){
                            myMask.hide();
                            Ext.Msg.alert('Error',args.error);
                            return;
                        }
                        
                        // add the company name
                        me.find('name','companyName')[0].setValue(args.data.companyName);
                        
                        // set up the transcript
                        var text = [];
                        for(var i = 0; i < args.data.transcript.length;i++){
                            var log = args.data.transcript[i];
                            // only include visitor messages
                            if(log.userId === 0){
                                // remove links from the content
                                var txt = log.text.replace(/<.+?>/g,'');
                                
                                // convert html entities; placing the values
                                // into a text area, then getting the value, will ensure they're valid
                                var ta = document.createElement('textarea');
                                ta.innerHTML = txt.replace(/</g,'&lt;').replace(/>/g,'&gt;');
                                
                                // store it and move on
                                text.push(ta.value);
                            }
                        }
                        me.find('name','notes')[0].setValue(text.join('\n'));
                        
                        // for future reference when saving the lead
                        me.companyId = args.data.companyId;
                        
                        // hide the non-dealership fields
                        if(args.data.companyType !== ApexChat.CompanyType.Dealership){
                            Ext.each(['year','make','model','used'], function(f){
                                var f = me.find('name',f)[0]
                                f.hide();
                                f.allowBlank = true;
                            });
                            
                            // resize the height to account for 2 missing rows
                            if(!me._resizedToFit){
                                me._resizedToFit = true;
                                me.setHeight(me.getHeight() - 60);
                            }
                        }
                        
                        // if the lead's already in place...
                        if(args.data.leadNotificationState){
                            var status = me.find('name','status')[0];
                            if(args.data.leadNotificationState == ApexChat.LeadNotificationState.Sent){
                                status.update('This lead has already been sent.');
                            }else if(args.data.leadNotificationState == ApexChat.LeadNotificationState.Sending){
                                status.update('Lead sending in progress...');
                            }else if(args.data.leadNotificationState == ApexChat.LeadNotificationState.SendingFailed){
                                status.update('Lead sending failed. ' + args.data.lead.notificationError || '');
                            }
                            
                            // load up the data
                            for(var prop in args.data.lead){
                                if(args.data.lead.hasOwnProperty(prop) && args.data.lead[prop]){
                                    var fld = me.find('name',prop)[0];
                                    if(fld){
                                        fld.setValue(args.data.lead[prop]);
                                    }
                                }
                            }
                            
                            if(args.data.lead.year == 1900){
                                me.find('name','year')[0].setValue('');
                            }
                            
                            me.find('name','send')[0].setText('Re-send');
                        }
                    }catch(e){
                        alert(e.message);
                    }
                    myMask.hide();
                },
                onFailure: function(args){
                    myMask.hide();
                    Ext.Msg.alert('Error',args.error);
                }
            });
        }
    });
    Ext.reg('apexchat.sendleadform', ApexChat.SendLeadForm);
    
    
    ApexChat.ExtChatRoom = function(config){
        ApexChat.ExtChatRoom.superclass.constructor.call(this, config);
    };
    
    Ext.extend(ApexChat.ExtChatRoom, Ext.Panel, {
        locationPlaceholder: '[Location Pending...]',
        flagAsLeadText: 'Flag as Lead',
        unflagAsLeadText: 'Un-flag as Lead',
        companyName: '',
        companyUrl: '',
        chatId: 0,
        initComponent: function(config){
            var logHeight = 160, 
                statsHeight = 80, 
                textHeight = 75,
                scriptHeight = logHeight + statsHeight + textHeight + 2, // +2 to account for borders
                me = this;
            
            this.history = [];
            
            Ext.apply(this, {      
                
                items: [{
                    title: 'Chat',
                    iconCls: 'x-icon-chat',
                    collapsible: true,
                    tools: [{
                        id: 'close',
                        handler: me.close.bind(me)
                    }],  
                    name: 'wrap',
                    items: [{
                        xtype: 'tabpanel',
                        border: false,
                        activeItem: 0,
                        height: 380,
                        items: [{
                            title: 'Chat',
                            items: [{
                                xtype: 'toolbar',
                                items: [{
                                    xtype: 'button',
                                    text: 'End Chat Session',
                                    iconCls: 'x-icon-end-chat',
                                    handler: function(){
                                        Ext.Msg.confirm('Are you sure?', 'This will end the chat for you and the visitor. Continue?', function(btn){
                                            if(btn == 'yes'){
                                                // we can't just call "endchat", because we want to stay subscribed to the channel until this window
                                                // is officially closed.
                                                fm.network.xhr({
                                                    retries: 0,
                                                    url: '/Services/ApexChatService.svc/chatrooms/' + me.chatId + '/close',
                                                    data: {},
                                                    onSuccess: function(){
                                                    },
                                                    onFailure: function(args){
                                                        Ext.Msg.alert('Error',args.error);
                                                    }
                                                });
                                            }
                                        });
                                    }
                                },{
                                    xtype: 'button',
                                    // put a reference in the root chat class
                                    ref: '../../../../flagLead',
                                    text: me.flagAsLeadText,
                                    iconCls: 'x-icon-lead-good',
                                    handler: function(btn){
                                        fm.network.xhr({
                                            retries: 0,
                                            url: '/Services/ApexChatService.svc/chatrooms/' + me.chatId + '/flaglead?isLead=' + (me.isLead ? false : true),
                                            data: {},
                                            onSuccess: function(args){
                                                me.isLead = args.data;
                                                if(me.isLead){
                                                    btn.setText(me.unflagAsLeadText);
                                                    btn.setIconClass('x-icon-lead-unflag');
                                                }else{
                                                    btn.setText(me.flagAsLeadText);
                                                    btn.setIconClass('x-icon-lead-flag');
                                                }
                                            },
                                            onFailure: function(args){
                                                Ext.Msg.alert('Error', args.error);
                                            }
                                        });
                                    }
                                }]
                            },{
                                padding: 5,
                                layout: 'hbox',
                                height: 325,
                                border: false,
                                defaults: { border: false },
                                layoutConfig: {
                                    defaultMargins: {
                                        top: 0, right: 2, left: 2, bottom: 0
                                    }
                                },
                                xtype: 'panel',
                                items: [{
                                    flex: 1,
                                    items: [{
                                        xtype: 'panel',
                                        name: 'log',
                                        cls: 'x-text',
                                        width: '100%',
                                        autoScroll: true,
                                        padding: 5,
                                        height: logHeight
                                    },{
                                        xtype: 'panel',
                                        items: [{
                                            xtype: 'textarea',
                                            typingIndicator: true,
                                            enableKeyEvents: true,
                                            height: textHeight,
                                            emptyText: 'Type a message and hit enter to send',
                                            style: 'border:0;',
                                            width: '100%',
                                            listeners: {
                                                focus: function(){
                                                    //ApexChat.Title.stopFlashing();
                                                    //ApexChat.Sound.stopNotifyIncomingChat();
                                                    me.highlight();
                                                },
                                                blur: function(){
                                                    me.unhighlight();
                                                },
                                                keyup: function(field, e){
                                                    //ApexChat.Title.stopFlashing();
                                                    //ApexChat.Sound.stopNotifyIncomingChat();
                                                    
                                                    // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN,
                                                    // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN
                                                    try{
                                                        if (e.getKey() == e.ENTER) {
                                                            me.sendMessage(field.getValue());
                                                            field.setValue('');
                                                            
                                                            window.clearTimeout(me.timeout);
                                                            me.typing = false;
                                                            me.sendTyping(false);
                                                            
                                                        }else{
                                                            if(!me.typing){
                                                                me.typing = true;
                                                                me.sendTyping(true);
                                                            }
                                                            window.clearTimeout(me.timeout);
                                                            me.timeout = window.setTimeout(function(){
                                                                me.typing = false;
                                                                me.sendTyping(false);
                                                            }, IDLE_TYPING_THRESHOLD * 1000);
                                                        }
                                                    }catch(ex){
                                                        alert(ex.message);
                                                    }
                                                }
                                            }
                                        }]
                                    },{
                                        html: 'Call stats go here',
                                        tpl:  new Ext.XTemplate(
                                            '<table class="stats x-text">',
                                                '<tr>',
                                                '  <td class="title">Company:</td>',
                                                '  <td class="value"><a href="{companyUrl}" target="_blank">{companyName}</a></td> ',
                                                '  <td class="title">Chat:</td>',
                                                '  <td class="value">#{chatId}</td> ',
                                                '  <td class="title">Time:</td> ',
                                                '  <td class="value">{startTime:date("g:i:s a")}</td> ',
                                                '</tr>',
                                                '<tr>',
                                                '  <td class="title">Pickup:</td>',
                                                '  <td class="value">{pickupWaitAmount}s</td> ',
                                                '  <td class="title">Duration:</td>',
                                                '  <td class="value">{durationMinutes}:{durationSeconds}</td> ',
                                                '  <td class="title">Idle:</td> ',
                                                '  <td class="value">{timeSinceLastVisitorMessage}s</td> ',
                                                '</tr>',
                                                '<tr>',
                                                '  <td class="title">Location:</td>',
                                                '  <td class="value" colspan="5">{location}</td> ',
                                                '</tr>',
                                                '<tr>',
                                                '  <td class="title">Referrer:</td>',
                                                '  <td class="value" colspan="5"><a href="{referrer}" target="_blank">{referrer}</a></td> ',
                                                '</tr>',
                                            '</table>'
                                        ),
                                        name: 'stats',
                                        cls: 'x-text',
                                        padding: 3,
                                        height: statsHeight
                                    }]
                                },{
                                    flex: .65,
                                    layout: 'fit',
                                    items: [{
                                        title: 'Script',
                                        cls: 'x-text',
                                        padding: 5,
                                        iconCls: 'x-icon-script',
                                        name: 'agentscript',
                                        height: scriptHeight,
                                        xtype: 'apexchat.searchabletree',
                                        rootVisible: false,
                                        root: new Ext.tree.TreeNode({
                                            text: 'Script',
                                            value: 0,
                                            item: null
                                        })
                                    }]
                                }]
                            }]
                        },{
                            title: 'Send Lead',
                            xtype: 'apexchat.sendleadform',
                            chatId: this.chatId
                        }]
                    }]
                }]
            });
            ApexChat.ExtChatRoom.superclass.initComponent.apply(this, arguments);
            
            // initialize the underlying chat room class
            this.init({
                chatId: this.chatId || 0,
                companyId: this.companyId || 0
            });
            
            if(this.status != ApexChat.ChatStatus.Inactive){
                this.statsUpdateInterval = window.setInterval(this.updateStats.bind(this), 1000);
            }
        },
        destroy: function(){
            window.clearInterval(this.statsUpdateInterval);
            ApexChat.ExtChatRoom.superclass.destroy.apply(this, arguments);
        },
        updateStats: function(){
            // ignore this until we have some stats to show
            if(!this.history.length){
                return;
            }
           
            var firstMessage = this.history[0];
            var lastMessage = this.history[this.history.length-1];
            var duration = ApexChat.timeSince(this.createdOn); //parseInt( ( (new Date()).getTime() - this.createdOn.getTime() ) / 1000);
            var responseTimes = [];
            var lastAgentMessage = null;
            var lastVisitorMessage = null;
            var visitorMessageCount = 0;
            var agentMessageCount = 0;
            
            for(var i = 0; i < this.history.length; i++){
                var msg = this.history[i];
                if(msg.userId > 0){
                    lastAgentMessage = msg;
                    agentMessageCount++;
                    
                    // loop back through the messages until we hit a visitor message
                    for(var j = i; j >= 0; j--){
                        var prevMessage = this.history[j];
                        if(prevMessage.userId === 0){
                            // store the difference
                            responseTimes.push( parseInt( (msg.createdOn.getTime() - prevMessage.createdOn.getTime()) / 1000));
                            break;
                        }
                    }
                }else{
                    lastVisitorMessage = msg;
                    visitorMessageCount++;
                }
            }
            
            var avg = 0;
            for(var i = 0; i < responseTimes.length;i++){
                avg += responseTimes[i];
            }
            if(responseTimes.length > 0){
                avg = avg / responseTimes.length;
            }
            
            var timeSinceVisitor = lastVisitorMessage ? ApexChat.timeSince(lastVisitorMessage.createdOn) : 0; //parseInt( ((new Date()).getTime() - lastVisitorMessage.createdOn.getTime()) / 1000 ) : 0;
            
            var stats = {
                companyName: this.companyName,
                companyUrl: this.companyUrl.indexOf('http') > -1 ? this.companyUrl : 'http://' + this.companyUrl,
                chatId: this.chatId,
                duration: duration,
                durationMinutes: parseInt(duration / 60),
                durationSeconds: String.leftPad((duration % 60), 2,'0'),
                location: this.location,
                referrer: this.referrer,
                pickedUp: this.pickupTime,
                timeSinceLastVisitorMessage: timeSinceVisitor,
                lastMessageWasVisitor: (lastMessage.userId === 0),
                averageResponseTime: avg,
                startTime: this.createdOn,
                pickupWaitAmount: parseInt((( firstMessage.createdOn.getTime() - this.createdOn.getTime() ) / 1000))
            };
            
            this.find('name','stats')[0].update(stats);
            
            this.stats = stats;
            this.refreshTitle();
        },
        close: function(){
            // end the underlying chat room class
            var me = this;
            Ext.Msg.confirm('Are you sure?', 'Are you sure you want to close this chat?', function(btn){
                if(btn == 'yes'){
                    me.endChat(true);
                    me.destroy();
                }
            });
        },
        expand: function(){
            this.find('name','wrap')[0].expand();
        },
        onMetaArrived: function(args){
            // if the location arrives after opening the chat, show it
            if(args.location){
                this.location = location;
            }
        },
        onChatJoined: function(args){
            // if the location arrives after joining, show it
            if(args.meta.room && args.meta.room.location){
                this.location = args.meta.room.location;
            }
            if(args.meta.agentScript){
                this.addAgentScript(args.meta.agentScript);
            }
            this.onChat();
        },
        onChatJoinFailure: function(args){
            // this happens if they're opening a historical chat
            // the details are still returned though, and we use that info to update the stats
            ApexChat.extend(this, args.meta.room);
            this.updateStats();
            if(args.meta.agentScript){
                this.addAgentScript(args.meta.agentScript);
            }
            this.onChat();
        },
        onChat: function(){
            this.flagLead.setText(this.isLead ? this.unflagAsLeadText : this.flagAsLeadText);
            this.flagLead.setIconClass(this.isLead ? 'x-icon-lead-unflag' : 'x-icon-lead-flag');
        },
        addAgentScript: function(script){
            var tree = this.find('name','agentscript')[0];
            var me = this;
            tree.root.beginUpdate();
            var findAndFocus = function(node){
                var txt = me.find('xtype','textarea')[0];
                var prepend = txt.getValue();
                if(prepend){
                    prepend += ' ';
                }
                // the node can start with text followed by "::"; if it does, then it's instructional,
                // and shouldn't be included
                var val = node.text;
                if(val && val.indexOf('::') > -1){
                    val = val.split('::')[1].replace(/^\s+/g,'');
                }
                
                txt.setValue(prepend + val);
                window.setTimeout(txt.focus.bind(txt),1);
            };
            
            for(var i = 0; i < script.questions.length; i++){
                var question = script.questions[i];
                var qNode = tree.root.appendChild(new Ext.tree.TreeNode({
                    text: question.text,
                    value: question.id,
                    item: question,
                    leaf: false,
                    singleClickExpand: true
                }));
                for(var j = 0; j < question.answers.length; j++){
                    var answer = question.answers[j];
                    qNode.appendChild(new Ext.tree.TreeNode({
                        text: answer.text,
                        value: answer.id,
                        item: answer,
                        leaf: true,
                        cls: 'answer',
                        listeners: {
                            click: findAndFocus
                        }
                    })); 
                }
            }
            tree.root.endUpdate();
        },
        onChatEnded: function(){
        },
        onParticipantsChanged: function(participants){
            var unique = {};
            var result = [];
            var visitor = false;
            for(var i = 0; i < participants.length; i++){
                var n = participants[i].displayName;
                if(!unique[n]){
                    unique[n] = true;
                    result.push(n);
                }
                if(n.toLowerCase() == 'visitor'){
                    visitor = true;
                }
            }
            
            this.users = result;
        },
        refreshTitle: function(){
            var users = '', stats = '';
            if(this.stats && this.stats.lastMessageWasVisitor && this.stats.timeSinceLastVisitorMessage){
                stats = ' - ' + ApexChat.getTimerElement('span', this.stats.timeSinceLastVisitorMessage);
                //stats = ' - <span>' +  + 's</span>';
            }
            if(this.users){
                users = ' - Chat with ' + this.users.join(', ');
            }
            this.find('name','wrap')[0].setTitle('Chat # ' + this.options.chatId + ' (' + this.companyName + ')' + users + stats);
            
            // todo: alter button text
        },
        onTypingArrived: function(args, self){
            if(self){ return; }
            var icon = Ext.query('.apexchat-typing-indicator', this.body.dom)[0];
            icon = new Ext.Element(icon);
            icon.dom.innerHTML = args.participantDisplayName + ' is typing...';
            if(args.indicator){
                icon.show();
            }else{
                icon.hide();
            }
            ApexChat.TypingArrived(this.chatId, args.indicator);
        },
        onMessageArrived: function(args, isInitialState, index){
            var body = this.find('name','log')[0].body;
            if(isInitialState && index === 0){
                // when the state loads, clear the contents with the first item
                body.dom.innerHTML = '';
                this.history = [];
            }
            
            // avoid duplicate messages; this is really only a problem
            // when the initial greeting is displayed, since it's published
            // right as the user subscribes
            if(Ext.get('chatlog_' + args.id)){
                return;
            }
            
            if(args.userId == -1){
                // system notification, which only happens if the chat is ended
                // in that case, stop updating the status
                window.clearInterval(this.statsUpdateInterval);
            }
            
            this.history.push(args);
            
            var xt = new Ext.XTemplate(
                '<div class="message<tpl if="userId == -1"> system</tpl><tpl if="userId == 0"> visitor</tpl><tpl if="userId &gt; 0"> agent</tpl>">',
                    '<span class="speaker">[{createdOn:date("g:i:s a")}] {participantDisplayName}: </span>',
                    '<span class="text" id="chatlog_{id}">{text}</span>',
                '</div>'
            );
            
            xt.append(body, args);
            body.dom.scrollTop = body.dom.scrollHeight;
            
            if(!isInitialState){
                ApexChat.MessageArrived(this.chatId, args);
            }
        },
        highlight: function(multiple){
            this.body.addClass('highlight'); //find('name','log')[0].body.setStyle({ 'background-color': '#efefef' });
        },
        unhighlight: function(){
            this.body.removeClass('highlight');
            //this.find('name','log')[0].body.setStyle({ 'background-color': '#fff' });
        },
        focus: function(){
            this.find('xtype','textarea')[0].focus();
        }
    });
    Ext.reg('apexchat.extchatroom', ApexChat.ExtChatRoom);
    
    // give our ExtJS chat room the core chat room functionality;
    // since we're mixing inheritance styles here, if we want to access
    // the underlying method, we'll have to access the prototype directly from the subclass
    Ext.applyIf(ApexChat.ExtChatRoom.prototype, ApexChat.ChatRoomBase.prototype);

    // a grid to display the various call queues
    ApexChat.QueueGrid = Ext.extend(ApexChat.LocalGrid, {
        needsLastMessage: true,
        initComponent: function(config){   
            Ext.apply(this, {
                viewConfig: {
                    forceFit: true,
                    getRowClass: function(record, index, rowParams, store){
                        if(record.get('generallyAvailable')){
                            return 'x-grid-generallyAvailable';
                        }
                    }
                },
                recordDef: Ext.data.Record.create(ApexChat.Definitions.ChatRoom),
                autoExpandColumn: 'lastMessage',
                colModel: new Ext.grid.ColumnModel({
                    defaults: {
                        sortable: true,
                        align: 'center',
                        renderer: ApexChat.ValueRenderer
                    },
                    columns: this.getColumns()
                }),
                listeners: {
                    rowclick: function(grid, rowIndex, e){
                        var record = grid.getStore().getAt(rowIndex);
                        var room = new ApexChat.Data.ChatRoom(record.json);
                        // if the room already is opened, we move to it,
                        // but not before
                        var findIt = !(room.isAwaitingPickup());
                        ApexChat.ActivateChatRoom(record.json, findIt);
                    }
                }
            });
            ApexChat.QueueGrid.superclass.initComponent.apply(this, arguments);
            
            window.setInterval(this.updateTime.bind(this),1000);
        },
        getColumns: function(){
            // overridden in base classes
            return [];
        },
        updateTime: function(){
            var columnIndex = 0;
            var columns = this.getColumnModel().columns;
            for(var i = 0; i < columns.length; i++){
                if(columns[i].dataIndex == 'lastActivityDate'){
                    columnIndex = i;
                    break;
                }
            }
            for(var i = 0; i < this.store.getCount(); i++){
                var cell = this.getView().getCell(i, columnIndex);
                var record = this.getStore().getAt(i);
                var value = record.get('lastActivityDate');
                cell.firstChild.innerHTML = ApexChat.TimeSinceLastMessageRenderer(value, null, record);
            }
        },
        findChatRecord: function(chatId){
            // find the record if it exists
            var record = this.store.getAt(this.store.findBy(function(r){
                return r.get('chatId') == chatId;
            }));
            return record;
        },
        removeChatRecord: function(room){
            var record = this.findChatRecord(room.getId());
            if(record){
                this.store.remove(record);
            }
        },
        upsertChatRecord: function(room){
            // find the record if it exists
            var chatId = room.getId();
            var store = this.store;
            var record = this.findChatRecord(chatId);
            
            if(!record){
                // new chat - insert it at the top of the list
                var result = store.reader.readRecords([room.data]);
                store.insert(store.getCount(), result.records);
            }else{
                // existing chat - update it
                record.beginEdit();
                for(var prop in room.data){
                    if(room.data.hasOwnProperty(prop)){
                        record.set(prop, room.data[prop]);
                    }
                }
                // make sure we update the participants list too
                record.json.participants = room.data.participants;
                record.endEdit();
                record.commit();
            }
        },
        chatIdRenderer: function(value, meta, record){
            return ApexChat.TipRenderer(value, 'Chat #' + value + '<br/><br/>' + record.get('location'));
        },
        numberRenderer: function(value, meta, record){
            if(record.get('typingIndicator')){
                return '<div style="position:relative">' + record.json.participants.length + ' <div class="grid-typing-indicator"></div></div>';
            }
            return record.json.participants.length;         
        }
    });
    Ext.reg('apexchat.queuegrid', ApexChat.QueueGrid);
    
    ApexChat.IncomingChatsGrid = Ext.extend(ApexChat.QueueGrid, {
        getColumns: function(){
            return [
                { dataIndex: 'chatId', header: 'ID', width: 67, renderer: this.chatIdRenderer },
                { dataIndex: 'companyName', header: 'Company', width: 110 },
                { id: 'lastActivityDate', dataIndex: 'lastActivityDate', width: 45, header: 'Idle', renderer: ApexChat.TimeSinceLastMessageRenderer },
                { id: 'location', align: 'left', dataIndex: 'location', header: 'Location', renderer: ApexChat.ValueRenderer },
                { header: '#', align: 'left', width: 20, renderer: this.numberRenderer }
            ];
        }
    });
    Ext.reg('apexchat.incomingchatsgrid', ApexChat.IncomingChatsGrid);
    
    ApexChat.MyChatsGrid = Ext.extend(ApexChat.QueueGrid, {
        getColumns: function(){
            return [
                { dataIndex: 'chatId', header: 'ID', width: 67, renderer: this.chatIdRenderer },
                { dataIndex: 'companyName', header: 'Company', width: 110 },
                { id: 'lastActivityDate', dataIndex: 'lastActivityDate', width: 45, header: 'Idle', renderer: ApexChat.TimeSinceLastMessageRenderer },
                { id: 'lastMessage', align: 'left', dataIndex: 'lastMessage', header: 'Last Message', renderer: ApexChat.LastMessageRenderer },
                { header: '#', align: 'left', width: 20, renderer: this.numberRenderer }
            ];
        }
    });
    Ext.reg('apexchat.mychatsgrid', ApexChat.MyChatsGrid);
    
    ApexChat.AllChatsGrid = Ext.extend(ApexChat.QueueGrid, {
        getColumns: function(){
            return [
                { dataIndex: 'chatId', header: 'ID', width: 67, renderer: this.chatIdRenderer },
                { dataIndex: 'companyName', header: 'Company', width: 110 },
                { id: 'lastActivityDate', dataIndex: 'lastActivityDate', width: 45, header: 'Idle', renderer: ApexChat.TimeSinceLastMessageRenderer },
                { id: 'pickedUpByUsername', align: 'left', dataIndex: 'pickedUpByUsername', header: 'Owner', renderer: ApexChat.ValueRenderer },
                { header: '#', align: 'left', width: 20, renderer: this.numberRenderer }
            ];
        }
    });
    Ext.reg('apexchat.allchatsgrid', ApexChat.AllChatsGrid);
    
    var permissionBasedItems = [];
    
    if(ApexChat.Settings.CurrentUserIsOpsManager){
        permissionBasedItems.push({
            xtype: 'button',
            text: 'Chats',
            id: 'chats',
            iconCls: 'x-icon-chats',
            handler: function(){
                Ext.getCmp('cardstack').getLayout().setActiveItem(0);
            }
        },{
            xtype: 'button',
            text: 'Operations',
            id: 'operations',
            iconCls: 'x-icon-operations',
            handler: function(){
                Ext.getCmp('cardstack').getLayout().setActiveItem(1);
            }
        });
    }else{
        permissionBasedItems.push({
            xtype: 'tbtext',
            text: 'My Stats',
            id: 'agentstats',
            iconCls: 'x-icon-stats'
        });
    }
    
    new Ext.Viewport({
        layout: 'border',
        items: [{
            region: 'north',
            xtype: 'toolbar',
            height: 30,
            items: permissionBasedItems.concat([
            '->',
            '-',
            {
                xtype: 'tbtext',
                name: 'status',
                text: '<b' + (ApexChat.Settings.CurrentUserStatus == 'online' ? '' : ' class="offline"')  + '>You are ' + (ApexChat.Settings.CurrentUserStatus == 'online' ? 'Online' : 'Offline') + '!</b>'
            },
            '-',
            {
                text: ApexChat.Cookies.get('soundDisabled') == 'true' ? 'Enable Sound' : 'Disable Sound',
                soundDisabled: (ApexChat.Cookies.get('soundDisabled') == 'true'),
                iconCls: ApexChat.Cookies.get('soundDisabled') == 'true' ? 'x-icon-sound-disabled' : 'x-icon-sound-enabled',
                handler: function(btn){
                    if(btn.soundDisabled){
                        ApexChat.Cookies.set('soundDisabled', false);
                        ApexChat.Sound.unmute();
                        btn.soundDisabled = false;
                        btn.setText('Disable Sound');
                        btn.setIconClass('x-icon-sound-enabled');
                    }else{
                        ApexChat.Sound.mute();
                        ApexChat.Cookies.set('soundDisabled', true);
                        btn.soundDisabled = true;
                        btn.setText('Enable Sound');
                        btn.setIconClass('x-icon-sound-disabled');
                    }
                }
            },
            '-',
            {
                text: 'Go ' + (ApexChat.Settings.CurrentUserStatus == 'online' ? 'Offline' : 'Online'),
                status: ApexChat.Settings.CurrentUserStatus,
                iconCls: (ApexChat.Settings.CurrentUserStatus == 'online' ? 'x-icon-online' : 'x-icon-offline'),
                handler: function(btn){
                    btn.setText('Changing status...');
                    btn.disable();
                    fm.network.xhr({
                        retries: 0,
                        url: '/Services/ApexChatService.svc/users/active/' + (btn.status == 'online' ? 0 : 1),
                        data: { },
                        onSuccess: function(args){
                            if(!args.success){
                                Ext.Msg.alert('Error',args.error);
                                return;
                            }
                            // change the status
                            btn.status = (btn.status == 'online' ? 'offline' : 'online');
                            var ct = btn.ownerCt;
                            
                            if(btn.status == 'online'){
                                btn.setText('Go Offline');
                                btn.setIconClass('x-icon-online');
                                ct.find('name', 'status')[0].setText('<b>You are online!</b>');
                            }else{
                                btn.setText('Go Online');
                                btn.setIconClass('x-icon-offline');
                                ct.find('name', 'status')[0].setText('<b class="offline">You are offline!</b>');
                            }
                            btn.enable();
                        },
                        onFailure: function(args){
                            Ext.Msg.alert('Error', args.error);
                        }
                    });
                }
            },
            {
                text: 'Logout ' + ApexChat.Settings.CurrentUserDisplayName,
                iconCls: 'x-button-logout',
                handler: function(){
                    fm.websync.client.disconnect({ sync: true });
                    window.location = 'Logout.aspx';
                }
            }])
        },{
            region: 'center',
            layout: 'card',
            id: 'cardstack',
            activeItem: 0,
            border: false,
            items: [{
                layout: 'border',
                border: false,
                items: [{
                    region: 'west',
                    width: 260,
                    collapsible: true,
                    split: true,
                    title: 'Overview',
                    autoScroll: true,
                    layout: 'border',
                    defaults: { border: false }, 
                    items: [{
                        // extra layer of nesting to get a title bar
                        region: 'north',
                        title: 'Incoming Chats',
                        iconCls: 'x-icon-incomingChats',
                        layout: 'fit',
                        height: 200,
                        split: true,
                        group: 'chatgrids',
                        xtype: 'apexchat.incomingchatsgrid',
                        needsLastMessage: false,
                        id: 'incomingChats'
                    },{
                        // extra layer of nesting to get a title bar
                        region: 'center',
                        
                        title: 'Active Chats',
                        iconCls: 'x-icon-activeChats',
                        layout: 'fit',
                        split: true,
                        items: [{
                            xtype: 'tabpanel',
                            border: false,
                            deferredRender: false,
                            activeTab: 0,
                            items: [{
                                title: 'My Chats',
                                layout: 'fit',
                                group: 'chatgrids',
                                xtype: 'apexchat.mychatsgrid',
                                id: 'myActiveChats'
                            },{
                                title: 'All Chats',
                                layout: 'fit',
                                xtype: 'apexchat.allchatsgrid',
                                id: 'allActiveChats'
                            },{
                                title: 'History',
                                xtype: 'apexchat.searchablegrid',
                                url: '/Services/ApexChatService.svc/chatrooms/',
                                tbar: {
                                    items: [{
                                        xtype: 'button',
                                        text: 'Only Leads',
                                        iconCls: 'x-icon-lead',
                                        enableToggle: true,
                                        handler: function(btn){
                                            var grid = btn.ownerCt.ownerCt;
                                            grid.store.baseUrl = grid.url;
                                            if(btn.pressed){
                                                grid.store.on('beforeload', grid.leadsFilter);
                                            }else{
                                                grid.store.un('beforeload', grid.leadsFilter);
                                            }
                                            grid.refresh();
                                        }
                                    }]
                                },
                                leadsFilter: function(store, options){
                                    store.proxy.conn.url = this.baseUrl + '?filter=leads';
                                },
                                recordDef: Ext.data.Record.create(ApexChat.Definitions.ChatRoom),
                                colModel: new Ext.grid.ColumnModel({
                                    defaults: {
                                        sortable: true,
                                        align: 'center'
                                    },
                                    columns: [
                                        { id: 'chatId', header: 'ID', dataIndex: 'chatId', width: 50},
                                        { header: 'Company Name', dataIndex: 'companyName'},
                                        { header: 'Started', dataIndex: 'createdOn', renderer: ApexChat.DateRenderer },
                                        { header: 'Picked Up', dataIndex: 'pickedUpOn', renderer: ApexChat.DateRenderer },
                                        { header: 'Ended', dataIndex: 'lastActivityDate', renderer: ApexChat.DateRenderer },
                                        { header: 'Owner', dataIndex: 'pickedUpByUsername', renderer: ApexChat.ValueRenderer }
                                        
                                    ]
                                }),
                                listeners: {
                                    rowclick: function(grid, rowIndex, e){
                                        var record = grid.getStore().getAt(rowIndex);
                                        ApexChat.ActivateChatRoom(record.json, true);
                                    }
                                }
                            }]
                        }]
                    }]
                
                },{
                    region: 'center',
                    id: 'chatrooms',
                    autoScroll: true,
                    title: 'Chat Rooms',
                    defaults: { padding: 10, border: false },
                    items: [] // added dynamically as needed
                }]
            },{
                xtype: 'panel',
                layout: 'fit',
                border: false,
                items: [{
                    title: 'Available Agents',
                    id: 'availableagents',
                    deferLoad: true, // we'll refresh manually once we're connected
                    xtype: 'apexchat.searchablegrid',
                    tbar: {
                        items: [{
                            id: 'availableagents.datepicker',
                            text: 'Agent Stats for ' + (new Date()).format('m/d/y') + ' (PST)',
                            menu: new Ext.menu.DateMenu({
                                maxDate: (new Date()),
                                handler: function(dp, date){
                                    var btn = Ext.getCmp('availableagents.datepicker');
                                    btn.setText('Agent Stats for ' + date.format('m/d/y') + ' (PST)');
                                    var grid = Ext.getCmp('availableagents');
                                    grid.store.baseUrl = grid.url;
                                    grid.store.date = date.format('m/d/y');
                                    grid.store.on('beforeload', grid.dateFilter);
                                    grid.refresh();
                                }
                            })
                        }]
                    },
                    dateFilter: function(store, options){
                        store.proxy.conn.url = this.baseUrl + '?date=' + this.date;
                    },
                    pageSize: 50,
                    url: '/Services/ApexChatService.svc/users/',
                    recordDef: Ext.data.Record.create(ApexChat.Definitions.User),
                    colModel: new Ext.grid.ColumnModel({
                        defaults: {
                            sortable: true,
                            align: 'center',
                            renderer: ApexChat.ValueRenderer
                        },
                        columns: [
                            { id: 'id', header: 'ID', dataIndex: 'id', width: 50},
                            { header: 'Username', dataIndex: 'username' },
                            { header: 'Name', dataIndex: 'displayName'},
                            { header: 'Online', dataIndex: 'online', renderer: 
                                function(value, meta, record){
                                    if(!record.get('liveStats')){
                                        return '-';
                                    }
                                    return ApexChat.CheckRenderer(value, meta, record);
                                }
                            },
                            { header: 'Active', dataIndex: 'active', renderer: 
                                function(value, meta, record){
                                    if(!record.get('liveStats')){
                                        return '-';
                                    }
                                    var check = ApexChat.CheckRenderer(value, meta, record);
                                    if(!record.get('active') && record.get('online')){
                                        check += '&nbsp;<div style="text-align:middle">' + ApexChat.IdleRenderer(value, meta, record) + '</div>';
                                    }
                                    return check;
                                }
                            },
                            { header: 'Active Chats', dataIndex: 'activeChats', renderer: ApexChat.LiveStatsRenderer },
                            { header: 'Sessions', dataIndex: 'sessionCount', renderer: ApexChat.LiveStatsRenderer },
                            { header: 'First Active', dataIndex: 'firstActiveToday', renderer: ApexChat.TimeRenderer },
                            { header: 'Last Logout', dataIndex: 'lastLogoutToday', renderer: ApexChat.TimeRenderer },
                            { header: 'Active Time', dataIndex: 'activeMinutesToday', renderer: ApexChat.ActiveRenderer },
                            { header: 'Chats Assigned', dataIndex: 'totalChatsAssignedToday' },
                            { header: 'Chats Missed', dataIndex: 'totalChatsMissedToday'},
                            { header: 'Blank Chats', dataIndex: 'blankChatsToday'},
                            { header: 'Leads Created', dataIndex: 'totalLeadsCreatedToday', renderer: 
                                function(value, meta, record){
                                    var total = record.get('totalChatsAssignedToday') || 0;
                                    var blank = record.get('blankChatsToday') || 0;
                                    var title = '{0} ({1}%, {2}%)'.format(value, formatPercent(value, total), formatPercent(value, (total - blank)));
                                    var qtip = '{0} lead, {1}% conversion including blank chats, {2}% conversion excluding blank chats'.format(value, formatPercent(value, total), formatPercent(value, (total - blank)));
                                    return '<span ext:qtip="' + qtip + '">' + title + '</span>';
                                }
                            },
                            { header: 'Avg. Pickup', dataIndex: 'averagePickupTimeToday', renderer: ApexChat.SecondsRenderer },
                            { header: 'Avg. Response', dataIndex: 'averageResponseTimeToday', renderer: ApexChat.SecondsRenderer}
                            
                        ]
                    })
                }]
            }]
        }]
    });
    
    
    // create a simple wrapper around our chat room data so we can add some helper methods
    ApexChat.Data = {};
    ApexChat.Data.ChatRoom = function(data){
        this.data = data;
    };
    ApexChat.extend(ApexChat.Data.ChatRoom.prototype, {
        isParticipant: function(userId){
            var participant = false;
            for(var j = 0; j < this.data.participants.length; j++){
                if(this.data.participants[j].userId == userId){
                    return true;
                }
            }
            return false;
        },
        currentUserIsParticipant: function(){
            return this.isParticipant(ApexChat.Settings.CurrentUserId);
        },
        isAssignedTo: function(userId){
            return ((this.data.assignedToAgent == userId || this.data.assignedToOperator == userId) && !this.data.generallyAvailable) || this.isParticipant(userId);
        },
        isAssignedToCurrentUser: function(){
            return this.isAssignedTo(ApexChat.Settings.CurrentUserId);
        },
        isGenerallyAvailable: function(){
            return this.data.generallyAvailable;
        },
        isActive: function(){
            return this.data.status == ApexChat.ChatStatus.Active;
        },
        isInactive: function(){
            return this.data.status == ApexChat.ChatStatus.Inactive;
        },
        isAwaitingPickup: function(){
            return this.data.status == ApexChat.ChatStatus.AwaitingPickup;
        },
        isAvailable: function(){
            return this.data.status == ApexChat.ChatStatus.AwaitingPickup || this.data.pickedUpBy == 0;
        },
        hasEverBeenPickedUp: function(){
            return this.data.pickedUpBy > 0;
        },
        getId: function(){
            return this.data.chatId;
        },
        getCompanyName: function(){
            return this.data.companyName;
        },
        getReferrer: function(){
            return this.data.referrer;
        },
        getLocation: function(){
            return this.data.location;
        },
        getData: function(){
            return this.data;
        },
        getPickedUpByUsername: function(){
            return this.data.pickedUpByUsername;
        }
    });
   
    ApexChat.Sound = {};
    ApexChat.extend(ApexChat.Sound, {
        stopped: {
            notification: true,
            newmessage: true
        },
        mute: function(){
            if(soundManager.supported()){
                soundManager.mute();
            }
        },
        unmute: function(){
            if(soundManager.supported()){
                soundManager.unmute();
            }
        },
        notifyIncomingChat: function(){
            // if the sound is stopped, start it
            if(ApexChat.Sound.stopped['notification']){
                ApexChat.Sound.stopped['notification'] = false;
                ApexChat.Sound.play('notification');
            }
        },
        notifyNewMessage: function(){
            // never play > 1
            ApexChat.Sound.play('newmessage');
        },
        play: function(sound){
            if(soundManager.supported()){
                var id = sound;
                soundManager.createSound({
                    id: id,
                    url: '/media/' + sound + '.mp3'
                });
                soundManager.play(id,{
                    //multiShot: true,
                    onfinish: function(){
                        if(!ApexChat.Sound.stopped[sound]){
                            ApexChat.Sound.play(sound);
                        }
                    }
                });
            }
        },
        stopNotifyIncomingChat: function(){
            ApexChat.Sound.stopped['notification'] = true;
        }
    });
    
    // adjusts a time based on the local time to match times coming from the server
    ApexChat.timeSince = function(dt){
        return parseInt( ( (new Date()).getTime() - dt.getTime() ) / 1000) + serverOffset;
    };
    
    // update agent stats 1x per minute
    ApexChat.updateAgentStats = function(){
        var tbItem = Ext.getCmp('agentstats');
        if(!tbItem){
            // admins don't get this stuff
            return;
        }
        
        fm.network.xhr({
            retries: 0,
            url: '/Services/ApexChatService.svc/agentstats',
            onSuccess: function(args){
                if(!args.success){
                    if(window && window.console){
                        window.console.error(args);
                    }
                    return;
                }
                if(args.data.numberOfCallsDay > 0){
                    args.data.conversionDay = ((args.data.leadsSentDay / args.data.numberOfCallsDay) * 100);
                    args.data.conversionDay = parseInt(args.data.conversionDay, 10) + '%';
                }else{
                    args.data.conversionDay = '-';
                }
                if(args.data.numberOfCallsWeek > 0){
                    args.data.conversionWeek = ((args.data.leadsSentWeek / args.data.numberOfCallsWeek) * 100);
                    args.data.conversionWeek = parseInt(args.data.conversionWeek, 10) + '%';
                }else{
                    args.data.conversionWeek = '-';
                }
                var tpl = new Ext.XTemplate(
                    '<div class="agentstats x-text">',
                        '  <span class="title group first">Today:</span>',
                        '  <span class="title">Conversion:</span>',
                        '  <span class="value">{conversionDay}</span> ',
                        '  <span class="title">Avg Pickup:</span>',
                        '  <span class="value">{averagePickupTimeDay}s</span> ',
                        '  <span class="title">Avg Response:</span>',
                        '  <span class="value">{averageResposeTimeDay}s</span> ',
                        '  <span class="title">Missed:</span>',
                        '  <span class="value">{missedChatsDay}</span> ',
                        '  <span class="title group">This Week:</span>',
                        '  <span class="title">Conversion:</span>',
                        '  <span class="value">{conversionWeek}</span> ',
                        '  <span class="title">Avg Pickup:</span>',
                        '  <span class="value">{averagePickupTimeWeek}s</span> ',
                        '  <span class="title">Avg Response:</span>',
                        '  <span class="value">{averageResposeTimeWeek}s</span> ',
                    '</div>'
                );
                
                tbItem.setText(tpl.applyTemplate(args.data));
            },
            onFailure: function(args){
                Ext.Msg.alert('Error', args.error);
            }
        });
        window.setTimeout(ApexChat.updateAgentStats, 60000);
    };
    
    ApexChat.updateAgentStats();
    
    // global stuff
    var client = fm.websync.client;
    client.initialize({
        requestUrl: '/request.ashx',
        stream: {
            requestUrl: '/request.ashx'
        },
        autoDisconnect: false,
        quiet: true 
    });
    
    // connect; the response from the connect indicates whether or not we are authenticated
    var authenticated = null;
    client.connect({
        stayConnected: true,
        retries: 1, // immediately fail out
        onSuccess: function(args){
            if(args.isReconnect){
                ApexChat.StreamReconnect(args);
            }
            if(args.meta === false){
                // if we're not authenticated, but we were previously,
                // refresh the page (which will force a redirect)
                if(authenticated === true){
                    var l = window.location.href;
                    window.location.href = l.substring(0, l.indexOf('#') > -1 ? l.indexOf('#') : l.length);
                }
            }
            authenticated = args.meta;
            // positive if the server is ahead, negative if it's behind
            serverOffset = parseInt( ( args.timestamp.getTime() - (new Date()).getTime() ) / 1000 );
            
            // once we're connected, refresh the agent data so we're up to date
            var grid = Ext.getCmp('availableagents');
            if(grid){
                grid.refresh();
            }
        },
        onFailure: function(args){
            ApexChat.StreamFailure(args);
            client.setId(0);
        },
        onStreamFailure: function(args){
            ApexChat.StreamFailure(args);
            client.setId(0);
        }
    });
    
    var notifiedChats = {};
    
    // ops managers get nofications about agent changes
    if(ApexChat.Settings.CurrentUserIsOpsManager){
        client.subscribe({
            channel: '/availableagents',
            onReceive: function(args){
                var grid = Ext.getCmp('availableagents');
                grid.store.each(function(record){
                    if(record.get('id') == args.data.id && record.get('liveStats')){
                        for(var i = 0; i < record.fields.keys.length; i++){
                            var key = record.fields.keys[i];
                            if(args.data.hasOwnProperty(key)){
                                record.set(key, args.data[key]);
                            }
                        }
                        record.commit();
                    }
                });
            }
        });
        
        window.setInterval(function(){
            var grid = Ext.getCmp('availableagents');
            grid.store.each(function(record){
                record.commit();
            });
        },1000);
    }
    client.subscribe({
        channel: ['/generalqueue'],
        onSuccess: function(args){ 
            try{
                // the success of this subscribe also returns us a list
                // of active chats
                var incomingChats = [];
                var allActiveChats = [];
                var myActiveChats = [];
                
                for(var i = 0; i < args.meta.length; i++){
                    var room = new ApexChat.Data.ChatRoom(args.meta[i]);
                    
                    if((room.isAssignedToCurrentUser() || room.isGenerallyAvailable()) && room.isAvailable()){
                        incomingChats.push(room.data); 
                    }
                    
                    /*if( && room.isAwaitingPickup()){
                        generalQueue.push(room.data);
                    }*/
                    
                    if(room.isAssignedToCurrentUser() && room.isActive()){
                        myActiveChats.push(room.data);
                    }
                    
                    if(room.isActive()){
                        allActiveChats.push(room.data);
                    }
                    
                    // activate the chat if they're involved
                    if(room.currentUserIsParticipant()){
                        ApexChat.ActivateChatRoom(room.data, false);
                    }
                }

                 // add items to the general queue
                //Ext.getCmp('generalQueue').store.loadData(generalQueue);
                
                // add items to the personal queue
                Ext.getCmp('incomingChats').store.loadData(incomingChats);
                
                // add items to the active queue
                Ext.getCmp('allActiveChats').store.loadData(allActiveChats);
                
                // add items to the active queue
                Ext.getCmp('myActiveChats').store.loadData(myActiveChats);
            
            }catch(ex){
                alert(ex.message);
            }
        },
        onReceive: function(args){
            // wrap up the incoming data to get us some helper methods
            var room = new ApexChat.Data.ChatRoom(args.data);
            
            // update the appropriate queue with any changes
            if(room.isActive()){
                if(room.isAssignedToCurrentUser()){
                    Ext.getCmp('myActiveChats').upsertChatRecord(room);
                }
                Ext.getCmp('allActiveChats').upsertChatRecord(room);
            }else if(room.isAvailable()){
                if (room.isAssignedToCurrentUser()) {
                    Ext.getCmp('incomingChats').upsertChatRecord(room);
                }else if(room.isGenerallyAvailable()){
                    if(ApexChat.Settings.CurrentUserStatus == 'online'){
                        Ext.getCmp('incomingChats').upsertChatRecord(room);
                    }
                }
            }

            // remove records from the appropriate queues
            if((!room.isAssignedToCurrentUser() && !room.isGenerallyAvailable()) || !room.isAvailable()){
                Ext.getCmp('incomingChats').removeChatRecord(room);
            }
            
            if(!room.isAssignedToCurrentUser()){
                Ext.getCmp('myActiveChats').removeChatRecord(room);
            }

            if(!room.isActive()){
                Ext.getCmp('myActiveChats').removeChatRecord(room);
                Ext.getCmp('allActiveChats').removeChatRecord(room);
            }
            
            if(Ext.getCmp('incomingChats').store.getCount() > 0){
                ApexChat.Sound.notifyIncomingChat();
                ApexChat.Title.flash();
                // window focusing doesn't work in anything but IE <= 7
                window.focus();
                self.focus();
            }else{
                ApexChat.Sound.stopNotifyIncomingChat();
                ApexChat.Title.stopFlashing();
            }
        },
        onFailure: function(args){
            alert(args.error);
        }
    });
});
