/*
 * Copyright (c) 2015-2016 NIBIO <http://www.nibio.no/>.
 *
 * This file is part of VIPSLogic.
 * VIPSLogic is free software: you can redistribute it and/or modify
 * it under the terms of the NIBIO Open Source License as published by
 * NIBIO, either version 1 of the License, or (at your option) any
 * later version.
 *
 * VIPSLogic is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * NIBIO Open Source License for more details.
 *
 * You should have received a copy of the NIBIO Open Source License
 * along with VIPSLogic.  If not, see <http://www.nibio.no/licenses/>.
 *
 */

/**
 * Logic for GIS information about an object
 * 
 * To get the current registered GIS as GEOJson, call getFeatures();
 * 
 * @author Viggo Lunde <viggo.lunde@nibio.no>
 * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
 */

var featureOverlay, map;
var olMapDivId = "objectGISInfoMap";

/**
 * @param {string} containerId - DOM id of the div where we can intialize the map
 * @param {ol.Coordinate} center - coordinates for the map's center (WGS84)
 * @param {int} zoomLevel - the zoom level (1-15, 1 is world wide view, 15 is greatest zoom)
 * @param {boolean} displayMarker - show observation marker in center location
 * @param {string} drawnObjs - GeoJSON with geometries to display
 * @param {string} chooseLayersObj Which polygon layers to choose from. Sample: 
 * <pre>
 *  {
 *      "chooseFromMapLayers":[
 *          {
 *              "mapLayerId":1,
 *              "mapLayerTitle":"Kommunegrenser",
 *              "description":"Norske kommunegrenser",
 *              "mapLayerType":"overlay",
 *              "defaultVisible":false,
 *              "hoverAttribute":"navn",
 *              "url":"http://kart13.skogoglandskap.no/geoserver/ows?srsname=EPSG:3857&format_options=decimals:0&service=WFS&version=1.0.0&outputFormat=json&request=GetFeature&typeName=sl:n2000_komm_flate&"
 *          },
 *          {
 *              "mapLayerId":2,
 *              "mapLayerTitle":"Fylkesgrenser",
 *              "description":"Norske fylkesgrenser",
 *              "mapLayerType":"overlay",
 *              "defaultVisible":false,
 *              "hoverAttribute":null,
 *              "url":"http://kart13.skogoglandskap.no/geoserver/ows?srsname=EPSG:3857&format_options=decimals:0&service=WFS&version=1.0.0&outputFormat=json&request=GetFeature&typeName=sl:n5_forv_fylke_mv&"
 *          }
 *      ]
 *  }
 *  </pre>
 *  
 * @returns {void}
 */
