//GLOBAL VARS
var map;
var layerArray = [];
var baseLayers = [];
var tree,layerloader,selectResults;
var viewport,gbase,gsat,ghybrid;

var lon     = -95;
var lat     = 40;
var zoom    = 4;
var latlon = new OpenLayers.LonLat(lon, lat);

OpenLayers.IMAGE_RELOAD_ATTEMPTS = 6;
OpenLayers.Util.ImageLoadErrorColor = "transparent";

/*
* Main Open Layers function...sets up map env
*/
function init(){

    Ext.BLANK_IMAGE_URL = Drupal.settings.freebase_path + '/assets/images/s.gif';
   
    var options = {
        numZoomLevels: 19,
        projection: new OpenLayers.Projection("EPSG:900913"),
        units: "m",
        maxResolution: 156543.0339,
        maxExtent: new OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508.34),
        displayProjection: new OpenLayers.Projection("EPSG:4326"),
     	controls: [
            new OpenLayers.Control.Navigation(),
            new OpenLayers.Control.PanZoomBar(),
            new OpenLayers.Control.MousePosition(),
            new OpenLayers.Control.ScaleLine({
               topOutUnits: 'mi'
               ,topInUnits: 'ft'
               ,bottomOutUnits: 'km'
               ,bottomInUnits: 'm'
            })
    	]
    }

    map = new OpenLayers.Map('map',options);

    gbase = new OpenLayers.Layer.Google("Google Basemap",{
        type: G_NORMAL_MAP,
        visibility:true,
        'sphericalMercator':true
    });

    gsat = new OpenLayers.Layer.Google("Google Satellite",
    {
        type: G_SATELLITE_MAP,
        'sphericalMercator':true
    });

    ghybrid = new OpenLayers.Layer.Google("Google Hybrid",
    {
        type: G_HYBRID_MAP,
        'sphericalMercator':true
    });
    
    layerArray['gbase']   = gbase;
    layerArray['gsat']    = gsat;
    layerArray['ghybrid'] = ghybrid;
    map.addLayers([gbase,gsat,ghybrid]);

    // Add a layer for the 'find location' markers
    var markers = new OpenLayers.Layer.Markers("Markers");
    layerArray['markers'] = markers;
    map.addLayer(markers);
    markers.setZIndex(100001); // one above scratchlayer
     
    // Add toolbar controls to map
    for (control in tbar_items) {
       map.addControl(tbar_items[control]);
    }
    
    latlon.transform(map.displayProjection, map.getProjectionObject());
    map.setCenter(latlon, zoom);
}



/**
   Main Ext functions - executes on page load
 */
