/*
 * Helper functions to create model for layer tree
 */

// TODO: use same dom element get everywhere


var helper = { // pseudo namespace

map: null,

/*-- wms servers and layers --*/

createLayerModel: function(layer, server_name) {
  // create (hopefully) unique layer name from server and layer name
  var unique_name = server_name + ";" + layer.name;

  var layer_model = {
      text: layer.name,
      checked: layer.getVisibility(),
      layerName: unique_name
  };

  // rename layer to unique name
  layer.setName(unique_name);

  return layer_model;
},

createTopicModel: function(topic) {
  var topic_model = {
    text: topic.title,
    checked: true,
    expanded: false,
    children: []
  };

  for (var i=0; i<topic.layers.length; i++) {
    var layer_model = helper.createLayerModel(topic.layers[i], topic.title);
    topic_model.children.push(layer_model);
  };

  return topic_model;
},

addTopic: function(map, model, topic) {
  helper.map.addLayers(topic.layers);
  model.push(helper.createTopicModel(topic));
},

/*-- legend --*/

getLayerURL: function(layer) {
  var url = layer.url;

  // this code fragment based on OpanLayers.Layer.HTTPRequest.getFullRequestString
  var lastServerChar = url.charAt(url.length - 1);
  if ((lastServerChar == "&") || (lastServerChar == "?")) {
    return url;
  }
  else {
    if (url.indexOf('?') == -1) {
      //serverPath has no ? -- add one
      url += '?';
    }
    else {
      //serverPath contains ?, so must already have paramsString at the end
      url += '&';
    }
  }

  return url;
},

// TODO: store request string directly?
// TODO: only show visible layers
addLegends: function(topic) {
  for(var j=0; j<topic.layers.length; j++) {
    var layer = topic.layers[j];
    var legend_url = helper.getLayerURL(layer) + "VERSION=" + layer.params.VERSION + "&SERVICE=WMS&REQUEST=GetLegendGraphic&LAYER=" + layer.params.LAYERS + "&FORMAT=image/png";
    Ext.get('legend').dom.innerHTML += "<img src='" + legend_url + "' title='" + topic.title + " : " + layer.name + "'><br>";
  }
},

/*-- zoom to scale --*/

// TODO: limit input to int
// TODO: works better with fractionalZoom
zoomToScale: function() {
  var element = document.getElementById('scaleInput');
  var scale = element.value;

  helper.map.zoomToScale(1.0/scale);
},

updateScale: function() {
  var element = document.getElementById('scaleInput');
  element.value = Math.round(helper.map.getScale());
},

/*-- query --*/

mouse_loc: null,
popup: null,
layer_tree: null,
query_list: [],
query_result: null,

queryCurrentLayer: function(ev) {
//orig  if(!helper.info_button.pressed) {
  if(!helper.info_button.pressed || helper.info_button.pressed) {
    return;
  }

  var selected_node = helper.layer_tree.getSelectionModel().getSelectedNode();
  if (selected_node != null && selected_node.attributes.layerNames) {
    // get selected layer
    var selected_layer_name = selected_node.attributes.layerNames[0];
    var selected_layers = helper.map.getLayersByName(selected_layer_name);
    if(selected_layers.length > 0) {
      // query selected layer
      helper.mouse_loc = ev.xy;
      var layer = selected_layers[0];
      if(!layer.isBaseLayer) {
        var query_url = helper.getLayerURL(layer) + "SERVICE=WMS&REQUEST=GetFeatureInfo"
          + "&VERSION=" + layer.params.VERSION
          + "&SRS=" + helper.map.getProjection()
          + "&BBOX=" + layer.map.getExtent().toBBOX()
          + "&X=" + ev.xy.x + "&Y=" + ev.xy.y
          + "&INFO_FORMAT=text/html"
          + "&LAYERS=" + layer.params.LAYERS
          + "&QUERY_LAYERS=" + layer.params.LAYERS
          + "&WIDTH=" + layer.map.size.w + "&HEIGHT=" + layer.map.size.h;
        OpenLayers.loadURL(query_url, '', this, helper.showQueryResult);
      }
      // TODO: query base layers?
    }
  }
  OpenLayers.Event.stop(ev);
},

showQueryResult: function(response) {
  // TODO: only show valid responses
  if(response.responseText.indexOf('<') == 0) {
    helper.showPopup(response.responseText);
  }
  else {
    // do nothing
  }
},

addQueries: function(queries) {
  helper.query_list.push(queries);
},

queryLayers: function(ev) {
//orig  if(!helper.info_button.pressed) {
  if(!helper.info_button.pressed || helper.info_button.pressed) {
    return;
  }
//  searcher.activate();

  helper.mouse_loc = ev.xy;
  // reset query result
  helper.query_result = "";
  
// Info von KML
/*
selectControl = new OpenLayers.Control.SelectFeature(map.layers[1], 
                {hover: testEvent, onSelect: testEvent}); 
  
map.addControl(selectControl); 
selectControl.activate(); 
*/

  // query all layers matching the query pos and scale
  var queryPos = helper.map.getLonLatFromPixel(ev.xy);
  var queryScale = helper.map.getScale();
  for(var i=0; i<helper.query_list.length; i++) {
    var query_layers = helper.query_list[i].layers;
    for(var j=0; j<query_layers.length; j++) {
      var layer = query_layers[j];

      if (layer.minScale != null && layer.minScale < queryScale) {
        // current scale does not match, skip layer
        continue;
      }

      if (layer.maxExtent != null && !layer.maxExtent.containsLonLat(queryPos)) {
        // query pos outside layer extents, skip layer
        continue;
      }

      var query_url = helper.getLayerURL(layer) + "SERVICE=WMS&REQUEST=GetFeatureInfo"
        + "&VERSION=" + layer.params.VERSION
        + "&SRS=" + helper.map.getProjection()
        + "&BBOX=" + helper.map.getExtent().toBBOX()
        + "&X=" + ev.xy.x + "&Y=" + ev.xy.y
        + "&INFO_FORMAT=text/html"
        + "&LAYERS=" + layer.params.LAYERS
        + "&QUERY_LAYERS=" + layer.params.LAYERS
        + "&WIDTH=" + helper.map.size.w + "&HEIGHT=" + helper.map.size.h;
      OpenLayers.loadURL(query_url, '', this, helper.addQueryResult);
    }
  }

  helper.showPopup(helper.query_result);
},

addQueryResult: function(response) {
  if(response.responseText.indexOf('<') != -1) {
    helper.query_result += response.responseText;

    helper.showPopup(helper.query_result);
  }
},

showPopup: function(text) {
  if (text == "") {
    return;
  }

  if(helper.popup != null) {
    helper.popup.destroy();
    helper.popup = null;
  }

  helper.popup = new OpenLayers.Popup.FramedCloud('popup',
    helper.map.getLonLatFromPixel(helper.mouse_loc),
    new OpenLayers.Size(210, 180),
    text,
    null,
    true,
    null
  );
  helper.popup.autoSize = true;
  helper.popup.overFlow = true;
  helper.map.addPopup(helper.popup);
},

/*-- measurement --*/

createMeasureControls: function(map, toolbar) {
  // style the sketch
  var sketchSymbolizers = {
    "Point": {
      pointRadius: 4,
      graphicName: "square",
      fillColor: "white",
      fillOpacity: 1,
      strokeWidth: 1,
      strokeOpacity: 1,
      strokeColor: "#a00000"
    },
    "Line": {
      strokeWidth: 3,
      strokeOpacity: 1,
      strokeColor: "#ff0000",
      strokeDashstyle: "dash"
    },
    "Polygon": {
      strokeWidth: 2,
      strokeOpacity: 1,
      strokeColor: "#ff0000",
      fillColor: "white",
      fillOpacity: 0.3
    }
  };
  var style = new OpenLayers.Style();
  style.addRules([
    new OpenLayers.Rule({symbolizer: sketchSymbolizers})
  ]);
  var styleMap = new OpenLayers.StyleMap({"default": style});

  var options = {
    handlerOptions: {
      style: "default", // this forces default render intent
      layerOptions: {styleMap: styleMap},
      persist: true
    }
  };

  // create controls and add to toolbar
  var measure_line = new OpenLayers.Control.Measure(
    OpenLayers.Handler.Path, options
  )
  toolbar.addControl(
    measure_line,
    {
      iconCls: 'drawline',
      tooltip: 'Distanzmessung',
      toggleGroup: 'map'
    }
  );

  var measure_poly = new OpenLayers.Control.Measure(
    OpenLayers.Handler.Polygon, options
  )
  toolbar.addControl(
    measure_poly,
    {
      iconCls: 'drawpolygon',
      tooltip: 'Fl&auml;chenmessung',
      toggleGroup: 'map'
    }
  );

  // setup text update handler
  var measureControls = [measure_line, measure_poly];
  for(var i=0; i<measureControls.length; i++) {
    var control = measureControls[i];
    control.events.on({
      "measure": helper.handleMeasurements,
      "measurepartial": helper.handleMeasurements
    });
    map.addControl(control);
  }
},

toRadian: function(angle) {
  return angle*Math.PI/180.0;
},

target_proj: new OpenLayers.Projection("EPSG:4326"),
transformToLatLon: function(point) {
  var new_point = new OpenLayers.Geometry.Point(point.x, point.y);
  OpenLayers.Projection.transform(new_point, helper.map.projection, helper.target_proj);
  return new_point;
},

calcVincenty: function(geometry) {
  var dist = 0;
  for (var i = 1; i < geometry.components.length; i++) {
    // transform to EPSG:4326
    var first_wgs = helper.transformToLatLon(geometry.components[i-1]);
    var second_wgs = helper.transformToLatLon(geometry.components[i]);

    dist += OpenLayers.Util.distVincenty(
      {lon: first_wgs.x, lat: first_wgs.y},
      {lon: second_wgs.x, lat: second_wgs.y}
    );
  }
  return dist * 1000.0; // in [m]
},

// this formula is based on the approximation described in 
// 'Some Algorithms for Polygons on a Sphere', http://trs-new.jpl.nasa.gov/dspace/bitstream/2014/40409/1/07-03.pdf
approxPolyAreaOnSphere: function(geometry) {
  var area = 0.0;

  if(geometry.components && (geometry.components.length > 0)) {
    var linear_ring = geometry.components[0];
    if(linear_ring.components && (linear_ring.components.length > 2)) {
      // calculate area of polygon on sphere
      var factor = 6378137.0*6378137.0/2.0;
      for(var i=0; i<linear_ring.components.length-1; i++) {
        // transform to EPSG:4326
        var first_wgs = helper.transformToLatLon(linear_ring.components[i]);
        var second_wgs = helper.transformToLatLon(linear_ring.components[i+1]);

        area += helper.toRadian(second_wgs.x - first_wgs.x) * (2 + Math.sin(helper.toRadian(first_wgs.y)) + Math.sin(helper.toRadian(second_wgs.y)));
      }
      area = Math.abs(area * factor);
    }
  }

  return area;
},

handleMeasurements: function(event) {
  var geometry = event.geometry;
  var order = event.order;
  var element = document.getElementById('measurementText');
  var out = "";
  if(order == 1) {
    var length = helper.calcVincenty(geometry);
    var units = " m";
    if(length > 1000.0) {
      length /= 1000;
      units = " km";
    }
    out += length.toFixed(3) + units;
  } else {
    var area = helper.approxPolyAreaOnSphere(geometry);
    var units = " m";
    if(area > 1000000.0) {
      area /= 1000000;
      units = " km";
    }
    out += area.toFixed(3) + units + "<sup>2</sup>";
  }
  element.innerHTML = out;
},

/*-- toolbar --*/

info_button: null,

addSeparator: function(toolbar){
  toolbar.add(new Ext.Toolbar.Spacer());
  toolbar.add(new Ext.Toolbar.Separator());
  toolbar.add(new Ext.Toolbar.Spacer());
},

initToolbarContent: function(map, toolbar) {
  // navigation

  toolbar.addControl(
    new OpenLayers.Control.ZoomIn({
      title: 'Vergr&ouml;ssern'
    }),{
      iconCls: 'zoomin',
      toggleGroup: 'map'
    }
  );

  toolbar.addControl(
    new OpenLayers.Control.ZoomOut({
      title: 'Verkleinern'
    }),{
      iconCls: 'zoomout',
      toggleGroup: 'map'
    }
  );

  toolbar.addControl(
    new OpenLayers.Control.DragPan({
      title: 'Verschieben'
    }),{
      iconCls: 'pan',
      toggleGroup: 'map'
    }
  );

  toolbar.addControl(
    new OpenLayers.Control.ZoomBox({
      isDefault: true,
      title: 'Vergr&ouml;ssern auf Rechteck'
    }),{
      iconCls: 'zoombox',
      toggleGroup: 'map'
    }
  );

  toolbar.addControl(
    new OpenLayers.Control.ZoomToMaxExtent({
      map: map,
      title: 'Zoom auf gesamte Kartenausdehnung'
    }),{
      iconCls: 'zoomfull',
      toggleGroup: 'map'
    }
  );

  toolbar.addText("1:");
  toolbar.addElement('scaleForm');

  helper.addSeparator(toolbar);

  // info

  // workaround: use another DragPan, because toggle group does not work with normal Ext.Toolbar.Button
  helper.info_button = toolbar.addControl(
    new OpenLayers.Control.DragPan({
      title: 'Info'
    }),{
      iconCls: 'info',
      toggleGroup: 'map'
    }
  );

  helper.addSeparator(toolbar);

  // history

  var nav_hist = new OpenLayers.Control.NavigationHistory();
  map.addControl(nav_hist);
  nav_hist.activate();

  toolbar.add(
    new Ext.Toolbar.Button({
      iconCls: 'back',
      tooltip: 'Vorherige Sicht',
      handler: nav_hist.previous.trigger
    })
  );

  toolbar.add(
    new Ext.Toolbar.Button({
      iconCls: 'next',
      tooltip: 'N&auml;chste Sicht',
      handler: nav_hist.next.trigger
    })
  );

  helper.addSeparator(toolbar);

  // measurements

  helper.createMeasureControls(map, toolbar);

  toolbar.add(new Ext.Toolbar.Spacer());

  toolbar.addElement('measurementText');

  helper.addSeparator(toolbar);

  // print

  toolbar.add(
    new Ext.Toolbar.Button({
      iconCls: 'print',
      tooltip: 'Drucken',
      handler: helper.openPrintWindow
    })
  );

  helper.addSeparator(toolbar);

  // mouse coordinates
  toolbar.addElement('mousePos');

  // help

  toolbar.add(new Ext.Toolbar.Fill());

  toolbar.add(
    new Ext.Toolbar.Button({
      iconCls: 'help',
      tooltip: 'Hilfe',
      handler: helper.openHelpWindow
    })
  );

  toolbar.activate();
},

/*-- print --*/

print_window: null,

openPrintWindow: function() {
  helper.print_window = window.open('print_preview.html', 'Print_preview', 'width=970,height=660,toolbar=0,menubar=1,location=0,personalbar=0,navigation=0,resizable=0');
},

// called by print window after it has finished loading
printWindowReadyCallback: function() {
  // setup map
  helper.print_window.applyMapSettings(helper.map);

  // add legend
  var legend = helper.print_window.document.getElementById('legend');
  legend.innerHTML = document.getElementById('legend').innerHTML;
  helper.print_window.document.close();

  helper.print_window.focus();
},

/*-- help --*/

openHelpWindow: function() {
  window.open('help/geodp_help.pdf', 'help', '');
}

}; // namespace helper
