/*
 * Freebase Layer class extends OpenLayers.Layer.WMS
 * For better(?) descriptions of the first four parameters, see docs for
 * OpenLayers.Layer.WMS
 * Provides query() functionality
 * @param {String} Layer Name
 * @param {String} WMS URL
 * @param {Object} GetMap query string parameters/values
 * @param {Object} Extra layer options
 */
FreebaseLayer = function(name, url, params, options) {
    FreebaseLayer.superclass.constructor.call(this, name, url, params, options);
};
 //   layer = new OpenLayers.Layer.WMS(name, url, params, options);
Ext.extend(FreebaseLayer, OpenLayers.Layer.WMS, {
    legend: null, // Legend is created lazily
    isQueryable: function() {
        if (this.view_name != undefined && this.view_name != "") {
            return true;
        } else {
            return false;
        }
    },
    getDisplayName: function() {
        return this.display_name;
    },
    getViewName: function() {
        return this.view_name;
    },
    getName: function() {
        return this.name;
    },
    /**
     * Return an AutoGridPanel containing the results of the query
     * @param Geometry User selection box
     * @return AutoGridPanel
     */
    query: function(geom) {
        var dataProxy = new Ext.data.HttpProxy({
            url: Drupal.settings.freebase_path + '/assets/php/AjaxRouter.php',
            timeout:20000
        });
        dataProxy.addListener({'loadexception':function(){
            alert('It looks like you\'ve requested a large amount of data.\nPlease try selecting a smaller region or activating fewer layers. ');
            viewport.findById('qresult_panel').collapse();
        }}); 
        // Create an AutoGridPanel containing the query results
        var grid = new Ext.ux.AutoGridPanel({
            title: this.display_name,
            closable: true,
            loadMask: true,
            autoSave: false,
            listeners: {
               'destroy': function() {
                  if (viewport.findById('qresult_panel').items.length == 0) {
                     viewport.findById('qresult_panel').collapse();
                  }
                },
                'rowclick': function(row,i,e){
                    // style for changing the color
                    hiliteStyle = {
                        strokeColor: "#00FF00",
                        strokeWidth: 3,
                        fillOpacity: .4,
                        fillColor: "#00FFFF"
                    };              
                    // the select tool is not created, create it
                    if (selectResults === undefined){
                        selectResults = new OpenLayers.Control.SelectFeature(
                           ScratchLayer.getInstance(),{selectStyle:hiliteStyle});
                    }
                    var f = ScratchLayer.getInstance().features[i];
                    // unselect feature
                    selectResults.unselectAll();
                    // select the row's feature
                    selectResults.select(f);
                }
            },
            store: new Ext.data.Store({
                proxy:dataProxy,
                reader:new Ext.data.JsonReader()
            }),
            tbar:[{
                text:"Download",
                pressed:true,
                icon:Drupal.settings.freebase_path + '/assets/images/icons/folder_go.png',
                iconCls:'nav',
                handler:function(){ 
                    // function to parse store into useful json 
                    // sends the data to the server; returns CSV filename 
                    var d         = grid.store.data.items;
                    var output    = {};
                    output.fields = [];
                    output.data   = [];
                    
                    for (var item in d){
                        if (!d.hasOwnProperty(item)) continue;
                        var curItem = d[item];
                        var temp = [];
                        for (var i = 0; i < (curItem.fields.items.length - 1); i++){
                            var key = curItem.fields.keys[i];
                            if (item == 0){
                                output.fields.push(key);
                            }
                            temp.push(curItem.data[key]);
                        }
                        // add the temp array to the list records
                        output.data.push(temp);
                    }  
                    // stringify to send to server
                    var out = JSON.stringify(output);      

                    // the site html (page-freepbase.tpl.php) contains a form and a hidden
                    // field to use to submit the query data to the server. This was necessary
                    // instead of creating the form/field here because of an IE bug. Modify
                    // the value attribute of the field and submit the form.
                    var inputField = document.getElementById('downloadQueryField');
                    inputField.setAttribute("value", out);
                    document.getElementById('downloadQueryForm').submit();
   
                }
            }]
        });
        // Show a progress bar
        var wait = Ext.MessageBox.wait(
            "The query will return a maximum of 300 results per layer.<br/>If you have selected " +
            "a large area, the query may not return<br/>all of the data requested.",
            "Retrieving Data"); 
        grid.store.load({params: {
            func: 'boxQuery', 
            geom: geom, 
            view: this.view_name, 
            meta: true
        }, callback: function(record, options, success) {
            if (record.length == 0) {
                wait.hide();
                return;
            } else {
                // Hide the geometry text column
                var colModel = grid.getColumnModel();
                var colIndex = colModel.findColumnIndex("geom_text");
                colModel.setHidden(colIndex, true);

                // Highlight the returned geometries
                var geomArray = new Array();
                var i = 0;

                // Collect the geometries
                grid.store.each(
                    function(record, id) {
                        geomArray[i++] = record.data.geom_text; 
                    }
                );
                grid.autoSizeColumns();
                ScratchLayer.getInstance().addGeometries(geomArray);
            }      
            // We're done, remove the progress bar
            wait.hide();
        }
        });
        return grid;
        
    },
    pointQuery: function(geom) {
        qResultWin.setTitle("Query Results for " + this.display_name);
        qResultWin.items.get('queryResultPanel').setVisible(false);
        qResultWin.items.get('queryResultNotFound').setVisible(false);
        qResultWin.items.get('queryResultLoading').setVisible(true);
        qResultWin.show();
        qResultWin.items.get('queryResultPanel').store.load({ params: {
            func: 'pointQuery',
            geom: geom.getCenterGeomText(map),
            view: this.view_name,
            meta: false
        }, callback: function(record, options, success) {
            if (record.length == 0) {
               qResultWin.items.get('queryResultLoading').setVisible(false);
               qResultWin.items.get('queryResultNotFound').setVisible(true);
            } else {
                // Filter the results to remove the geometry text. At the same time,
                // add the geometry to the temp layer.
                qResultWin.items.get('queryResultLoading').setVisible(false);
                qResultWin.items.get('queryResultPanel').setVisible(true);
                qResultWin.items.get('queryResultPanel').store.filterBy(        
                    function(record, id) {
                        if (record.data.field.toLowerCase() == "geom text") {
                            ScratchLayer.getInstance().clear();
                            ScratchLayer.getInstance().addGeometry(record.data.value);
                            return false;
                        } else {
                            return true;
                        }
                    }
                );
            }
        }});
    },
    /**
     * Reset this layer to default styling. Removes all thresholding/coloring
     * changes.
     */
    reset: function() {
      // If this layer doesn't have any styling changes, don't do anything.
      if (this.params.DIRTY === undefined || this.params.DIRTY == false) {
         return;
      }

      Ext.Ajax.request({
         url: Drupal.settings.freebase_alter+'/'+this.name+'/reset'
         ,scope: this
         ,success: function(result, response) {
            this.params.SLD="";
            this.params.DIRTY = false;
            this.params.STYLE_CHG = false;
            // result.responseText contains Json that includes
            // all default styling for the layer -- pass this to the legend
            // in order to reset colors/checkboxes to default state
            this.cleanLegend(result.responseText);            
            this.redraw(true);  
         }
         ,failure: ajaxFailure
         ,method: 'POST'
      });
    },

    /**
     * Apply styling changes to this layer
     * layer.params.DIRTY - set to true when the layer is using a custom SLD.
     * layer.params.STYLE_CHG - this is used by the 'apply' and 'reset all layers' buttons on
     *                          the LegendPanel to determine which layers have local changes and
     *                          should be submitted to the server.
     * DIRTY is used when a custom sytle is being used; STYLE_CHG indicates that the user has
     * made styling changes that haven't been sent to the server.
     */
    applyStyle: function() {

         // If this layer doesn't have any styling changes, don't do anything.
         if (this.params.STYLE_CHG === undefined || this.params.STYLE_CHG == false) {
            return;
         }

         // Send the layer object back to the server via AJAX
         Ext.Ajax.request({
             url: Drupal.settings.freebase_alter+"/"+this.name,
             scope: this,
             success: function(result, request) {
               // On AJAX response, reload this layer
               this.params.SLD = this.params.LAYER_SLD;
               this.params.DIRTY = true;
               this.params.STYLE_CHG = false; // This layer has been submitted to the server
                                              // and is now up to date.
               this.legend.applyButton.disable();
               this.redraw(true);
             },
             failure: ajaxFailure,
             params: {
                 changelayer: '{"type":"user","layer":' + JSON.stringify(this.layer) + '}'
             },
             method: 'POST'
         });
    },

    /**
     * Resets the legend for this layer to it's default state - default colors 
     * and all checkboxes checked
     * Ths json passed into this function contains all default styling for the layer
     */
    cleanLegend: function(theJson) {
        this.legend.clean(theJson); 
    },

    /**
     * Builds a static HTML view of the legend, very similar to what's
     * shown in the Ext Panel, except for Print.
     */
    buildPrintLegend: function() {
      this.getLegend();
      if(this.legend != null) {
        return this.legend.buildPrintView(); 
      }
    },

    /**
     * Retrieve the legend object for this layer
     *
     * @return {Legend} the Legend Object
     */
    getLegend: function() {
        // If a legend exists for this layer, return it, otherwise build one.
        if (this.legend == null) {
            this.legend = new Legend(this);
        }
        return this.legend;
    },
    zoomToLayer: function(){
        //this.extent;
        if (this.maxExtent){
            map.zoomToExtent(this.maxExtent);
        } else {
            map.setCenter(new OpenLayers.LonLat(lon,lat),zoom); // zoom to initial extent (home)
        }
    },
    // Get a menu item for this layer
    getMenuItem: function(handler) {
        var buttonPanel = new Ext.menu.Item({
            text: this.display_name,
            layerObj: this,
            handler: handler
        });
        return buttonPanel;
    }    
});

