/*
 * 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
  layer.unique_name = server_name + ";" + layer.title;

  var layer_model = {
      text: layer.title,
      checked: layer.wms_options.visibility,
      layerName: layer.unique_name,
      maxScale: layer.wms_options.minScale
  };

  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++) {
    if (topic.layers[i].display_layers == 't001')
    {
      // hide base layer in treeview
      continue;
    }
    var layer_model = helper.createLayerModel(topic.layers[i], topic.title);
    topic_model.children.push(layer_model);
  };

  return topic_model;
},

deepCopyProps: function(props)
{
  var new_props = {};
  for (prop in props)
  {
    new_props[prop] = props[prop];
  }
  return new_props;
},

createVisibleLayer: function(topic_layer)
{
  // wms_params + layers
  var wms_params = helper.deepCopyProps(topic_layer.wms_params);
  wms_params.layers = topic_layer.display_layers;

  // wms_options + extent
  var wms_options = helper.deepCopyProps(topic_layer.wms_options);
  if (topic_layer.extent != null)
  {
    wms_options.maxExtent = topic_layer.extent;
  }

  var layer = new OpenLayers.Layer.WMS.KKGEO(
    topic_layer.unique_name, // use unique name generated in createLayerModel()
    topic_layer.url,
    wms_params,
    wms_options
  );

  helper.map.addLayer(layer);
},

getLayerURL: function(layer_url) {
  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;
},

createQueryLayers: function(topic_layer)
{
  for (var i=0; i<topic_layer.query_layers.length; i++)
  {
    // wms_params + query layer
    var wms_params = helper.deepCopyProps(topic_layer.wms_params);
    wms_params.layers = topic_layer.query_layers[i];

    // wms_options + extent
    var wms_options = helper.deepCopyProps(topic_layer.wms_options);
    if (topic_layer.extent != null)
    {
      wms_options.maxExtent = topic_layer.extent;
    }

    var title = "query;" + topic_layer.title + ";" + topic_layer.query_layers[i];

    var layer = new OpenLayers.Layer.WMS.Untiled(
      title,
      topic_layer.url,
      wms_params,
      wms_options
    );

    helper.query_list.push(layer);
  }
},

createLegend: function(topic_layer, topic_title)
{
  for (var i=0; i<topic_layer.legend_layers.length; i++)
  {
    var legend_url = helper.getLayerURL(topic_layer.url) + "VERSION=1.1.1&SERVICE=WMS&REQUEST=GetLegendGraphic&LAYER=" + topic_layer.legend_layers[i] + "&FORMAT=image/png";
    Ext.get('legend').dom.innerHTML += "<img src='" + legend_url + "' title='" + topic_title + " : " + topic_layer.legend_layers[i] + "'><br>";
  }
},

addTopic: function(model, topic) {
  // create model
  model.push(helper.createTopicModel(topic));

  for (var i=0; i<topic.layers.length; i++)
  {
    // create visible layer
    helper.createVisibleLayer(topic.layers[i]);
    // create query layers
    helper.createQueryLayers(topic.layers[i]);
    // create legend layers
    helper.createLegend(topic.layers[i], topic.title);
  }
},

/*-- 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,

queryLayers: function(ev) {
  if(!helper.info_button.pressed) {
    return;
  }

  helper.mouse_loc = ev.xy;
  // reset query result
  helper.query_result = "";

  // 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 layer = helper.query_list[i];

    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 srs = layer.params.SRS;
    if (srs == null) {
      srs = helper.map.getProjection();
    }

    var query_url = helper.getLayerURL(layer.url) + "SERVICE=WMS&REQUEST=GetFeatureInfo"
      + "&VERSION=" + layer.params.VERSION
      + "&SRS=" + srs
      + "&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: 'btnInfo',
      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/kkgeo_help.pdf', 'help', '');
}

}; // namespace helper

