$("input:radio[name ='gender']").change(function () {
  refreshYourMarker();
})

/**
 * Very important globals. Used by other files.
 */
var locationTimeout;
var autocomplete;
var yourMarker;
if (typeof (google) != "undefined") {
  google.maps.event.addDomListener(window, 'load', initialize);
  yourMarker = new google.maps.Marker();
}

var watchPositionId;
var autocompleteElement = document.getElementById('venue');
var loadCounter = 0;

$(GLOBAL).bind("getPosition", function () {
  consoleLog("Waiting for position");
  $(".mapLoader").html("Determine location... <img src='img/smallLoader.gif' />");
  $(".mapLoader").show();
  loadCounter++;
});

$(GLOBAL).bind("loadSettingsFromServer", function () {
  consoleLog("Loading settings from server");
  $(".mapLoader").html("Load settings... <img src='img/smallLoader.gif' />");
  $(".mapLoader").show();
  loadCounter++;
});

$(GLOBAL).bind("showPosition loadSettingsFromServerDone", function () {
  consoleLog("Load Counter: " + loadCounter);
  loadCounter--;
  if (loadCounter == 0) {
    $(GLOBAL).trigger("loadingDone");
  }
});

$(GLOBAL).bind("loadingDone showError", function () {
  consoleLog("loadingDone");
  $(".mapLoader").hide();
});

function initialize() {
  consoleLog("Initialize gmaps");

  /**
   * Hide map when small screen size.
   */
  if (Modernizr.mq('only all and (max-width: 480px)')) {
    //$("#map-canvas").hide();
  }
  pollLocation();
  var defaultZoom = 13;
  if (localStorage["gmapZoom"] != null) {
    defaultZoom = parseInt(localStorage["gmapZoom"]);
    if (isNaN(defaultZoom)) {
      defaultZoom = 13;
    }
  }
  consoleLog("Default zoom: " + defaultZoom);

  var mapOptions = {
    center: defaultLatLng,
    zoom: defaultZoom,
    draggable: true,
    streetViewControl: false,
    panControl: false,
    mapTypeControl: false
  };
  map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);

  // Map idled
  google.maps.event.addListener(map, 'idle', function () {
    consoleLog("Map loaded");
    localStorage["gmapZoom"] = map.getZoom();
  });

  // MarkerClusterer config.
  var clusterStyles = [
    {
      textColor: '#ffffff',
      url: 'img/0.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/10.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/20.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/25.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/30.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/40.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/50.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/60.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/70.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/75.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/80.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/90.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/100.png',
      height: 32,
      width: 32
    },
    {
      textColor: '#ffffff',
      url: 'img/neutral.png',
      height: 32,
      width: 32
    }
  ];
  var mcOptions = {
    gridSize: 25,
    styles: clusterStyles,
    maxZoom: 18
  };
  mc = new MarkerClusterer(map, [], mcOptions);
  mc.setCalculator(calculator_);

  /*autocomplete = new google.maps.places.Autocomplete(autocompleteElement);
  autocomplete.bindTo('bounds', map);
  google.maps.event.addListener(autocomplete, 'place_changed', autocompletePlaceChanged);*/
  //google.maps.event.addListener(map, 'zoom_changed', centerGMap);
  //google.maps.event.addListener(map, 'bounds_changed', centerGMap);

  /**
   * Show tutorial menu button.
   */
  $(".mapMenuItem").show();

  consoleLog("gmapInitialised fired");
  $(GLOBAL).trigger("gmapInitialised");
}

/**
 * Get lat lng based on given address.
 * The result is send to the callback function.
 * The callback function accepts 2 arguments:
 * - String: address
 * - google.maps.LatLng
 */
function getLatLong(address, callback) {
  consoleLog("getLatLng");
  if (address != null && address.length > 0) {
    var geocoder = new google.maps.Geocoder();

    geocoder.geocode({ 'address': address }, function (results, status) {
      if (status == google.maps.GeocoderStatus.OK) {
        callback(address, results[0].geometry.location);
      } else {
        $(GLOBAL).trigger('processNearbyPlacesDone');
      }
    });
  } else {
    callback(address, defaultLatLng);
  }
}

