﻿Ext.namespace("TNRIS");

/** @class Displays a grid of data based on layer's gridStore
    @augments Ext.grid.GridPanel
    @param {configObject} config Configuration Parameters
    @config {array} initialLoadArgs The initial arguments from the store - to initialize the paging toolbar properly
    @config {TNRIS.ShapeLayer} layer the layer that provides the rows of data
*/
TNRIS.DataPanel = function(config) {
    Ext.apply(this, config, {
        stateful: true
    });
    
    var sm  = new Ext.grid.RowSelectionModel();
    var columns = this.layer.gridColumnSpecs();

    var bbar = null;
    var store = this.layer.getGridStore();
    if (store.getTotalCount() != 0 && store.getCount() != store.getTotalCount()) {
        bbar = new Ext.PagingToolbar({
                store: this.layer.getGridStore(),
                displayInfo: true,
                displayMsg: 'Displaying Rows {0} - {1} of {2}',
                emptyMsg: "No Rows to display"
        });
        var init = this.initialLoadArgs;
        if (init) {
            bbar.onLoad(init[0], init[1], init[2]);
        }
    }
        
    var toolbar = [];
    if (this.layer.isEditable() && (! this.layer.hasRestrictedEdit())) {
        sm = new Ext.grid.CheckboxSelectionModel();
        columns = [sm].concat(columns);
        toolbar.push({
            text: 'Delete All Selected',
            scope: this,
            handler: function(btn, e) {
                this.layer.removeRecords(this.getSelectionModel().getSelections());
            }
        }, 
        '-',
        {
            text: 'Add Column',
            scope: this,
            handler: function(btn, e) {
                Ext.Msg.prompt('Field', 'Please enter the name of the new field', function(btn, text) {
                    if (btn == 'ok') {
                        this.layer.addField(text);
                    }
                }, this);
            }
        }, 
        '-',
        {
            xtype: 'checkbox',
            boxLabel: 'Selected Only',
            listeners: { 'check': {scope: this, fn: function(checkbox, checked) { this.selectedOnly = checked; this.filterGrid(); }}}
        });
    }
    
    
    TNRIS.DataPanel.superclass.constructor.call(this, {
        autoScroll: true,
        store: store,
        columns: columns,
        sm: sm,
        bbar: bbar,
        tbar: toolbar,
        clicksToEdit: 2
    });
    
    this.on('afteredit', this.afterEdit, this);
    this.on('close', this.onClose, this);
    this.on('beforeedit', this.beforeEdit, this);

    if (this.layer.isEditable()) {
        this.on('headercontextmenu', this.onHeaderContext, this);
    }    
    this.layer.on('fieldchange', this.onFieldChange, this);
    this.layer.on('metadatachange', this.onFieldChange, this);
    TNRIS.SelectionManager.on('add', this.onGlobalSelection, this);
    TNRIS.SelectionManager.on('remove', this.onGlobalUnselection, this);
    TNRIS.SelectionManager.on('beforeunselectlayer', this.onUnselectingLayer, this);
    this.getSelectionModel().on('rowselect', this.onGridSelection, this);
    this.getSelectionModel().on('rowdeselect', this.onGridDeselection, this);
};