Ext.onReady(function(){

    
    Ext.BLANK_IMAGE_URL = Drupal.settings.freebase_path + '/assets/images/s.gif';
 
    /**
        Call the OpenLayers init functions

        sets up map and adds other controls
        must be called before the layer tree
     */
    init();


    /**
        EXT JS Tree Panel Functions
     */
    layerloader = new Ext.tree.TreeLoader({
       dataUrl:Drupal.settings.freebase_json
    });

    tree = new Ext.tree.TreePanel({
        border:false,
        animate:true,
        loader: layerloader,
        enableDD:true,
        listeners:{
            'beforenodedrop': function(dropEvent) {
                // Folders can be dragged anywhere
                if(dropEvent.data.node.attributes.cls == 'folder') {
                  return true;
                }

                // Leaf's can only be dragged within their folder
                source_folder_id = dropEvent.data.node.parentNode.id;
                if(dropEvent.target.attributes.cls == 'folder') {
                  dest_folder_id = dropEvent.target.id;
                }
                else {
                  dest_folder_id = dropEvent.target.parentNode.id;
                }

                if(source_folder_id != dest_folder_id) {
                  return false;
                }
                else {
                  return true;
                }
            },
            'enddrag': function(tree,node,dragEvent) {
                var root = tree.getRootNode();
                root.eachChild(treeWalkAndReorder);
            },
            'checkchange': function(node){
                /** toggle layer visibility */
                var lyr = layerArray[node.id];
                lyr.getVisibility() ? lyr.setVisibility(false) : lyr.setVisibility(true);
                enablePrint();
                //updateActiveWin(); // not used right now
            },
            /** view layer controls */
            'contextmenu': function(node){
                        node.select();
                        lyr = node.id;
                        // if not a folder: show menu
                        if(node.attributes.cls != 'folder'){
                             var menuC = new Ext.menu.Menu({
                                 id:'main-menu'
                                 ,allowOtherMenus: true
                                 ,listeners: {
                                    // When the user dismesses the menu, reset unapplied 
                                    // style changes
                                    hide: function() {
                                       layerArray[lyr].getLegend().synchronize();
                                    }
                                 }
                             });
                             //menuC.add({text: 'Zoom to Layer',handler:function(){layerArray[lyr].zoomToLayer()}});
                             menuC.add({text: 'View Legend',menu:[new Ext.menu.LegendItem({
                                 bodyStyle:'padding:5px;',
                                 autoWidth:true,
                                 autoHeight:true,
                                 title:'Legend',
                                 closable:true,
                                 items: layerArray[lyr].getLegend().getElement()
                             })]});              
                             menuC.add({text: 'Adjust Transparency',menu:[new Ext.menu.LegendItem({
                                 title:'Transparency',
                                 width:185,
                                 height:50,
                                 items: new Ext.Slider({
                                    width:185,
                                    height: 25,
                                    vertical:false,
                                    value:layerArray[lyr].opacity*100,
                                    minValue: 0,
                                    maxValue: 100,
                                    overCls: 'menu-over',
                                    listeners:{ drag: function(){ layerArray[lyr].setOpacity(this.getValue()/100);} }
                                })
                             })]});
                             if (layerArray[lyr].local_options !== undefined){
                                 var metadata = layerArray[lyr].local_options.metadata;
                                 if (metadata != "" && metadata !== undefined) {
                                    menuC.add({text: 'Metadata',menu:[new Ext.menu.LegendItem({
                                       title:'Metadata',
                                       //width:'400',
                                       bodyStyle:'padding:5px;',
                                       items:[{ html:metadata, border:false }]
                                    })]});
                                 }   
                             }   
                             menuC.show(node.ui.getAnchor());

                     // This lets us get a handle to the context menu using Ext.getCmp()
                     // in Legend.js without passing a reference to the menu through
                     // freebase layer. See the bug report in tracker called 'Memory Leak'
                     Ext.ComponentMgr.register(menuC);
                }
            },
            /** loads layers, adds them to the map */
            'load' : function(node){
                        if(node.attributes.cls == 'folder'){
                           for (var i = 0; i < node.childNodes.length; i++){
                                if (node.childNodes[i].attributes.cls != 'folder'){
                                    var id          = node.childNodes[i].attributes.id;
                                    var wmslink     = node.childNodes[i].attributes.wmslink;
                                    var server_sld  = node.childNodes[i].attributes.server_sld;
                                    var vis         = node.childNodes[i].attributes.checked;
                                    var lyr_opacity = node.childNodes[i].attributes.opacity;
                                    var lyr_sld     = node.childNodes[i].attributes.sld;
                                    var lyr_dirty   = node.childNodes[i].attributes.dirty;
                                    var view_name   = node.childNodes[i].attributes.view_name;
                                    var text        = node.childNodes[i].attributes.text;
                                    var options     = node.childNodes[i].attributes.options;
                                    var layer       = node.childNodes[i].attributes.layer;
                                    var zindex      = node.childNodes[i].attributes.zindex;
                                    loadGISLayer(id,wmslink,vis,lyr_opacity,lyr_sld, lyr_dirty, view_name, text, layer, options, zindex, server_sld);
                                }
                           }
                        }
            }
        }
    });

    var root = new Ext.tree.AsyncTreeNode({
          text:'GIS Layers'
        })
    tree.setRootNode(root);
    root.expand();
    
    /** routine to expand and collapse folders
    * this forces each layer to be loaded 
    * layers on high in the tree draw on bottom
    */
    setTimeout(function(){
        var treeFolders = tree.root.childNodes;
        for (var i = 0; i < treeFolders.length; i++){
            // if the layer is not expanded, then expand it
            if (!treeFolders[i].expanded){
                treeFolders[i].expand();
                treeFolders[i].collapse();
            }
            // for each child - look for children
            for (var j = 0; j < treeFolders[i].childNodes.length; j++){
                // check if child is a subfolder
                if (treeFolders[i].childNodes[j].attributes.cls == "folder"){
                    if (!treeFolders[i].childNodes[j].expanded){
                        treeFolders[i].childNodes[j].expand();
                        treeFolders[i].childNodes[j].collapse();
                    }
                }
            }
        }
        //build the active layer controller
        //buildActiveLayerWin();
    },3000);

    /**
       Hide the loading mask after a certain amount of time
     */
    setTimeout(function(){
        Ext.get('loading').remove();
        Ext.get('loading-mask').fadeOut({remove:true});
    },2000);

    /**
     page layout, controls positions of EXT panels
    */
    viewport = new Ext.Viewport({
        layout: 'border',
        items: [
            new Ext.BoxComponent({ // raw
                region:'north',
                el: 'north',
                height:5
            }),
                new Ext.TabPanel ({
                id: 'qresult_panel',
                region:'south',
                contentEl: 'south',
                split:true,
                height: 200,
                minSize: 100,
                maxSize: 400,
                collapsible: true,
                collapsed: true,
                title:'Query Results',
                enableTabScroll: true,
                margins:'0 0 0 0'
            }),{
                region:'west',
                title:'&nbsp',
                split:true,
                width: 250,
                minSize: 175,
                maxSize: 400,
                collapsible: true,
                layout: 'border',
                margins:'0 0 0 5',
                items:[
                  new Ext.Panel({
  		    contentEl: "logo"
	 	    ,region: 'north'
		    ,border: false
	          })
		  ,new Ext.TabPanel({
                    border:false,
		    region: 'center',
                    activeTab:0,
                       items:[{
                          title: 'Layers',
                          items: [tree],
                          autoScroll: true
                       },{
                          title:'Legend',
                          layout:'fit',
                          border:true,
                          autoScroll:true,
                          height:300,
                          items: legendPanel,
                          listeners:{
                             'activate':function() { legendPanel.activatePanel(); },
                             'deactivate':function() { legendPanel.deactivatePanel(); }
                          }
                       }
                    ]})
	         ]
               },{
                region:'center',
                id:'center',
                contentEl: 'map',
                collapsible: false,
                margins:'0 5 0 0',
                listeners: {
                    'resize': function(thisObj, adjWidth, adjHeight, rawWidth, rawHeight) {
                        // This lets openlayers know that the map window has been resized.
                        if (map) {
                            map.setCenter(map.getCenter());
                        }
                    }
                },
                tbar: getToolbar() 
            }]
      });

   
      // adds a toolbar to the right hand side of the map window
      viewport.findById('center').getTopToolbar().add('->',
       {
             text: 'Baselayers',
             pressed:false,
             menu:baseMenu,
             handler: function(){
             }
       });


      // If the doHelp function is defined, enable the help button
      if (typeof doHelp != "undefined") {
         Ext.getCmp("helpButton").enable();
      }

      // Build the query result window. Needs to happen OnReady
      buildQResultWin();


});