function refreshYourMarker() {
  if (typeof (yourMarker) != "undefined") {
    var icon = 'img/female.png';
    if ($("input:radio[name ='gender']:checked").val() == "male") {
      icon = 'img/male.png';
    }
    yourMarker.setVisible(true);
    yourMarker.setClickable(false);
    yourMarker.setMap(map);
    yourMarker.setIcon(icon);
    yourMarker.setPosition(defaultLatLng);
  }
}

/**
 * sizePercentage is a value between 0 and 1 inclusive.
 */
function addPlayerMarker(nickname, gender, lat, lng, sizePercentage) {
  console.log("addPlayerMarker: " + nickname + " " + gender + " " + lat + " " + lng) + " " + sizePercentage;
  yourMarker.setVisible(false);
  var icon = 'img/' + gender + '.png';
  var image = {
    url: icon,
    scaledSize: new google.maps.Size(10 + (10 * sizePercentage), 18 + (18 * sizePercentage)),
  };

  var playerMarker = new google.maps.Marker();
  playerMarker.setMap(map);
  playerMarker.setIcon(image);
  playerMarker.setPosition(new google.maps.LatLng(lat, lng));
  google.maps.event.addListener(playerMarker, 'click', function () {
    alert(nickname);
  });
}

/**
 * Event handler callback function.
 */
/*function autocompletePlaceChanged() {
  var place = autocomplete.getPlace();
  $(autocompleteElement).trigger("placeChanged");
  consoleLog("Check-in place changed: " + place.name);
}*/

function requestNearbyPlaces(keyword, defaultLatLng, noRecurse) {
  consoleLog("requestNearbyPlaces");
  // Trigger event.
  $(GLOBAL).trigger('requestNearbyPlaces');
  consoleLog("requestNearbyPlaces fired");

  var request = {
    keyword: keyword,
    location: defaultLatLng,
    radius: Math.min(radius, 50000),
    types: ['art_gallery', 'bar', 'cafe', 'establishment', 'gym', 'museum', 'night_club', 'park', 'restaurant', 'stadium', 'store', 'university']
  };
  // Default search with no keywords. We show nearby places of interest.
  if (keyword.length == 0) {
    request = {
      keyword: keyword,
      location: defaultLatLng,
      rankBy: google.maps.places.RankBy.DISTANCE,
      types: ['art_gallery', 'bar', 'cafe', 'gym', 'museum', 'night_club', 'park', 'restaurant', 'stadium', 'store', 'university']
    };
  }
  deleteMarkers();
  deletePlaces();
  $("#places").empty();
  centerGMap(defaultLatLng);

  var service = new google.maps.places.PlacesService(map);
  service.nearbySearch(request, function (results, status) {
    processNearbyPlaces(keyword, results, status, noRecurse);
  });
}

/**
 * Event handler callback function.
 * Used by requestNearbyPlaces function. This function will
 * actually place the markers on the map and if checkIn is true
 * also handle the check-in.
 */
