﻿/**
 *  cjrtGoogleMaps library
 *
 *  wymagania: skrypt google maps api
 */
 
 /*********************************************************************
  *
  * cjrtGoogleMaps class
  *
  * pola:
  *   - map   {GMap2}    - referencja do obiektu mapy
  *   - icons {GIcon[]}  - tablica
  */
  
  /** konstruktory **/
  
  /**
   *  {GMap2} map - mapa google maps
   */
  function cjrtGoogleMaps(map){
		this.map = map;
		this.icons = new Array();
	}
  
  /** metody: **/
  
 /**
  *  {void} addMarker({GMarker} marker) 
  *
  *  dodaje do overlay mapy marker
  */   
  cjrtGoogleMaps.prototype.addMarker = function(marker) {
		this.map.addOverlay(marker);
  };
	
  /** metody statyczne: **/
  
 /**
  *  {GMarker} createMarker({GLatLng} point, {GMarkerOptions} markerOptions, {GIcon} icon) 
  *
  *  tworzy marker
  */	
	cjrtGoogleMaps.createMarker = function(point, markerOptions, icon) {
		markerOptions.icon = icon;
		var marker = new GMarker(point, markerOptions);
		return marker;
	};   
	
  /**
   *  {GIcon} createIcon({String} iconImage, {GSize} iconSize, {String} shadowImage, {GSize} shadowSize, {GPoint} iconAnchor, {GPoint} infoWndAnchor)
   *
   *  tworzy ikonę z cieniem 
   */
   cjrtGoogleMaps.createIcon = function(iconImage, iconSize, shadowImage, shadowSize, iconAnchor, infoWndAnchor) {
		var icon = new GIcon();
		icon.image = iconImage;
		icon.iconSize = iconSize;
		icon.shadow = shadowImage;
		icon.shadowSize = shadowSize;
		icon.iconAnchor = iconAnchor;
		icon.infoWindowAnchor = infoWndAnchor;
		return icon;
	};
	
  /**
   *  {GIcon} createIconWithoutShadow({String} iconImage, {GSize} iconSize, {GPoint} iconAnchor, {GPoint} infoWndAnchor)
   *  
   *  tworzy ikonę bez cienia
   */																
   cjrtGoogleMaps.createIconWithoutShadow = function(iconImage, iconSize, iconAnchor, infoWndAnchor) {
			var icon = new GIcon();
			icon.image = iconImage;
			icon.iconSize = iconSize;
			icon.iconAnchor = iconAnchor;
			icon.infoWindowAnchor = infoWndAnchor;
			return icon;
	};

 /*****************************************************************************************
  * 
  *  cjrtGroupedMarkers class
  *
  *  pola:
  *    - markers - zgrupowane markery
  *    - marker  - marker reprezentujacy zgrupowane markery
  */
  
  /** konstruktory **/
  function cjrtGroupedMarkers() {
		this.markers = new Array();
		this.selfPoints = new Array();
		this.marker = null;
	}

  /** metody **/
	
	/**
	 *  {void} setGlobalMarker({GMarker} marker)
	 *
	 *  ustawienie globalnego markera (reprezentujacy te zgrupowane)
	 */
	cjrtGroupedMarkers.prototype.setGlobalMarker = function(marker, setEvent) {
		this.marker = marker;
		
		if (setEvent) {
			var markers = this.markers;
			var selfPoints = this.selfPoints;
			
			GEvent.addListener(marker, 'click', function(){
				setEvent(marker, markers, selfPoints);
			});
		}
	}
	
	/**
	 *  {void} addSelfPoint({selfPoint}) 
	 *
	 *  dodanie selfPoint do grupy
     */	 
	cjrtGroupedMarkers.prototype.addSelfPoint = function(selfPoint) {
		this.selfPoints.push(selfPoint);
	};

	/**
	 *  {void} addMarker({GMarker}) 
	 *
	 *  dodanie markera do grupy
     */	 
	cjrtGroupedMarkers.prototype.addMarker = function(marker) {
		this.markers.push(marker);
	};
 
 
   /***************************************************************************************
    *
	*  cjrtMarkerManager class
	*
	*  pola:
	*    - {cjrtGoogleMaps}       cjrtGM         - referencja na obiekt cjrtGoogleMaps
	*    - {GMareker[]}           markers        - markery zarzadzane przez cjrtMarkerManager
	*    - {cjrtGroupedMarkers[]} groupedMarkers - tablica zgrupowanych markerow
	*    - {}                     properties     - wlasciwosci : 
	*													- groupInRadius : {int} x    - grupuj markery w promienu x pikseli
	*    - {GIcon[][]}            iconSet        - zestaw ikon wyswietlanych w zaleznosci od ilosci zgrupowanych markerow
    *                                                 iconSet postaci:  [
	* 																	  [{GIcon} ikona dla pojedynczego markera],
    * 																	  [{int} dolny zakres, {GIcon} ikona reprezentujaca grupowy marker]
    *                                                                     ...
	*                                                                   ]
	*/
	
	/** konstruktory **/
	
	/**
	 *  {cjrtGoogleMaps} gm
	 *  {}               properties
	 *  {GIcon[][]}      iconSet
	 */
	function cjrtMarkerManager(gm, properties, iconSet) {
		this.cjrtGM = gm;
		this.markers = new Array();
		this.selfPoints = new Array();
		this.groupedMarkers = new Array();
		this.properties = properties || {};
		this.iconSet = iconSet;
		this.functionForGroupedMarkers = null;
		if (!properties.groupInRadius)
			properties.groupInRadius = 0;
		
	}
	
	/**
	 *  {void} setClickFunctionForGroupedMarkers( {callback(marker, markers, selfPoint)} fcn)
	 *
	 *   {GMarker}   marker  - marker grupujacy
	 *   {GMarker[]} markers - markery zgrupowane
	 *
	 *  ustawia funkcję zwrotną dla click event dla markera grupującego; 
	 */
	cjrtMarkerManager.prototype.setClickFunctionForGroupedMarkers = function(fcn) {
		this.functionForGroupedMarkers = fcn;
	}
	
	/**
	 *  {GIcon} getIconToSize({int} size)
	 *
	 *  zwraca ikonę dopasowaną do liczby zgrupowanych markerów
	 */
	cjrtMarkerManager.prototype.getIconToSize = function(size){
		if (this.iconSet != null) {
			if (size == 1) {
				return this.iconSet[0][0];
			}
			else {
				for (var i = this.iconSet.length - 1; i > 0 ; --i) {  
					if (size >= this.iconSet[i][0]) {
						return this.iconSet[i][1];
					}
				}
			}
		}
		
	};
	
	/**
	 *  {void} addToOverlay()
	 *
	 *  dodaje grupowe markery do mapy
	 */
	cjrtMarkerManager.prototype.addToOverlay = function() {

			for (i = 0; i < this.groupedMarkers.length; ++i) {
				//get number of grouped markers
				var howMany = this.groupedMarkers[i].markers.length;
				
				if (howMany == 1) {
					this.groupedMarkers[i].setGlobalMarker(this.groupedMarkers[i].markers[0]);
				}
				else{
					// set appopriate icon for group marker
					var icon = this.getIconToSize(howMany);
					this.groupedMarkers[i].setGlobalMarker(cjrtGoogleMaps.createMarker(this.groupedMarkers[i].markers[0].getLatLng(), { draggable: false }, icon), this.functionForGroupedMarkers);
				}
				// add to map overlay
				this.cjrtGM.map.addOverlay(this.groupedMarkers[i].marker);
			}
		}
	
	/**
	 *  {void} addMarkerWithSelfPoint({GMarker} marker, {selfPoint} selfPoint)
	 *
	 *  dodanie markera wraz z selfPoint do menadzera
	 */
	cjrtMarkerManager.prototype.addMarkerWithSelfPoint = function(marker, selfPoint) {
		this.markers.push(marker);
		this.selfPoints.push(selfPoint);
	}
	
	/**
	 *  {void} addMarker({GMarker} marker)
	 *
	 *  dodanie markera do menadzera
	 */
	cjrtMarkerManager.prototype.addMarker = function(marker) {
		this.markers.push(marker);
	}

	/**
	 *  {void} groupMarkers()
	 *
	 *  główny algorytm grupowania markerów, po zakończeniu grupowania wywołuje metodę addToOverlay() i wyswietla markery na mapie
	 */
	cjrtMarkerManager.prototype.groupMarkers = function() {
	
		// remove all grouped markers
		for (i = 0; i < this.markers.length; ++i) {
			if (this.groupedMarkers[i]) {
				if (this.groupedMarkers[i].marker != null) {
					this.cjrtGM.map.removeOverlay(this.groupedMarkers[i].marker);
				}
			}
		}
	
		var tempMarkerArray = new Array();
		var tempSelfPointArray = new Array();
		
		// selecting markers in bounds by div coordinates
		var markersInRange = new Array();
		var northEastBound = this.cjrtGM.map.fromLatLngToDivPixel(this.cjrtGM.map.getBounds().getNorthEast());
		var southWestBound = this.cjrtGM.map.fromLatLngToDivPixel(this.cjrtGM.map.getBounds().getSouthWest());

		for (i = 0; i < this.markers.length; ++i) {
			var xyMarker = this.cjrtGM.map.fromLatLngToDivPixel(this.markers[i].getLatLng());
			if (northEastBound.x + this.properties.groupInRadius > xyMarker.x && southWestBound.x - this.properties.groupInRadius < xyMarker.x && northEastBound.y - this.properties.groupInRadius < xyMarker.y && southWestBound.y + this.properties.groupInRadius > xyMarker.y) {
				tempMarkerArray.push(this.markers[i]);
				tempSelfPointArray.push(this.selfPoints[i]);
			}
		}
		
		
		this.groupedMarkers = new Array();

		var i = 0;
		while (tempMarkerArray.length > 0) {
		
			// get first marker and remove it from tempMarkerArray
			var firstMarker = tempMarkerArray.shift();
			var firstSelfPoint = tempSelfPointArray.shift();
			var xyFirstMarker = this.cjrtGM.map.fromLatLngToDivPixel(firstMarker.getLatLng());
			this.groupedMarkers[i] = new cjrtGroupedMarkers();
			this.groupedMarkers[i].addMarker(firstMarker);
			this.groupedMarkers[i].addSelfPoint(firstSelfPoint);
			
			
			var secondMarker = null;
			var secondSelfPoint = null;
			var xySecondMarker = null;
			
			// look for markers in range
			for (j = 0; j < tempMarkerArray.length; ++j) {
				secondMarker = tempMarkerArray[j];
				secondSelfPoint = tempSelfPointArray[j];
				xySecondMarker = this.cjrtGM.map.fromLatLngToDivPixel(secondMarker.getLatLng());
				
				// if second marker is in range
				if (Math.pow(xyFirstMarker.x-xySecondMarker.x, 2) + Math.pow(xyFirstMarker.y-xySecondMarker.y, 2) < Math.pow(this.properties.groupInRadius, 2)) {
				    // add it to groupedMarkers and remove from temp array
					this.groupedMarkers[i].addMarker(secondMarker);
					this.groupedMarkers[i].addSelfPoint(secondSelfPoint);
					tempMarkerArray.splice(j,1);
					tempSelfPointArray.splice(j,1);
					--j;
				}
			}
			++i;
		}
		
		// add to overlay
		this.addToOverlay();
	}
