/**
 * @name Mapová aplikace Modré hory
 * @version 1.6
 * @date 10.5.2011
 * @author Via Aurea s.r.o.
 *
 * @changelog
		v1.6
			- oprava chyby zorbazovani ikonek ve druhe urovni v kombinaci s aktivitou
		v1.5
			- pridano nastaveni výchoziho zoomu do inizializace
		v1.4
			- korekce pro finalni mapove podklady
		v1.3
			- prepinac popupWindow
			- cesta k zoomovacim ikonkam zoomIconPlus, zoomIconMinus
		v1.2
			- prepinac na vlastni zoomovac
		v1.1
			- podpora kodovani GPX. Parametr GPXEncoding
			- placeholder_toolbar nepovinny parametr pokud je showControls = false
			- pridano nastaveni shlukovace do inizializace
			- pridano nastaveni URL pro mapove podklady
 */


var modrehory = {

    map: null, // Google map object
    markerCluster: null, // Shlukovac markeru
    markers: new google.maps.MVCArray(), // seznam vsech body v pameti, bez ohledu jestli je typ videt
    infowindow: new google.maps.InfoWindow(), // infomarcni okno
    opts: null, // konfigurace
    typesData: null, // typy objektu
    zoom: null, // aktualni zoom
    loadedType: [], // seznam typu, ktere byly ze serveru nactene AJAXem

    /** Nastaveni typu objektu **/
    setTypes: function(types) {
        this.typesData = types;
    },

    /** Vygeneruje prepinac typu objektu **/
    createToolbar: function() {
        var mainDiv = document.createElement('div');
        var elUl = document.createElement('ul');
        var x = 0;
        for (var i in this.typesData) {
            x++;
            var elLi = document.createElement('li');
            var item = document.createElement('input');
            $(item).attr('type', 'checkbox')
					.attr('id', 'toolbar' + x)
					.attr('value', this.typesData[i].MapObjectTypeId)
					.attr('data-active', this.typesData[i].IsActive);
            $(item).bind('click', function() {
                var active;
                if ($(this).attr('data-active') == 'true') active = true;
                if ($(this).attr('data-active') == 'false') active = false;
                if (this.checked) modrehory.showType(this.value, active);
                else modrehory.hideType(this.value, active);
                return false;
            });
            $(elLi).append(item);
            $(elLi).append('<label for="toolbar' + x + '">' + this.typesData[i].MapObjectTypeDisplayName + '</label>');

            if (this.typesData[i].SubItems) {
                var elUls = document.createElement('ul');
                for (var j in this.typesData[i].SubItems) {
                    x++;
                    var elLis = document.createElement('li');
                    var item = document.createElement('input');
                    $(item).attr('type', 'checkbox')
							.attr('id', 'toolbar' + x)
							.attr('value', this.typesData[i].SubItems[j].MapObjectTypeId)
							.attr('data-active', this.typesData[i].SubItems[j].IsActive);
                    $(item).bind('click', function() {
                        var active;
                        if ($(this).attr('data-active') == 'true') active = true;
                        if ($(this).attr('data-active') == 'false') active = false;
                        if (this.checked) modrehory.showType(this.value, active);
                        else modrehory.hideType(this.value, active);
                        return false;
                    });
                    $(elLis).append(item);
                    $(elLis).append('<label for="toolbar' + x + '">' + this.typesData[i].SubItems[j].MapObjectTypeDisplayName + '</label>');
                    $(elUls).append(elLis);
                }
                $(elLi).append(elUls);
            }
            $(elUl).append(elLi);
        }
        $(mainDiv).append(elUl);
        $('#' + this.opts.placeholder_toolbar).append(mainDiv);
    },

    /** Vygeneruje vlasni zoomovac **/
    createZoom: function() {
        var zoom = document.createElement('div');
        zoom.className = 'zoom';
        $(zoom).css('position', 'absolute').css('top', '0').css('left', '0');
        var plus = document.createElement('img');
        plus.src = this.opts.zoomIconPlus;
        var self = this;
        $(plus).bind('click', function() { self.zoomIn() });
        $(zoom).append(plus);
        $(zoom).append('<br/>');
        var minus = document.createElement('img');
        minus.src = this.opts.zoomIconMinus;
        $(minus).bind('click', function() { self.zoomOut() });
        $(zoom).append(minus);
        $('#' + this.opts.placeholder).append(zoom);
    },

    /** Vygeneruje Google mapu **/
    createMap: function() {
        var gmap = document.createElement('div');
        $(gmap).attr('id', 'gmap');
        var height = $('#' + this.opts.placeholder).height();
        $(gmap).css('height', height);
        $('#' + this.opts.placeholder).append(gmap);
    },

    /** Vygeneruje prepinac mapovych podkladu **/
    createMapSwitch: function() {
        html = '<div id="type">' +
				'<input type="radio" id="maptype_1" name="maptype" checked="checked" onclick="modrehory.map.setMapTypeId(\'mh\')" />' +
				'<label for="maptype_1">' + this.opts.langs['Turist'] + '</label>' +
				'<input type="radio" id="maptype_2" name="maptype" onclick="modrehory.map.setMapTypeId(google.maps.MapTypeId.SATELLITE);" />' +
				'<label for="maptype_2">' + this.opts.langs['Satellite'] + '</label>' +
				'</div>';
        $('#' + this.opts.placeholder).css('position', 'relative').append(html);
    },

    /** Inicializace mapy **/
    init: function(options) {

        var langs = new Array();
        langs['Satellite'] = 'Satelitní';
        langs['Turist'] = 'Turistická';
        // set up default options
        var defaults = {
            showControls: true,  // zobrazi toolbar s typy objektu a prepinac mapovych podkladu
            customControls: false, // vlastni zoomovac (pro kiosek)
            langs: langs, 	// popisky
            lat: 48.9105619444, // vychozi souradnice
            log: 16.8515527778,  // vychozi souradnice
            placeholder_toolbar: null,
            popupWindow: true, // zobrazi okno po kliknuti na marker
            mapsUrl: '/data',
            zoom: 13,
            zoomIconPlus: '/img/in.png',
            zoomIconMinus: '/img/out.png',
            clusterer: { gridSize: 25} // nastaveni shlukovace
        };
        this.opts = $.extend({}, defaults, options);

        this.setTypes(this.opts.types);

        this.createMap();
        if (this.opts.showControls) this.createMapSwitch();

        // vychozi souradnice
        var defalutLatlng = new google.maps.LatLng(this.opts.lat, this.opts.log);
        var me = this;
        var mapOverlay = new google.maps.ImageMapType({
            // turisticka mapa
            getTileUrl: function(tile, zoom) {

                if (
				  (zoom == 10 && (tile.x >= 558 && tile.x <= 560 && tile.y >= 350 && tile.y <= 353))
				  ||
				  (zoom == 11 && (tile.x >= 1117 && tile.x <= 1121 && tile.y >= 701 && tile.y <= 706))
				  ||
				  (zoom == 12 && ((tile.x >= 2235 && tile.x <= 2242) && (tile.y >= 1401 && tile.y <= 1413)))) {
                    return me.opts.mapsUrl + "/zoom" + zoom + "/" + tile.x + "/" + tile.y + ".png";
                }
                else if ((zoom == 13 && (tile.x >= 4471 && tile.x <= 4485 && tile.y >= 2803 && tile.y <= 2827))
				  ||
				  (zoom == 14 && (tile.x >= 8942 && tile.x <= 8971 && tile.y >= 5607 && tile.y <= 5654))
				  ||
				  (zoom == 15 && (tile.x >= 17884 && tile.x <= 17943 && tile.y >= 11213 && tile.y <= 11308))
				  ||
				  (zoom == 16 && (tile.x >= 35769 && tile.x <= 35887 && tile.y >= 22425 && tile.y <= 22615))) {
                    return me.opts.mapsUrl + "/zoom" + zoom + "/" + tile.x + "/" + tile.x + '-' + tile.y + ".png";
                }
                return "http://mt1.google.com/vt/lyrs=m@127&hl=cs&x=" + tile.x + "&y=" + tile.y + "&z=" + zoom + "&s=";
            },
            tileSize: new google.maps.Size(256, 256),
            maxZoom: 16,
            minZoom: 7,
            isPng: true
        });

        var options = {
            zoom: this.opts.zoom,
            center: defalutLatlng,
            disableDefaultUI: this.opts.customControls,
            scaleControl: true,
            scaleControlOptions: {
                position: google.maps.ControlPosition.BOTTOM_CENTER
            },
            streetViewControl: false,
            mapTypeControl: false,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        }

        this.map = new google.maps.Map(document.getElementById("gmap"), options);
        this.zoom = options.zoom;

        this.map.mapTypes.set('mh', mapOverlay);
        this.map.setMapTypeId('mh');

        this.markerCluster = new MarkerClusterer(this.map, [], this.opts.clusterer);

        // vytvori a zobrazi toolbar s typy
        if (this.opts.showControls && this.opts.placeholder_toolbar != null) modrehory.createToolbar();

        // vytvori vlasni zoomovac
        if (this.opts.customControls) modrehory.createZoom();
    },

    zoomIn: function() {
        if (this.zoom < 16) this.zoom++;
        this.map.setZoom(this.zoom);
    },

    zoomOut: function() {
        if (this.zoom > 7) this.zoom--;
        this.map.setZoom(this.zoom);
    },

    /** prepocitani bodu ve shlukovaci. Nutne volat po pridavani bodu nebo skryti/zobrazeni typu**/
    reloadCluster: function() {
        this.markerCluster.resetViewport();
        this.markerCluster.redraw();
    },

    /** Prida marker do mapy. Nijak bod nerobrazuje.
    @param id (int) id objektu
    @param type (int) typ objektu (sklepy, restaurace...)
    @param lat (float) x GPS souracnice
    @param lng (float) y GPS souracnice
    @param title (string) název objektu
    @param gpx (array) pole body k vykleseni trasy
    @param color (string) barva trasy zadana hexadecimalne (FF0000)
    @param active (bool) otevreno/zavreno/nezname
    @param gpxEncoding (string) kodovani GPX none|base64
    **/
    addMarker: function(id, type, lat, lng, title, gpx, color, active, gpxEncoding) {

        var icon;
        active = active == 1 ? true : false;

        // najde ikonu podle typu, kde je aktivita = null
        for (var i in this.typesData) {
            if (this.typesData[i].MapObjectTypeId == type && this.typesData[i].IsActive == null) {
                icon = this.typesData[i].MapObjectTypeIcon;
            }
            if (this.typesData[i].SubItems) {
                for (var j in this.typesData[i].SubItems) {
                    if (this.typesData[i].SubItems[j].MapObjectTypeId == type && this.typesData[i].SubItems[j].IsActive == null) {
                        icon = this.typesData[i].SubItems[j].MapObjectTypeIcon;
                    }
                }
            }
        }
        // vetsi priorita pokud je shodna ikonka podle typu a aktivity
        for (var i in this.typesData) {
            if (this.typesData[i].MapObjectTypeId == type && this.typesData[i].IsActive == active) {
                icon = this.typesData[i].MapObjectTypeIcon;
            }
            if (this.typesData[i].SubItems) {
                for (var j in this.typesData[i].SubItems) {
                    if (this.typesData[i].SubItems[j].MapObjectTypeId == type && this.typesData[i].SubItems[j].IsActive == active) {
                        icon = this.typesData[i].SubItems[j].MapObjectTypeIcon;
                    }
                }
            }
        }

        var marker = new google.maps.Marker({
            id: id,
            type: type,
            position: new google.maps.LatLng(lat, lng),
            icon: icon,
            title: title,
            flat: true,
            active: active,
            mapvisible: false
        });
        var me = this;

        if (gpx != undefined) {

            var points = new Array();

            if (gpxEncoding == 'base64') var xml = Base64.decode(gpx);
            else var xml = gpx;

            var result = $("<div>" + xml + "</div>").find("rtept").each(function(i) {
                points.push(new google.maps.LatLng($(this).attr('lat'), $(this).attr('lon')));
            });

            var result = $("<div>" + xml + "</div>").find("trkpt").each(function() {
                points.push(new google.maps.LatLng($(this).attr('lat'), $(this).attr('lon')));
            });

            var marker = new google.maps.Marker({
                id: id,
                type: type,
                position: points[0],
                icon: icon,
                title: title,
                flat: true,
                active: active,
                mapvisible: false
            });

            var poly = new google.maps.Polyline({
                path: points,
                start: marker,
                strokeColor: '#' + color,
                strokeOpacity: 1.0,
                strokeWeight: 3
            });
            marker.gpx = poly;


            showInfoWindow = function() {
                marker = poly.start;
                if (marker.html) {
                    me.infowindow.content = '<div id="window">' + marker.html + '</div>';
                }
                /*else {
                me.infowindow.content = '<div id="window">' + marker.title + '<br/><br/><img src="/img/loader.gif"/> </div>';
                me.loadObjectInfo(marker);
                }*/
                me.infowindow.open(this.map, marker);
            };
            if (this.opts.popupWindow) {
                google.maps.event.addListener(poly, 'click', showInfoWindow);
            }
        }

        if (this.opts.popupWindow) {
            google.maps.event.addListener(marker, 'click', function() {
                if (marker.html) {
                    me.infowindow.content = '<div id="window">' + marker.html + '</div>';
                }
                /*else {
                me.infowindow.content = '<div id="window">' + marker.title + '<br/><br/><img src="/img/loader.gif"/> </div>';
                me.loadObjectInfo(marker);
                }*/
                me.infowindow.open(this.map, marker);
            });
        }
        this.markers.push(marker);
        this.markerCluster.addMarker(marker, true);
        return marker;
    },

    /** nazoomuje, aby byly videt vsechny objekty **/
    fitToAllMarkers: function() {
        var r = new google.maps.LatLngBounds();
        this.markers.forEach(function(item) {
            r.extend(item.position);
        });
        this.map.fitBounds(r);
    },

    /** vycentrovani na objekt
    @param marker (Marker)
    **/
    _centerToObject: function(marker) {
        if (marker.gpx == undefined) { // objekt
            modrehory.map.setCenter(marker.position);
        }
        else { // trasa => nazoomuje za zorbazi celou trasu
            var r = new google.maps.LatLngBounds();
            marker.gpx.getPath().forEach(function(item) {
                r.extend(item);
            });
            this.map.fitBounds(r);
        }
    },


    /** Nacte objekty podle typu ze serveru
    @param id (int) id typu
    **/
    loadObjectType: function(id, callback) {
        var me = this;
        var ws_url = this.opts.getMapObjectsUrl + '/' + id;
        $.getJSON(ws_url, function(items) {
            me.loadedType[id] = true;
            var markers = new Array();
            for (var i in items) {
                if (me.getObject(items[i].id) == false) {
                    var marker = me.addMarker(items[i].MapObjectId, items[i].MapObjectTypeId, items[i].Latitude, items[i].Longitude, items[i].ObjectName, items[i].GPX, items[i].Color, items[i].IsActive, items[i].GPXEncoding);
                    marker.html = Base64.decode(items[i].Description);
                    markers.push(marker);
                }
            }
            if (callback != undefined) callback.call(me, markers);
        });
    },

    /** Nacte objekt ze serveru
    @param id (int) ID objektu
    **/
    loadObject: function(id, callback) {
        var ws_url = this.opts.getMapObjectUrl + '/' + id;
        var me = this;
        $.getJSON(ws_url, function(item) {
            marker = modrehory.addMarker(item.MapObjectId, item.MapObjectTypeId, item.Latitude, item.Longitude, item.ObjectName, item.GPX, item.Color, item.IsActive, item.GPXEncoding);
            marker.html = Base64.decode(item.Description);
            callback.call(me, marker);
        });
    },

    /** Metoda, ktera nacte objekt do pameti z webove sluzby.
    Jedna se asynchroni (AJAX) volání, takže lze použit callback pro zachycení výsledku
    @param objectId (int) id objektu
    @param callback (function) callbackova funce, ktere se preda vysledek, jakmile je k dispozici
    */
    getMapObject: function(objectId, callback) {
        var marker = this.getObject(objectId);
        if (marker != false) { // objekt je jiz v pameti
            if (callback != undefined) callback.call(this, marker);
        }
        else {
            // nacte z webove sluzby
            this.loadObject(objectId, function(marker) {
                if (callback != undefined) callback.call(this, marker);
            });
        }
        return marker;
    },

    /** Metoda, ktera nacte objekty podle typu do pameti z webove sluzby.
    Jedna se asynchroni (AJAX) volání, takže lze použit callback pro zachycení výsledku
    @param typeId (int) id typu
    @param callback (function) callbackova funkce, ktere se preda vysledek, jakmile je k dispozici
    */
    getMapObjects: function(objectTypeId, callback) {
        var markers = new Array();
        if (this.loadedType[objectTypeId]) {
            this.markers.forEach(function(item) {
                if (item.type == objectTypeId) markers.push(item);
            });
            if (callback != undefined) callback.call(this, markers);
        }
        else {
            me = this;
            this.loadObjectType(objectTypeId, function(markers) {
                if (callback != undefined) callback.call(this, markers);
            });
        }
    },


    /** Zobrazi objekty podle typu
    @param id (int) id typu
    @param onlyActive (bool) pouze otevrene/zavrene
    **/
    showType: function(objectTypeId, onlyActive) {
        if (this.loadedType[objectTypeId]) {
            this.markers.forEach(function(item) {
                if (item.type == objectTypeId && (onlyActive == undefined || onlyActive == 'null' || item.active == onlyActive)) {
                    item.setMapVisible(true);
                }
            });
            this.reloadCluster();
        }
        else {
            me = this;
            this.loadObjectType(objectTypeId, function() {
                me.showType(objectTypeId, onlyActive);
            });
        }
    },

    /** Zobrazi vsechny objekty **/
    showAll: function() {
        this.markers.forEach(function(item) {
            item.setMapVisible(true);
        });
        this.reloadCluster();
    },

    /** Zobrazi objekt podle id
    @param objectId (int) id objektu
    **/
    showObject: function(objectId) {
        var item = this.getObject(objectId);
        if (item != false) {
            item.setMapVisible(true);
            this.reloadCluster();
        }
        else {
            this.loadObject(objectId, function(marker) {
                modrehory.infowindow.setContent('<div id="window">' + marker.html + '</div>');
                marker.setMapVisible(true);
                modrehory.reloadCluster();
            });
        }
    },

    /** Zobrazi objekty podle typu
    @param arr (Array) zobrazeni vice objektu
    **/
    showObjects: function(arr) {
        for (var id in arr) {
            this.showObject(arr[id]);
        }
        this.reloadCluster();
    },

    /** vycentrovani na objekt
    @param objectId (int) id objektu
    **/
    centerToObject: function(objectId) {
        var item = this.getObject(objectId);
        if (item != false) {
            this._centerToObject(item);
        }
        else {
            this.loadObject(objectId, function(marker) {
                this._centerToObject(marker);
            });
        }
    },


    /** skryje objekty podle typu
    @param objectTypeId (int) id typu
    @param onlyActive (bool) pouze otevrene/zavrene
    **/
    hideType: function(objectTypeId, onlyActive) {
        this.markers.forEach(function(item) {
            if (item.type == objectTypeId && (onlyActive == undefined || onlyActive == 'null' || item.active == onlyActive)) {
                item.setMapVisible(false);
            }
        })
        this.reloadCluster();
    },

    /** skryje objekty podle typu
    @param objectTypeId (int) id typu
    **/
    hideObject: function(objectId) {
        this.markers.forEach(function(item) {
            if (item.id == objectId) item.setMapVisible(false);
        })
        this.reloadCluster();
    },

    /** skryje vsechny objekty **/
    hideAll: function() {
        this.markers.forEach(function(item) {
            item.setMapVisible(false);
        })
        this.reloadCluster();
    },

    /** existuje objekt v pameti? Vrati false nebo existujici objekt
    @param objectId (int) id objektu
    **/
    getObject: function(objectId) {
        var exists = false;
        this.markers.forEach(function(item) {
            if (item.id == objectId) exists = item;
        })
        return exists;
    }
}


/** Nastaveni zobrazeni bodu na mape. Protoze se o fyzicke zobrazeni bodu stara MarkerClusterer,
	ktery neni soucasti GoogleMaps API, tak jsem misto Marker:setVisible implementoval Marker:setMapVisible
**/
google.maps.Marker.prototype.setMapVisible = function(b) {
	this.mapvisible = b;
	if(this.gpx != undefined) {
		if(b) this.gpx.setMap(modrehory.map);
		else this.gpx.setMap();
	}
}