Ext.extend(TNRIS.DataPanel, Ext.grid.EditorGridPanel, 
    /** @scope TNRIS.DataPanel */
    {
    beforeEdit: function(o) {
        if (o.record.get(o.field) == null) {
            o.record.set(o.field, "");
        }
    },
    
    onUnselectingLayer: function(layer) {
        return this.layer != layer;
    },
    
    onGlobalSelection: function(index, o) {
        var sm = this.getSelectionModel();
        if (o.layer == this.layer && !sm.isIdSelected(o.recordId)) {
            sm.selectRecords([this.getStore().getById(o.recordId)], true);
        }
        this.filterGrid();
    },
    
    onGlobalUnselection: function(o) {
        var sm = this.getSelectionModel();
        if (o.layer == this.layer && sm.isIdSelected(o.recordId)) {
            sm.deselectRow(this.getStore().indexOfId(o.recordId));
        }
        this.filterGrid();
    },
    
    onGridSelection: function(model, rowIndex, record) {
        TNRIS.SelectionManager.selectRecord(this.layer, record);
    },
    
    onGridDeselection: function(model, index, record) {
        TNRIS.SelectionManager.unselectRecord(this.layer, record);
    },
    
    filterGrid: function() {
        if (this.selectedOnly) {
            this.getStore().filterBy(function(record) {
                return TNRIS.SelectionManager.isSelectedRecord(this.layer, record);
            }, this);
            var sm = this.getSelectionModel();
            sm.suspendEvents();
            sm.selectAll();
            sm.resumeEvents();
        } else {
            this.getStore().clearFilter();
        }
    },
    
    afterEdit: function(args) {
        this.layer.modifyRecord(args.record, this.getStore());
    },
    
    onFieldChange: function() {
        var sm = this.getSelectionModel();
        this.getColumnModel().setConfig([sm].concat(this.layer.gridColumnSpecs()));
    },
    
    onClose: function() {
        this.layer.removeListener('fieldchange', this.onFieldChange, this);
        this.layer.removeFromGrid();
        TNRIS.SelectionManager.removeListener('add', this.onGlobalSelection, this);
        TNRIS.SelectionManager.removeListener('remove', this.onGlobalUnselection, this);
        TNRIS.SelectionManager.removeListener('beforeunselectlayer', this.onUnselectingLayer, this);
    },
    
    onHeaderContext: function(grid, colIndex, e) {
        if (this.headerContext == null) {
            this.headerContext = new Ext.menu.Menu({
                items:[{
                    icon: '../images/buttons/rmove_layer.png',
                    cls: 'removeColumnCtxMenuItem',
                    text:"Remove Field", 
                    scope: this, 
                    handler:function (btn, e) {
                        btn.parentMenu.hide();
                        var cm = this.getColumnModel();
                        var dbName = cm.getDataIndex(this.headerContextIndex);
                        var guiName = cm.getColumnHeader(this.headerContextIndex);
                        Ext.Msg.confirm('Warning', 'Do you wish to remove the "' + guiName + '" field from all shapes in this layer? (This cannot be undone)',
                            function(btn) {
                                if (btn == 'yes') {
                                    this.layer.deleteField(dbName);
                                }
                            }, this);
                    }
                }]
            });
        }
        e.stopEvent();
        this.headerContextIndex = colIndex;
        this.headerContext.showAt(e.getXY());
    }
});