/**
 * Define a few layer-related functions. These should probably by static methods of the
 * FreebaseLayer class.
 */

/**
 * Retrieve an array of FreebaseLayer objects for visible, queryable layers
 * @return {Array} Array of visible, queryable layers
 */
function getVisibleQueryableLayers() {
    var layers = new Array();
    for (layername in layerArray) {
        var layer = layerArray[layername];
        if (!layerArray.hasOwnProperty(layername)) continue;
        if (layer instanceof FreebaseLayer) {
           if (layer.isQueryable() && layer.getVisibility()) {
              layers.push(layer);
           }
        }
    }
    return layers;
}

/**
 * Get a textual list of queryable for the "No queryable layers" dialog
 * @return {String} list of queryable layers
 */
function getQueryableLayers() {
    var text = "";
    for (layername in layerArray) {
        var layer = layerArray[layername];
        if (!layerArray.hasOwnProperty(layername) || !(layer instanceof FreebaseLayer)) continue;
        if (layer.isQueryable()) {
            text += layer.getDisplayName() + "<br>";
        }
    }
    return text;
}

/**
 *  * Get a non textual list of queryable layers
 *   * @return {Array} array of queryable layers
 *    */
function getQueryableLayerArray() {
    var arr = []
    for (layername in layerArray) {
        var layer = layerArray[layername];
        if (!layerArray.hasOwnProperty(layername) || !(layer instanceof FreebaseLayer)) continue;
        if (layer.isQueryable()) {
            arr.push(layer);
        }
    }
    return arr;
}