function initMap(containerId, center, zoomLevel, displayMarker, drawnObjs, chooseLayersObj) {

  // Create the DOM structure
  var theContainer = el(containerId);
  theContainer.innerHTML = [
      "<div>",
      "     <label>",getI18nMsg("mapDrawTypeLabel"),"</label>",
      "     <select id='type'>",
      "         <option value='Point'>",getI18nMsg("point"),"</option>",
      "         <option value='Polygon'>",getI18nMsg("polygon"),"</option>",
      "     </select>",
      "     <button id='delete' type='button'>",getI18nMsg("clearAll"),"</button>",
      "     <button id='deleteOne' type='button'>",getI18nMsg("clearOne"),"</button>",
      "</div>",
      "<div id='", olMapDivId ,"' class='map'></div>",
      "<div id='alert_placeholder'></div>"
  ].join("");
  
  

  // Adding standard base layers
  // 
  // TODO: Make this configurable per organization (input base layers as function parameter)

  // OpenStreetMap
  var osm = new ol.layer.Tile({
    'title': 'OSM',
    type: 'base',
    visible: true,
    source: new ol.source.OSM({
      attributions: [
        new ol.Attribution({
          html: mapConstants.MAP_ATTRIBUTION
        })
      ]
    })
  });

  // Satellite photo map from Microsoft/Bing
  /*var bingArial = new ol.layer.Tile({
    title: 'Bing Arial',
    type: 'base',
    visible: false,
    source: new ol.source.BingMaps({
      imagerySet: 'Aerial',
      key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3'
    })
  });*/

  // Detailed map of Norway in shades of grey
  var topo2graatone = new ol.layer.Tile({
    title: "Gråtone",
    type: 'base',
    visible: false,
    source: new ol.source.TileWMS({
      url: "http://opencache.statkart.no/gatekeeper/gk/gk.open?",
      params: {
        LAYERS: 'topo2graatone',
        VERSION: '1.1.1'
      }
    })
  });



  //########################Get maplayers you can choose from########################

  //var layersObj = eval ("(" + chooseLayersObj + ")");
  var allLayers = [osm, topo2graatone] ;
  var chooseLayers = [];
  var choosenLayer;
  var hoverAttribute = '';
  var deleteObj = false;

  var typeSelect = document.getElementById('type');

    if (chooseLayersObj.chooseFromMapLayers.length > 0){
      for (i = 0; i < chooseLayersObj.chooseFromMapLayers.length; i++) { 
        var layer = chooseLayersObj.chooseFromMapLayers[i];
        var opt = document.createElement("option");
        opt.value = layer.mapLayerId;
        opt.text = layer.mapLayerTitle;
        typeSelect.appendChild(opt);
        chooseLayers.push(new ol.layer.Vector({
          id: layer.mapLayerId,
          title: layer.mapLayerTitle,
          type: layer.mapLayerType,
          visible: layer.defaultVisible, 
          hoverAttribute:layer.hoverAttribute,          
          source: new ol.source.Vector({
            projection: layer.projection,
            format: new ol.format.GeoJSON(),
            url: layer.url
          })
        }));
      }
    }

    allLayers.push.apply(allLayers, chooseLayers);


window.app = {};
var app = window.app;


//
// Define rotate to north control.
//



/**
 * @constructor
 * @extends {ol.control.Control}
 * @param {Object=} opt_options Control options.
 */
app.geoLocationControl = function(opt_options) {

  var options = opt_options || {};

  var button = document.createElement('button');
  //button.innerHTML = 'O';
  button.title = getI18nMsg("showMeWhereIAm");

  var this_ = this;

  var handleGeoLocation = function(e) {
    /*if (geolocation.getTracking()){
      geolocation.setTracking(false);
      geoOverlay.getSource().clear(true);
    } else {*/
      map.removeInteraction(draw);
      removeMouseOver();
      $('#deleteOne').removeClass('clearOneActive');
      deleteObj = false;
      geolocation.setTracking(true);
    //}
  };

  button.addEventListener('click', handleGeoLocation, false);
  button.addEventListener('touchstart', handleGeoLocation, false);

  var element = document.createElement('div');
  element.className = 'ol-unselectable ol-control geo-location';
  element.appendChild(button);

  ol.control.Control.call(this, {
    element: element,
    target: options.target
  });

};
ol.inherits(app.geoLocationControl, ol.control.Control);

 

  // Creating the map
  map = new ol.Map({
    target: olMapDivId, // Defined at the top of this file
    controls: ol.control.defaults({
      attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
        collapsible: true
      })
    }).extend([
      new app.geoLocationControl()
    ]),
    layers: allLayers,
    renderer: 'canvas'
  });

  var centerPosition = ol.proj.transform(center, 'EPSG:4326', 'EPSG:3857');

  // Setting zoom and center for the map (need to do this after creating map. so that we kan transform our
  // center to correct map projection)
  view = new ol.View({
    center: centerPosition,
    zoom: zoomLevel
  });

  map.setView(view);




  // Marker overlay
  var marker = new ol.Overlay({
    position: displayMarker ? centerPosition : undefined,
    positioning: 'center-center',
    element: document.getElementById('marker'),
    stopEvent: false
  });

  map.addOverlay(marker);

  // Adding the mouse position control
  var mousePositionControl = new ol.control.MousePosition({
    coordinateFormat: ol.coordinate.createStringXY(8),
    projection: 'EPSG:4326',
    undefinedHTML: '&nbsp;'
  });
  map.addControl(mousePositionControl);

  var layerSwitcher = new ol.control.LayerSwitcher({
    tipLabel: 'Layerswitcher' // Optional label for button
  });
  map.addControl(layerSwitcher);


  //###################### DRAWING ########################################
  // make interactions global so they can later be removed
  // The features are not added to a regular vector layer/source,
  // but to a feature overlay which holds a collection of features.
  // This collection is passed to the modify and also the draw
  // interaction, so that both can add or modify features.
  var features = new ol.Collection();
  featureOverlay = new ol.layer.Vector({
    source: new ol.source.Vector({
      features: features
    }),
    style: new ol.style.Style({
      fill: new ol.style.Fill({
        color: 'rgba(255, 0, 255, 0.2)'
      }),
      stroke: new ol.style.Stroke({
        color: '#ff00ff',
        width: 2
      }),
      image: new ol.style.Circle({
        radius: 7,
        fill: new ol.style.Fill({
          color: '#ff00ff'
        })
      })
    })
  });

  

  function computeFeatureStyle(feature, resolution) {
    return [new ol.style.Style({
      image: new ol.style.Circle({
        radius: 10,
        fill: new ol.style.Fill({
          color: 'rgba(0, 0, 255, 0.5)'
        })
      }),
      fill: new ol.style.Fill({
        color: 'rgba(0, 0, 255, 0.2)'
      }),
      stroke: new ol.style.Stroke({
        color: 'rgba(0, 0, 255, 0.8)',
        width: 1
      }),
      text: new ol.style.Text({
        font: '12px helvetica,sans-serif',
        text: feature.get(hoverAttribute),
        //rotation: 360 * rnd * Math.PI / 180,
        fill: new ol.style.Fill({
          color: '#000'
        }),
        stroke: new ol.style.Stroke({
          color: '#fff',
          width: 2
        })
      })
    })];
  }


  var mouseFeatures = new ol.Collection();
  mouseOverlay = new ol.layer.Vector({
    source: new ol.source.Vector({
      features: mouseFeatures
    }),
    style: computeFeatureStyle
  });

  

  if (drawnObjs) { // Get drawn objs param from initMap
    var format = new ol.format.GeoJSON();
    var drawnfeatures = format.readFeatures(drawnObjs, {
      dataProjection: 'EPSG:4326',
      featureProjection: map.getView().getProjection().getCode()
    });
    //featureOverlay.clear(true);
    featureOverlay.getSource().addFeatures(drawnfeatures);
  }

  featureOverlay.setMap(map);
  mouseOverlay.setMap(map);

  var modify = new ol.interaction.Modify({
    features: features,
    // the SHIFT key must be pressed to delete vertices, so
    // that new vertices can be drawn at the same position
    // of existing vertices
    deleteCondition: function(event) {
      return ol.events.condition.shiftKeyOnly(event) &&
        ol.events.condition.singleClick(event);
    }
  });


  var draw; // global so we can remove it later
  function addDrawInteraction() {
    draw = new ol.interaction.Draw({
      features: features,
      type: /** @type {ol.geom.GeometryType} */ (typeSelect.value)
    });
    map.addInteraction(draw);
  }

  var pointerMoveId;
  function removeMouseOver(){
    map.unByKey(pointerMoveId);
    mouseOverlay.getSource().clear(true);
  }


  function chooseFromLayer(chooseLayer) {
    map.removeInteraction(draw);
    map.removeInteraction(modify);
    chooseLayer.setVisible(true);

    var clickInfo = function(pixel) {
      var features = [];
      map.forEachFeatureAtPixel(pixel, function(feature) {

        if (deleteObj){
          if (feature){
            //debugger;
            featureOverlay.getSource().removeFeature(feature);
            setTimeout(function() { //wait until feature is removed
              mouseOverlay.getSource().clear(true);
            }, 100);
          }
          
        }else {
          if (feature.getId()){
            featureOverlay.getSource().addFeature(feature);
          }
        }
      });
    };

    map.on('click', function(evt) {
      var pixel = evt.pixel;
      clickInfo(pixel);
    });

    var mouseInfo = function(pixel) {
      var features = [];
      mouseOverlay.getSource().clear(true);
      map.forEachFeatureAtPixel(pixel, function(feature, layer) {
        mouseOverlay.getSource().addFeature(feature);
      });
    };

    pointerMoveId =  map.on('pointermove', function(evt) {
      var pixel = evt.pixel;
      mouseInfo(pixel);
    });
  }



  /**
   * Let user change the geometry type.
   * @param {Event} e Change event.
   */
  typeSelect.onchange = function(e) {
    map.removeInteraction(draw);
    deleteObj = false;

    for (i = 0; i < chooseLayers.length; i++) {
      if (parseInt(e.target.value) === chooseLayers[i].getProperties().id) {
        choosenLayer = chooseLayers[i];
        hoverAttribute = choosenLayer.getProperties().hoverAttribute;
        choosenLayer.setVisible(true);
        chooseFromLayer(choosenLayer);
      } else {
        chooseLayers[i].setVisible(false);
      }
    }
    if (e.target.value === 'Point' || e.target.value === 'Polygon'){
      map.addInteraction(modify);
      addDrawInteraction();
      removeMouseOver();
    }
  };

  addDrawInteraction();



 /* $('#save-button').click(function() {
    // get the features drawn on the map
    var features = null;
    features = featureOverlay.getSource().getFeatures();
    var format = new ol.format.GeoJSON();
    // write features to GeoJSON format using projection EPSG:4326

    var result = format.writeFeatures(features, {
      dataProjection: 'EPSG:4326',
      featureProjection: map.getView().getProjection().getCode()
    });

    alert(result);
  });*/







  // clear map when user clicks on 'Delete all features'
  $("#delete").click(function() {
    clearMap();
  });

  // clears the map and the output of the data
  function clearMap() {
    featureOverlay.getSource().clear(true);
    geoOverlay.getSource().clear(true);
    //geolocation.setTracking(false);
  }

  $('#deleteOne').click(function() {
    deleteObj = !deleteObj; // toggle
    if (deleteObj){
      $('#deleteOne').addClass('clearOneActive');
      chooseFromLayer(featureOverlay);
      map.removeInteraction(modify);
      map.removeInteraction(draw);
    } else {
      $('#deleteOne').removeClass('clearOneActive');
      removeMouseOver();
      mouseOverlay.getSource().clear(true);
      map.addInteraction(modify);
      addDrawInteraction();
    }
      
  });

  function removeFeature(featureID) {

   var features = featureOverlay.getSource().getFeatures();
     if (features != null && features.length > 0) {
         for (x in features) {
            var id = features[x].getProperties().id;
            if (id == featureID) {
              featureOverlay.getSource().removeFeature(features[x]);
              geoOverlay.getSource().clear(true);
               break;
            }
          }
        }
      }

  // creates unique id's
  function uid() {
    var id = 0;
    return function() {
      if (arguments[0] === 0) {
        id = 0;
      }
      return id++;
    }
  }

  //######################## GEOLOCATION  #############


  var geoPosStyle = new ol.style.Style({
    fill: new ol.style.Fill({
        color: 'rgba(0, 0, 255, 0.1)'
      }),
      stroke: new ol.style.Stroke({
        color: 'rgba(0, 0, 255, 0.5)',
        width: 2
      }),
      image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
        anchor: [13, 13],
        anchorXUnits: 'pixels',
        anchorYUnits: 'pixels',
        opacity: 1,
        src: 'images/geoloc2.png'
      }))
    });


 var geoFeatures = new ol.Collection();
  geoOverlay = new ol.layer.Vector({
    source: new ol.source.Vector({
      features: geoFeatures
    }),
    style: geoPosStyle
  });

  geoOverlay.setMap(map);