TNRIS.InfoTabPanel = function(config) {
    Ext.apply(this, config);
    
    this.locationSearch = new Ext.form.TextField({
        emptyText: 'Enter a location',
        width: 400,
        id: 'locationSearch',
        selectOnFocus: true,
        listeners: {
            'specialkey': {
                scope: this,
                fn: function(input, e) {
                    if (e.getKey() == Ext.EventObject.ENTER) {
                        this.onSearch();
                    }
                    return true;
                }
            }
        }
    });
    
    var searchTemplate = new Ext.XTemplate(
        '<tpl for=".">',
        '   <div class="searchResult">',
        '       <div><p>{Name}</p></div>',
        '       <div class="removeButton"></div>',
        '   </div>',
        '</tpl>');
        
    var store = this.mapPanel.getLocationStore();
    store.on('add', function(store, records, index) { this.mapPanel.selectLocation(records[0]); }, this);
        
    this.locationSearchTab = new Ext.Panel({
        title: 'Locations',
        id: 'locationTab',
        autoScroll: true,
        layout: 'fit',
        items: new Ext.DataView({
            store: store,
            tpl: searchTemplate,
            singleSelect: false,
            overClass: 'x-view-over',
            emptyText: "",
            itemSelector: 'div.searchResult',
            listeners: {
                'click': {
                    scope: this,
                    fn: this.onClick
                }
            }
        }),
        tbar: [
            'Find: ',
            this.locationSearch,
            ' ',
            new Ext.Button({
                text: 'Go',
                handler: this.onSearch,
                cls: 'infoPanelAddressSearchBtn',
                scope: this
            }),
            '->',
            ' '
        ]
    });
    
    
    this.fromDirectionSearch = new Ext.form.TextField({
        emptyText: 'Enter from address',
        width: 200,
        selectOnFocus: true,
        id: 'fromDirectionSearch',
        listeners: {
            'specialkey': {
                scope: this,
                fn: function(input, e) {
                    if (e.getKey() == Ext.EventObject.ENTER) {
                        this.onDirectionSearch();
                    }
                    return true;
                }
            }
        }
    });
    
    this.toDirectionSearch = new Ext.form.TextField({
        emptyText: 'Enter To address',
        width: 200,
        id: 'toDirectionSearch',
        listeners: {
            'specialkey': {
                scope: this,
                fn: function(input, e) {
                    if (e.getKey() == Ext.EventObject.ENTER) {
                        this.onDirectionSearch();
                    }
                    return true;
                }
            }
        }
    });
    
    this.routeByTime = new Ext.form.Radio({
        name: 'routeOptimize',
        id: 'routeOptimizeByTime',
        boxLabel : 'Shortest Time',
        cls: 'routeOptimizeByTime'
    });
    
    this.routeByDistance = new Ext.form.Radio({
        name: 'routeOptimize',
        id: 'routeOptimizeByDistance',
        boxLabel : 'Shortest Distance',
        checked: true,
        cls: 'routeOptimizeByDistance'
    });
    
    this.useTraffic = new Ext.form.Checkbox({
        id: 'useTraffic',
        cls: 'useTraffic',
        boxLabel: 'Use Traffic Info'
    });
    
    var dirSearchTemplate = new Ext.XTemplate(
        '<tpl for=".">',
        '   <div class="searchResult">',
        '           <div class="routeInfo" title="Click to refresh this route. Route options selected will be applied to this route on refresh.">',
        '                   <p>Route: {From}-{To}</p>',
        '               <div>Distance: {Distance} Miles</div>',
        '               <br/>',
        '               <div>Time: {Time}</div>',
        '               <br/>',
        '               <div>{Itinerary}</div>',
        '           </div>',
        '       <div class="removeButton"></div>',
        '       <div class="refreshButton" title="Refresh route info."></div>',        
        '   </div>',
        '</tpl>');
    
    var directionStore = this.mapPanel.getDirectionStore();
    
    directionStore.on('remove', function() {
        var recCount = directionStore.getCount();
        if (recCount <= 0){
            this.mapPanel.getMap().DeleteRoute();
        }
    }, this);
    
    //HACK: this panel introduces some 'Permission denied to get property' javascript error.
    this.directionSearchTab = new Ext.Panel({
        title: 'Directions',
        id: 'directionTab',
        autoScroll: true,
        layout: 'fit',
        items: new Ext.DataView({
            store: directionStore,
            tpl: dirSearchTemplate,
            singleSelect: false,
            overClass: 'x-view-over',
            emptyText: "",
            itemSelector: 'div.searchResult',
            listeners: {
                'click': {
                    scope: this,
                    fn: this.onDirectionClick
                }
            }
        }),
        tbar: [
            'Directions: ',
            this.fromDirectionSearch,
            'To',
            this.toDirectionSearch,
            ' ',
            new Ext.Button({
                text: 'Go',
                handler: this.onDirectionSearch,
                cls: 'infoPanelAddressSearchBtn',
                scope: this
            }),
            ' ',
            new Ext.Button({
                text: 'Clear Route',
                handler: function(){
                    this.mapPanel.getMap().DeleteRoute();
                },
                cls: 'infoPanelAddressSearchBtn',
                scope: this
            }),
            '->',
            '-',
            this.routeByDistance,
            ' ',
            this.routeByTime,
            '-',
            this.useTraffic,
            ' '
        ],
        listeners: {
            'activate' : {
                scope: this,
                fn: function(panel) {
                    /*the panel does not unmask as there is no callback when user close 
                    the ambiguous window on popup. hence a temp setting to reset direction search by switching tabs*/
                   this.clearDirectionParameters();
                }            
            }
        }
    });
    
    this.tabs = new Ext.TabPanel({
        layoutOnTabChange: true,
        items: [this.locationSearchTab,this.directionSearchTab],
        activeTab: 0,
        enableTabScroll: true,
        autoDestroy: true
    });    
    
    this.addEvents({'statechange': true});

    TNRIS.InfoTabPanel.superclass.constructor.call(this, {
        items: this.tabs,
        layout: 'fit',
        stateful: true,
        animCollapse: false,
        stateEvents: ['statechange']
    });
    
    this.on('resize', function() { 
        // restore state - after the first time the map is resized
        if (this.state != null) {
            var state = this.state;
            (new Ext.util.DelayedTask(this.restoreState, this, [state])).delay(100);
            this.state = null;
        }
    }, this);
    
    this.tabs.on('remove', function() { this.fireEvent('statechange'); }, this);
    this.tabs.on('tabchange', function() { this.fireEvent('statechange'); }, this);
};