function processNearbyPlaces(keyword, results, status, noRecurse) {
  if (status == google.maps.places.PlacesServiceStatus.OK) {
    if (results.length > 0) {
      var bounds = new google.maps.LatLngBounds();
      var distance;
      for (var i = 0; i < results.length; i++) {
        var place = results[i];
        var placeLoc = place.geometry.location;
        bounds.extend(placeLoc);
        if (distance == undefined) {
          centerGMap(placeLoc);
          distance = getDistance(defaultLatLng.lat(), defaultLatLng.lng(), placeLoc.lat(), placeLoc.lng()).toFixed(3);
        } else {
          distance = Math.min(distance, getDistance(defaultLatLng.lat(), defaultLatLng.lng(), placeLoc.lat(), placeLoc.lng())).toFixed(3);
        }
        createMarker(place);
      }

      /**
       * Sort the places from nearest to furthest.
       */
      placesArray.sort(function (a, b) {
        var distanceA = a.getDistance(defaultLatLng);
        var distanceB = b.getDistance(defaultLatLng);
        return (distanceA < distanceB) ? -1 : (distanceA > distanceB) ? 1 : 0;
      });
      createPlacesResultList();
      getPlacesResultListIcons();
      map.fitBounds(bounds);
      var newZoom = map.zoom;
      map.setZoom(Math.min(13, newZoom));
      consoleLog("Closest from your location: " + distance + "KM");
      // Trigger done event.
      $(GLOBAL).trigger('processNearbyPlacesDone');
      consoleLog("processNearbyPlacesDone fired");
    }
  } else if (noRecurse !== true) {
    requestNearbyPlaces(keyword, defaultLatLng, true);
  } else {
    // Trigger done event.
    $(GLOBAL).trigger('processNearbyPlacesDone');
    consoleLog("processNearbyPlacesDone fired");
  }
}

/**
 * Replaces the default icons in the places result list with the correct ones.
 */
function getPlacesResultListIcons() {
  consoleLog("getPlacesResultListIcons");
  var placesJson = [];
  for (var key in placesObjArray) {
    var place = {};
    place["id"] = key;
    place["name"] = placesObjArray[key].getPlaceResult()["name"];
    place["vicinity"] = placesObjArray[key].getPlaceResult()["vicinity"];
    place["lat"] = placesObjArray[key].getLat();
    place["lng"] = placesObjArray[key].getLng();
    placesJson.push(place);
  }

  ajaxCall("/api/v1/getPlacesGender.php", {
    uuid: localStorage["uniqueId"],
    places: placesJson
  }).fail(function (data) {
    consoleLog("getPlacesGender error: " + JSON.stringify(data));
  }).done(function (data) {
    if (data.response != undefined) {
      switch (data.response.responseCode) {
        case 200:
          for (var key in data.response.value) {
            if (data.response.value[key] == 'men') {
              $(".genderIcon" + key).attr('src', "img/mars.png");
              markersObjArray[key].setIcon("img/mars.png");
            } else if (data.response.value[key] == 'women') {
              $(".genderIcon" + key).attr('src', "img/venus.png");
              markersObjArray[key].setIcon("img/venus.png");
            } else {
              $(".genderIcon" + key).attr('src', "img/trans.png");
              markersObjArray[key].setIcon("img/trans.png");
            }
          }
          break;
        default:
          consoleLog("getPlacesGender error " + data.response.responseCode + ": " + data.response.message);
          break;
      }
    } else {
      consoleLog("getPlacesGender server error" + data);
    }
  }).always(function () {
    // Trigger done event.
    $(GLOBAL).trigger("getPlacesResultListIconsDone");
    consoleLog("getPlacesResultListIconsDone fired");
  });
}

/**
 * Create a marker with corresponding content.
 */
function createMarker(place) {
  consoleLog("createMarker");
  var placeLoc = place.geometry.location;
  var marker = new google.maps.Marker({
    map: map,
    position: placeLoc,
    icon: "img/trans.png"
  });
  markers.push(marker);
  markersObjArray[place.id] = marker;

  var placeCont = new Place();
  placeCont.setPlaceResult(place);
  placeCont.setLat(place.geometry.location.lat());
  placeCont.setLng(place.geometry.location.lng());
  placeCont.setIsGooglePlaceResult(true);
  placesArray.push(placeCont);
  placesObjArray[place.id] = placeCont;

  google.maps.event.addListener(marker, 'click', function () {
    revealMessageDialog("", placesObjArray[place.id].getHtmlPricingTable(true));
    map.panTo(placeLoc);
  });
  return marker;
}

/**
 * Get place details for given Google Place object.
 */