var zindexInitial = 10000;
function treeWalkAndReorder(node) {
  // Build the layer id & zindex list
  if(node.attributes.cls != 'folder') {
    layerArray[node.id].setZIndex(zindexInitial);
    zindexInitial -= 10;
    //console.log('Set ID: '+id+' to Zindex: '+zindexInitial);
  }

  node.eachChild(treeWalkAndReorder);
}


/**
     Loads each layer into the map
     Uses data from the tree loader json
 */
function loadGISLayer(id,wmslink,vis,lyr_opacity,lyr_sld, lyr_dirty, view_name, text, layer, options, zindex, server_sld){
    if (vis == true){ Ext.getCmp('printButton').enable(); } 
    if (layer !== undefined){
        var opts = eval('('+ layer.options +')');
    }
    layerArray[id] = new FreebaseLayer(id,wmslink, {
        layers: id, 
        transparent:'true',
        sld: (lyr_dirty == true?lyr_sld:""),
        dirty: lyr_dirty,
        layer_sld: lyr_sld,
        server_sld: server_sld,
        format:'image/gif' 
    },{
        visibility: vis, 
        opacity: lyr_opacity, 
        view_name: view_name,
        display_name: text, 
        layer: layer,
        local_options: opts
    });
    registerEvents(layerArray[id]);
    layerArray[id].setZIndex(zindex);

     /*
     // Temporary hack to force states, cities, and highways to the top of the layer stack
     if (id == "us_states" ||
         id == "us_cities" ||
         id == "interstates" ||
         id == "highways" ||
         id == "H_productionfacilities" ||
         id == "h2fueling_stations") {
            layerArray[id].setZIndex(zindex);
            zindex += 5;
     }
     */
}




/**
 * Legend menu item: extends the rest
 */