Ext.extend(TNRIS.InfoTabPanel, Ext.Panel, {
    getState: function() {
        var state = TNRIS.InfoTabPanel.superclass.getState.call(this) || {};
        var layers = [];
        this.tabs.items.each(function(item) {
            if (item.layer != null) {
                layers.push(item.layer.layerId());
            }
        }, this);
        if (layers.length > 0) {
            state.layers = layers;
            state.active = this.tabs.getActiveTab().id;
        }
        
        return state;
    },
    
    applyState: function(state, config) {
        if (state) {
            this.state = state;
        }
    },
    
    restoreState: function(state) {
        state.layers && Ext.each(state.layers, function(layerId) {
            var layer = TNRIS.LayerFactory.getLayerById(layerId);
            layer && this.addLayerToGrid(layer);
        }, this);
    },
    
    /*Locations search*/
    
    onSearch: function(btn, e) {
        var searchTerm = this.locationSearch.getValue();
        if (searchTerm.length == 0) {
            Ext.Msg.alert("Problem", "Please enter a location to find in the search box");
            return;
        }
        this.locationSearchTab.el.mask("Searching...", 'x-mask-loading');
        this.mapPanel.getMap().Find(null, searchTerm, null, null, 0, 1, false, false, true, true, this.onSearchResults.bind(this));
    },
    
    onSearchResults: function(shapeLayer, findResults, places, hasMore, error) {
        this.locationSearchTab.el.unmask();
        if (error != null) {
            Ext.Msg.alert("Error", "There was a problem finding your location through Virtual Earth. " + error);
            return;
        }
        this.mapPanel.addLocation(places[0]);
        this.locationSearch.setValue('');
    },
    
    onClick: function(dv, index, node, e) {
        var record = dv.getRecord(node);
        if (Ext.fly(e.target).hasClass('removeButton')) {
            this.mapPanel.removeLocation(record);
        } else {
            this.mapPanel.selectLocation(record);
            var location = record.get('LatLong');
            this.mapPanel.getMap().SetCenter(location);
        }
    },
    
    /* Directions search */
    onDirectionSearch: function(btn,e) {
        var searchFrom = this.fromDirectionSearch.getValue();
        var searchTo = this.toDirectionSearch.getValue();
        
        if (searchFrom.length == 0 || searchTo.length == 0 )  {
            Ext.Msg.alert("Problem", "Please enter 'From' and 'To' Address to find a direction");
            return;
        }
        
        this.searchDirection(searchFrom.toUpperCase(), searchTo.toUpperCase());
    },
    
    getRouteOptions: function(){
        var options = new VERouteOptions;

        if (this.routeByTime.getValue()){
            options.RouteOptimize = VERouteOptimize.MinimizeTime; 
        }
        else {
            options.RouteOptimize = VERouteOptimize.MinimizeDistance; 
        }
        
        options.RouteMode = VERouteMode.Driving;
        options.SetBestMapView  = true;
        options.UseTraffic = this.useTraffic.getValue();
        options.DistanceUnit   = VERouteDistanceUnit.Mile;
        options.ShowDisambiguation = true;
        options.ShowErrorMessages=true; 
        return options;
    },

    searchDirection: function(searchFrom, searchTo) {
        this.directionSearchTab.el.mask("Searching...", 'x-mask-loading');
        
        if (null != this.activeDirSearch) {
            Ext.Msg.alert("Busy","Please wait until the system completes your previous direction search request");
            return;
        }
        
        this.activeDirSearch = {};
        this.activeDirSearch.from = searchFrom;
        this.activeDirSearch.to = searchTo;
        
        var options = this.getRouteOptions();
        options.RouteCallback  = this.onSearchDirectionComplete.bind(this);
        this.mapPanel.getMap().GetDirections([searchFrom, searchTo],options);
    },

    onSearchDirectionComplete: function(route) {
        if (this.activeDirSearch) {
            var from = this.activeDirSearch.from;
            var to = this.activeDirSearch.to;

            if (route && route.RouteLegs.length >0)  {
                var distance = route.Distance.toFixed(2);
                var min = (route.Time)/60;
                var hours = Math.floor(min/60);
                min = Math.round(min%60);
                var time = hours + " Hours and" + min + " Min";
                var veItinerary = route.RouteLegs[0].Itinerary;
                var itinerary = "";
                var step = 0;
                Ext.each(veItinerary.Items, function(itmValue){
                    itinerary = itinerary +'<br/><i>' + 'Step ' + step + ':</i> '+ itmValue.Text;
                    step= step+1; } ,this);
                this.mapPanel.addDirection(from,to,distance,time,itinerary);
            }
        }
        this.clearDirectionParameters();
    },
    
    clearDirectionParameters: function() {
        this.fromDirectionSearch.setValue('');
        this.toDirectionSearch.setValue('');
        this.directionSearchTab.el.unmask();
        this.activeDirSearch = null;
    },
    
    onDirectionClick: function(dv,index,node,e){
        var record = dv.getRecord(node);
        if (Ext.fly(e.target).hasClass('removeButton')) {
            this.mapPanel.removeDirection(record);
        } else {
            this.searchDirection(record.get('From'),record.get('To'));
        }
    },    

    addLayerToGrid: function(layer) {
        this.el.mask('loading...');
        layer.on({
            'loadGrid': {
                scope: this,
                fn: function(s, r, o) {
                    this.el.unmask();
                    var grid = new TNRIS.DataPanel({
                        id: 'grid-' + layer.layerId(),
                        layer: layer,
                        mapPanel: this.mapPanel,
                        initialLoadArgs: [s, r, o],
                        title: layer.name(),
                        closable: true,
                        stripeRows: true,
                        enabledHdMenu: false,
                        mapPanel: this.mapPanel
                    });
                    this.tabs.add(grid);
                    this.tabs.activate(grid);
                    this.fireEvent('statechange');
                },
                single: true
            },
            'loadexception': {
                scope: this,
                fn: function() {
                    this.el.unmask();
                    Ext.Msg.alert("Error", "There was a problem reading that layer from the server.  Please try again or contact your administrator");
                },
                single: true
            }
        });
        layer.loadGridData();
    }
    
});


if (typeof(Sys) !== "undefined") { Sys.Application.notifyScriptLoaded(); }