function getPlaceDetails(place) {
  consoleLog("getPlaceDetails: " + JSON.stringify(place));
  var service = new google.maps.places.PlacesService(map);
  service.getDetails(place, function (placeDetails, status) {
    if (status == google.maps.places.PlacesServiceStatus.OK) {
      var addressComponents = placeDetails.address_components;
      for (var key in addressComponents) {
        consoleLog("Place detail: " + JSON.stringify(addressComponents[key]));
        if (addressComponents[key]["types"].indexOf("locality") > -1 ||
          addressComponents[key]["types"].indexOf("administrative_area_level_2") > -1 ||
          addressComponents[key]["types"].indexOf("country") > -1) {
          placesObjArray[place.id].setCity(addressComponents[key]["long_name"]);
          placesObjArray[place.id].setWebsite(placeDetails.website);

          placesObjArray[place.id].loadPoints();
          break;
        }
      }
    } else {
      consoleLog("ELSE: " + status);
      placesObjArray[place.id].loadPoints();
    }
  });
}

/**
 * createConqueredMarker is different from createMarker.
 * The placeCont is an object with limited data as opposed to the place object
 * used by the createMarker function. The place object used by createMarker
 * Is a full-fledged class by Google with functions and inheritances.
 */
function createConqueredMarker(placeCont, iconPath) {
  var placeLoc = new google.maps.LatLng(placeCont.getLat(), placeCont.getLng());
  var marker = new google.maps.Marker({
    position: placeLoc,
    icon: iconPath
  });
  markers.push(marker);
  mc.addMarker(marker);
  markersObjArray[placeCont.getPlaceResult().id] = marker;

  placesArray.push(placeCont);
  placesObjArray[placeCont.getPlaceResult().id] = placeCont;

  google.maps.event.addListener(marker, 'click', function () {
    revealMessageDialog("", placesObjArray[placeCont.getPlaceResult().id].getHtmlPricingTable(true));
    map.panTo(placeLoc);
  });
  return marker;
}

/**
 * Refresh location at fixed interval.
 */
function pollLocation() {
  consoleLog("pollLocation");
  getLocation();
}

/**
 * First we try to locate user by IP.
 * If that's not possible we ask to locate by browser Geolocation API.
 */
function getLocation() {
  consoleLog("getLocation");
  // First check using IP address.
  if (google.loader.ClientLocation != null && google.loader.ClientLocation.latitude != null && google.loader.ClientLocation.longitude != null) {
    defaultLatLng = new google.maps.LatLng(google.loader.ClientLocation.latitude, google.loader.ClientLocation.longitude);
  }
  // Then check with browser geolocation service.
  if (navigator.geolocation) {
    //locationTimeout = setTimeout("geolocationFail()", 30000);
    //watchPositionId = navigator.geolocation.watchPosition(showPosition, showError, { 'enableHighAccuracy': false, maximumAge: 30000, timeout: 10000 });
    getPosition();
  } else {
    revealMessageDialog("Location error", "It looks like your webbrowser does not support geolocation determination. Sadly in order to play this game your webbrowser has to support geolocation.");
  }
}

/**
 * Get position one time.
 */
function getPosition() {
  consoleLog("getPosition");
  if (navigator.geolocation) {
    $(GLOBAL).trigger("getPosition");
    navigator.geolocation.getCurrentPosition(showPosition, showPositionError, { enableHighAccuracy: false, maximumAge: 30000, timeout: 10000 });
  } else {
    revealMessageDialog("Location error", "It looks like your webbrowser does not support geolocation determination. Sadly in order to play this game your webbrowser has to support geolocation.");
  }
}

/**
 * Event handler callback function.
 * Used by getLocation function.
 * This is called when the user hasn't decided to allow us to use the geolocation service
 * within a timeout.
 */
function geolocationFail() {
  consoleLog("Location error", "We were unable to determine your location. In order to play this game you need to enable location sharing.");
  alert("We were unable to determine your location. In order to play this game you need to enable location sharing.");
  getLocation();
}

/**
 * Event handler callback function.
 * Used by getLocation function.
 * This is called when we are able to use the geolocation service.
 * A marker of the current location returned by the geolocation service
 * will be set on the map.
 */