Ext.menu.LegendItem = function(config){
    Ext.menu.LegendItem.superclass.constructor.call(this, new Ext.Panel(config), config);
};
Ext.extend(Ext.menu.LegendItem, Ext.menu.Adapter, {});


/**
* Function: registerEvents
*   add loading events to a layer that modify the layer tree icon 
*   this tells whether or not the layer is still loading
* Parameter:
*   layer - the layer to which we add events
*/

function registerEvents(layer) {
    var treeNode;
    layer.logEvent = function(event) {
        //console.log( this.name + ": " + event);
    }

    layer.events.register("loadstart", layer, function() {
        treeNode = tree.getNodeById(this.name);
        treeNode.attributes['icon_orig'] = treeNode.attributes.icon;
        treeNode.attributes.icon = Drupal.settings.freebase_path + "/assets/images/ajax-loader.gif";
        var nodeUI = treeNode.getUI();
        var iconEl = nodeUI.getIconEl();
        if (iconEl !== undefined){
            iconEl.src = treeNode.attributes.icon;
        }
        this.logEvent("Load Start");
        layer.getLegend().setLoadMask();
    });

    layer.events.register("loadend", layer, function() {
        treeNode = tree.getNodeById(this.name);
        var nodeUI = treeNode.getUI();
        var iconEl = nodeUI.getIconEl(); 

        // TODO: need to revisit this
        //       right now, we are supporting a legend icon within the layer
        //       tree so when user changes color dynamically, need to update
        //       icon in layer tree -- this poses a challenge when there are 
        //       multiple legend classes in the layer -- simply draw the icon 
        //       with color from the first class?
        //       Also, currently coded to only apply to lines and points where 
        //       there is no external graphic url 
        
        // use last symbolizer for first legend class in layer
        var symbolizer = (layer.layer.rules[0].symbolizers[layer.layer.rules[0].symbolizers.length - 1]);
        if ((symbolizer.type).toLowerCase() == 'line' || ((symbolizer.type).toLowerCase() == 'point' && symbolizer.graphic.external_graphic_url == undefined)) {
            var theIcon = getLayerTreeIcon(symbolizer);
            iconEl.src = theIcon
            treeNode.attributes.icon = theIcon;
        }
        else {
            iconEl.src = treeNode.attributes.icon_orig;
            treeNode.attributes.icon = treeNode.attributes.icon_orig;
        }
                
        this.logEvent("Load End. Grid:" + this.grid.length + "x" + this.grid[0].length);
        layer.getLegend().clearLoadMask();
        
    });

    map.addLayer(layer);
}


/**
* Function: getLayerTreeIcon
*   gets icon for placement in layer tree
*   currently coded to only apply to lines and points where there is no 
*   external graphic url
* Parameter:
*   symbolizer - the symbolizer from which to create the icon
*/
function getLayerTreeIcon(symbolizer) {
    var theLayerTreeIcon;
    switch ((symbolizer.type).toLowerCase()) {
        case "point":
            theLayerTreeIcon = Drupal.settings.freebase_path + "/assets/php/icon.php?" +
			       "shape=" + symbolizer.graphic.graphic_mark.toLowerCase()+
			       "&s=8&w=9&h=14&hc=" + symbolizer.graphic.fill;
            break;
           
         case "line":
             theLayerTreeIcon = Drupal.settings.freebase_path + "/assets/php/icon.php?" +
                   "shape=line&s=8&w=9&h=14&hc=" + symbolizer.strokes[0].color;
             break;
             
         default:
		     alert("Unsupported Symbolizer type " + symbolizer.type + " encountered (fb.js)");
		     break;
    }
    return theLayerTreeIcon;      
}

var printBox;
/**
 * Print the map by sending the innerHTML of the map div to the server, which builds a
 * printable view.
 */