/* var element = document.createElement('div');
    element.className = 'ol-unselectable ol-control geo-location';

    var button = document.createElement('button');
    button.setAttribute('title', tipLabel);
    element.appendChild(button);

*/
  var geolocation = new ol.Geolocation({
    projection: view.getProjection()
  });

  function el(id) {
    return document.getElementById(id);
  }


  // handle geolocation error.
  geolocation.on('error', function(error) {
    alert(error.message);
  });

  function showalert(message,alerttype) {

    $('#alert_placeholder').append('<div id="alertdiv" class="alert alert-danger fade in"><a class="close" data-dismiss="alert">×</a><span>'+message+'</span></div>')
    setTimeout(function() { // this will automatically close the alert and remove this if the users doesnt close it in 5 secs
      $("#alertdiv").remove();
    }, 5000);
  }


/**
 * Listen for changes in the user's location
 */
  geolocation.on('change:position', function() {

    removeFeature('geoLoc');

    var coordinates = geolocation.getPosition();

    var accuracy = geolocation.getAccuracy();
    if(accuracy > 500){
      showalert('Geolocation accuracy (blue circle) is larger than 500, accuracy now: '+accuracy,'alert-error')
    }
    
    var geoExtent = [
      coordinates[0] - accuracy - 100,
      coordinates[1] - accuracy - 100,
      coordinates[0] + accuracy + 100,
      coordinates[1] + accuracy + 100
      ];
    var posFeature = new ol.Feature({
      geometry: new ol.geom.Point(coordinates),
    });
    posFeature.set('id', 'geoLoc');
   
    featureOverlay.getSource().addFeature(posFeature);
    geoOverlay.getSource().addFeature(posFeature);

    
    var accuracyFeature = new ol.Feature();
      geolocation.on('change:accuracyGeometry', function() {
        accuracyFeature.setGeometry(geolocation.getAccuracyGeometry());
      });

    geoOverlay.getSource().addFeature(accuracyFeature);

    var numFeatures = featureOverlay.getSource().getFeatures().length;
   
    if (numFeatures > 1) { //Zoom to all features
      var extent = featureOverlay.getSource().getExtent();
      map.getView().fit(extent, map.getSize());
    } else if (geoExtent) { //Zoom to accuracy circle
      map.getView().fit(geoExtent, map.getSize());
    } else { //Zoom to geolocation
      map.getView().setCenter(coordinates);
      map.getView().setZoom(15);
    }

    map.addInteraction(modify);
    addDrawInteraction();

    geolocation.setTracking(false);
 
 });



  /*function updateLocationPosition(coordinate) {
    //var locationPosition = ol.coordinate.toStringXY(ol.proj.transform(coordinate, 'EPSG:3857', 'EPSG:4326'), 8);
    // Set/move location pin
    marker.setPosition(coordinate);
    // Update form field "location"
    //var locationEl = document.getElementById("location");
    //locationEl.setAttribute("value", locationPosition);
    // Adding a little animation
    $("#location").animate({
      borderWidth: "4"
    }, 500, function() {
      $("#location").animate({
        borderWidth: "1"
      }, 500, function() {});
    });
  }*/
}

/**
 * @return {GEOJson} returns all features currently on the map in GEOJson format
 */
function getFeatures() {
  var features = featureOverlay.getSource().getFeatures();
  var format = new ol.format.GeoJSON();
  // write features to GeoJSON format using projection EPSG:4326

  var result = format.writeFeatures(features, {
    dataProjection: 'EPSG:4326',
    featureProjection: map.getView().getProjection().getCode()
  });

  return result;
}