var showPositionFirstTime = true;
function showPosition(position) {
  consoleLog("showPosition");
  $(GLOBAL).trigger("showPosition");

  //clearTimeout(locationTimeout);
  defaultLatLng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
  consoleLog("Your current location.\r\nLatitude: " + position.coords.latitude + "\r\nLongitude: " + position.coords.longitude);
  consoleLog("Location accuracy: " + position.coords.accuracy + "m");
  refreshYourMarker();

  if (showPositionFirstTime) {
    consoleLog("showPositionFirstTimeDone fired");
    $(GLOBAL).trigger("showPositionFirstTimeDone");
    centerGMap(defaultLatLng);
    showPositionFirstTime = false;
    consoleLog(JSON.stringify(localStorage));
  }
}

function getNearbyPlayers() {
  consoleLog("getNearbyPlayers");
  ajaxCall("/api/v1/getNearbyPlayers.php", {
    uuid: localStorage["uniqueId"],
    lat: defaultLatLng.lat(),
    lng: defaultLatLng.lng()
  }).fail(function (data) {
    consoleLog("getNearbyPlayers error: " + JSON.stringify(data));
  }).done(function (data) {
    if (data.response != undefined) {
      switch (data.response.responseCode) {
        case 200:
          if (data.response.value != undefined) {
            consoleLog("getNearbyPlayers received");
            var allPlayers = data.response.value.players;
            for (var key in allPlayers) {
              var player = allPlayers[key];
              var sizePercentage = (player.total_points / data.response.value.highscore);
              if (player.gender == "male") {
                addPlayerMarker(player.nickname, "male", player.lat, player.lng, sizePercentage);
              } else if (player.gender == "female") {
                addPlayerMarker(player.nickname, "female", player.lat, player.lng, sizePercentage);
              }
            }
          } else {
            consoleLog("getNearbyPlayers no places: " + data.response.responseCode + ":" + data.response.message);
          }
          break;
        case 401:
          consoleLog(JSON.stringify(data));
          break;
        case 404:
        case 412:
          revealMessageDialog("getNearbyPlayers authentication error", data.response.responseCode + ": " + data.response.message);
          break;
        default:
          revealMessageDialog("getNearbyPlayers error ", data.response.responseCode + ": " + data.response.message);
          break;
      }
    } else {
      revealMessageDialog("getNearbyPlayers server error", data);
    }
  }).always(function () {
    // Trigger done event.
    $(GLOBAL).trigger("getNearbyPlayersDone");
    consoleLog("getNearbyPlayersDone fired");
  });
}

/**
 * Show nearby places on the map.
 */
function getNearbyPlaces(apiCall, theRadius, showPlacesResultList, handler) {
  consoleLog(apiCall + " " + defaultLatLng.lat() + " " + defaultLatLng.lng());
  if (hasLocalStorage()) {
    if (typeof (theRadius) == "undefined") {
      theRadius = 50000000;
    }

    ajaxCall("/api/v1/" + apiCall + ".php", {
      uuid: localStorage["uniqueId"],
      lat: defaultLatLng.lat(),
      lng: defaultLatLng.lng(),
      radius: theRadius,
      women: localStorage["#interestWomen"],
      men: localStorage["#interestMen"],
      me: localStorage["#interestMe"]
    }).fail(function (data) {
      consoleLog(apiCall + " error: " + JSON.stringify(data));
    }).done(function (data) {
      if (data.response != undefined) {
        switch (data.response.responseCode) {
          case 200:
            if (handler != null) {
              handler(data);
            } else {
              if (data.response.value != undefined) {
                consoleLog(apiCall + " received");
                processPlaces(data.response.value);
                if (showPlacesResultList) {
                  createPlacesResultList();
                }
              } else {
                consoleLog(apiCall + " no places: " + data.response.responseCode + ":" + data.response.message);
              }
            }
            break;
          case 401:
            consoleLog(JSON.stringify(data));
            break;
          case 404:
          case 412:
            revealMessageDialog(apiCall + " authentication error", data.response.responseCode + ": " + data.response.message);
            break;
          default:
            revealMessageDialog(apiCall + " error ", data.response.responseCode + ": " + data.response.message);
            break;
        }
      } else {
        revealMessageDialog(apiCall + " server error", data);
      }
    }).always(function () {
      // Trigger done event.
      $(GLOBAL).trigger(apiCall + "Done");
      consoleLog(apiCall + "Done fired");
    });
  }
}