function printMap() {
   var legend = '<table border=0><tr>';
   vizLayers = tree.getChecked('id');
   for (var i = 0; i < vizLayers.length; i++) {
        
       if (i % 3 == 0 && i > 0) {   // 3 legends per row for printing
         legend += "</tr><tr>";
       }
       var layer = layerArray[vizLayers[i]];

       // For IE, set the layer opacity to 1 (IE can't print transparent images)
       if (Ext.isIE) {
          layer.origOpacity = layer.opacity;
          layer.setOpacity(1);
       }

       var nodePath = tree.getNodeById(layer.name).getPath().split("/");
       var legendDescr = "<span class='legend-descr'>";
       for (var j = 3; j < nodePath.length-1; j++) {
         legendDescr += tree.getNodeById(nodePath[j]).text + "<br/>";
       }
       legendDescr += layer.display_name;
       if (layer.local_options !== undefined && 
           layer.local_options.units !== undefined && 
           layer.local_options.units != "") {
          legendDescr += " (" + layer.local_options.units + ")</span>";
       } 
       legend += "<td valign='top'><fieldset><legend class='legend-title'>" + 
                  tree.getNodeById(nodePath[2]).text + "</legend>" + 
                  legendDescr +
                  layer.buildPrintLegend() + "</fieldset></td>"; 
    }

   legend += '</tr></table>';

    Ext.Ajax.request({
        url:Drupal.settings.freebase_path + '/assets/php/print.php',
        success: printDone,
        timeout:60000,
        failure:function(){
            alert('A server error occurred while generating the printable page.');
            printBox.hide();
        },
        params: { 
            height: document.getElementById('map').offsetHeight,
            width: document.getElementById('map').offsetWidth,
            map_html: document.getElementById('map').innerHTML, 
            legend: legend,
            javascript: Drupal.settings.freebase_path + '/assets/js/drag.js'
        }
    });

    printBox = Ext.MessageBox.show({
        title: 'Creating map',
        progressText: 'please wait...',
        closable:true,
        modal:false,
        width:400,
        wait:true,
        waitConfig: {interval:200}
    });

    // After we send the AJAX request, reset the layer opacities to their original values (IE)
    if (Ext.isIE) {
       for (var i = 0; i < vizLayers.length; i++) {
          var layer = layerArray[vizLayers[i]];
          opacityChanged = false;

          // reset the opacity and set a flag to tell printDone() to alter the IE user
          if (layer.opacity != layer.origOpacity) {
            layer.setOpacity(layer.origOpacity);
            opacityChanged = true;
          }
      }

   }
}

var opacityChanged = false;

// when the request is ready (the image has been generated) this callback expects the output
// to be the HTML that comprises the printable view.
function printDone(request) 
{
   printBox.hide();
   printwin = window.open(
         '', 'Print', 'width=800, scrollbars=1, menubar=1, toolbar=1, resizable=1');

   printwin.document.write(request.responseText);
   printwin.document.close();

   if (opacityChanged) {
      printwin.alert(
         'Hey! Why did my map change?\n' +
         'Because Internet Explorer is not able to print transparent images,\n' +
         'your layers will print as fully opaque. We apologize for the\n' +
         'inconvenience and suggest the Firefox browser as an alternative.'
      );
   }

}

/*
* Opens Ext dialog to input address  
*/
function openSearch() 
{
    Ext.Msg.prompt('Find a Location', 'Enter any one of: address, city, state, zip, or Lat/Long (decimal degrees)', function(btn, text){
        layerArray['markers'].clearMarkers();
        if(btn == 'ok' && text){
            var place = text;
            search(place);
        } else if(btn == 'cancel'){
        }
    });
}

/*
* Uses google geocoder to get lat lon, sets map center on that point
* @param (address) 
*/
function search(place)
{
    if (place){
       var GeoCoder = new GClientGeocoder();
       GeoCoder.getLocations(place, function(response) {
           if (!response.Placemark) {
               alert(place + " not found");
           } else {
               var accuracy = response.Placemark[0].AddressDetails.Accuracy;
               var point = response.Placemark[0].Point;
               var newPnt = new OpenLayers.LonLat(point.coordinates[0],point.coordinates[1]);
               newPnt.transform(map.displayProjection,map.getProjectionObject());
               map.setCenter(newPnt,accuracy+4);
               var size = new OpenLayers.Size(20,34);
               var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);                
               var icon = new OpenLayers.Icon(Drupal.settings.freebase_path + 
                   '/assets/images/marker.gif',size,offset);
               layerArray['markers'].addMarker(new OpenLayers.Marker(newPnt,icon));
           }
       });
    } else {
       alert("Please Enter either an address, city, state, or Lat/Long");
    }
}

// global measure win variable
var measureWin;

/**
* Opens a window with the measurement_output html div as its content
* the measure toll will display totals in the window
*/
var measureInitialized = false;
function openMeasure(){
   measureWin = MeasureWin.getInstance();
   measureWin.show();
   if (!measureInitialized) {
      tbar_items.measure.events.on({
         "measure": MeasureWin.getInstance().handleEndMeasurements
         ,"measurepartial": MeasureWin.getInstance().handleMeasurements
      });
      measureInitialized = true;
   }
}