/**
 * Given an array of FreebaseLayer objects, return an array of the view names 
 * and layer names for those objects
 * This is used by the query functionality. The result of this function is passed to
 * the server which determines whether data exists for each layer.
 * @param {Array} Array of FreebaseLayer objects
 * @return {Array} Array of objects: [{view:view name, layer:layer name}, ...]
 */
function getLayerNames(layers) {
   var result = new Array();
   for (var i in layers) {
     if (!layers.hasOwnProperty(i) || !(layers[i] instanceof FreebaseLayer)) continue;
     var j = {};
     j.view = layers[i].getViewName();
     j.layer = layers[i].getName();
     result.push(j);
   }
   return result;
}

/**
 * Given an array of layer names, return an array of freebaseLayer objects
 * @param {Array} Array of layer names
 * @return {Array} Array of FreebaseLayer objects
 */
function getLayersByName(views) {
    var result = new Array();
    for (layername in layerArray) {
        var layer = layerArray[layername];
        if (!layerArray.hasOwnProperty(layername) || !(layer instanceof FreebaseLayer)) continue;
        var name = layer.getName();
        for (var i in views) {
            if (!views.hasOwnProperty(i)) continue;
            if (views[i] == name) {
                result.push(layer);
                break;
            }
        } // End for each view name provided by the caller
    } // End for each layer in the application
    return result;
}