function processPlaces(allPlaces) {
  consoleLog("processPlaces");
  for (var key in allPlaces) {
    if (!allPlaces.hasOwnProperty(key)) {
      //The current property is not a direct property of p
      continue;
    }
    deleteMarkers();
    deletePlaces();
    $("#places").empty();

    var bounds = new google.maps.LatLngBounds();
    bounds.extend(defaultLatLng);
    for (var placeKey in allPlaces.places) {
      var place = allPlaces.places[placeKey];
      var placeResult = {
        id: place.id,
        name: place.name,
        vicinity: place.vicinity,
        city: place.city,
        website: place.website
      };
      var placeObj = new Place();
      placeObj.setPlaceResult(placeResult);
      var placeLoc = new google.maps.LatLng(place.lat, place.lng);
      bounds.extend(placeLoc);
      placeObj.setLat(place.lat);
      placeObj.setLng(place.lng);
      placeObj.setWebsite(place.website);
      placeObj.setMen(parseFloat(place.men));
      placeObj.setWomen(parseFloat(place.women));
      if (parseFloat(place.men) > parseFloat(place.women)) {
        createConqueredMarker(placeObj, "img/mars.png");
      } else if (parseFloat(place.men) < parseFloat(place.women)) {
        createConqueredMarker(placeObj, "img/venus.png");
      } else {
        createConqueredMarker(placeObj, "img/trans.png");
      }
    }

    map.fitBounds(bounds);
    var newZoom = map.zoom;
    map.setZoom(newZoom);
    map.setCenter(defaultLatLng);
  }
}

function createPlacesResultList() {
  consoleLog("createPlacesResultList");
  for (var key in placesArray) {
    $("#places").append(placesArray[key].getHtmlPricingTable());
  }
}

/**
 * Event handler callback function.
 * Used by getLocation function.
 * This function handles the different error codes returned by the geolocation service.
 */
var locationErrorFirstTime = true;
function showError(error) {
  navigator.geolocation.clearWatch(watchPositionId);
  // Some androids can only work with enableHighAccuracy set to true.
  if (locationErrorFirstTime) {
    locationErrorFirstTime = false;
    navigator.geolocation.watchPosition(showPosition, showError, { 'enableHighAccuracy': true, maximumAge: 30000 });
  } else {
    switch (error.code) {
      case error.PERMISSION_DENIED:
        revealMessageDialog("Location denied error", "We were unable to determine your location. In order to play this game you need to enable location sharing.");
        break;
      case error.POSITION_UNAVAILABLE:
        revealMessageDialog("Location unavailable error", "We were unable to determine your location. In order to play this game we need your location information.");
        break;
      case error.TIMEOUT:
        revealMessageDialog("Location timeout  error", "We were unable to determine your location. In order to play this game we need your location information.");
        break;
      case error.UNKNOWN_ERROR:
        revealMessageDialog("Unknown location error", "We were unable to determine your location. Maybe your webbrowser does not support geolocation services.");
        break;
    }
    $(GLOBAL).trigger("showError");
  }
}

var positionErrorFirstTime = true;
function showPositionError(error) {
  // Some androids can only work with enableHighAccuracy set to true.
  if (positionErrorFirstTime) {
    positionErrorFirstTime = false;
    navigator.geolocation.getCurrentPosition(showPosition, showPositionError, { enableHighAccuracy: true, maximumAge: 30000 });
  }
}
