var GMA = function () {
}

var GMABounds = function () {
    
}

GMABounds.prototype = {
    swlat: null,
    swlon: null,
    nelat: null,
    nelon: null,
    
    init: function () {
        this.swlat = null;
        this.swlon = null;
        this.nelat = null;
        this.nelon = null;
    },
    
    extend: function (lat, lon) {
        if (this.swlat == null || this.swlon == null || this.nelat == null || this.nelon == null) {
            this.swlat = lat;
            this.swlon = lon;
            this.nelat = lat;
            this.nelon = lon;
            return;
        }
        
        if (lat > this.nelat) {
            this.nelat = lat;
        }
        if (lon < this.swlon) {
            this.swlon = lon;
        }
        
        if (lat < this.swlat) {
            this.swlat = lat;
        }
        if (lon > this.nelon) {
            this.nelon = lon;
        }
    },
    
    extendPoint: function (point) {
        this.extend(point.lat(), point.lng());
    },
    
    getGLatLngBoundsCenter: function () {
        var lat = this.swlat + (this.nelat - this.swlat) / 2;
        var lon = this.swlon + (this.nelon - this.swlon) / 2;
        return new GLatLng(lat, lon);
    },
    
    getZoomLevel: function (map) {
        //return map.getBoundsZoomLevel(new GLatLngBounds(new GLatLng(this.ltlat, this.ltlon), new GLatLng(this.rblat, this.rblon)));
        if (!this.nelat) {
            return 1;
        }
        if (this.nelat == this.swlat && this.nelon == this.swlon) {
            return 9;
        }
        
        var zoom = 18;
        while (zoom > 1) {
            map.setZoom(zoom);
            var bounds = map.getBounds();
            if (this.swlat > bounds.getSouthWest().lat() && this.swlon > bounds.getSouthWest().lng()
            && this.nelat < bounds.getNorthEast().lat() && this.nelon < bounds.getNorthEast().lng()) {
                //zoom++;
                break;
            }
            zoom--;
        }
        return zoom;
    }
};

GMA.prototype = {
    map: null,
    geocoder: null,
    places: new Array(),
    markers: new Array(),
    mapContainer: null,
    locationsContainer: null,
    submitUrl: null,
    userPointsUrl: null,
    quickFormsContainer: null,
    relativeUrl: null,

    loggedUserId: null,
    loggedUserMarkerUrl: null,
    defaultMarkerUrl: null,
    shadowMarkerUrl: null,
    
    registerHugUrl: null,
    
    setDefaultMarkerUrl: function (url) {
        this.defaultMarkerUrl = url;
    },
    setShadowMarkerUrl: function (url) {
        this.shadowMarkerUrl = url;
    },
    
    setRelativeUrl: function (url) {
        this.relativeUrl = url;
    },
    
    setLoggeddata: function (userId, markerUrl) {
        this.loggedUserId = userId;
        this.loggedUserMarkerUrl = markerUrl;
    },
    
    setSubmitUrl: function (url) {
        this.submitUrl = url;
    },
    setRegisterHugUrl: function (url) {
        this.registerHugUrl = url;
    },
    setQuickFormsContainer: function (quickFormsContainer) {
        this.quickFormsContainer = quickFormsContainer;
    },
    
    setUserPointsUrl: function (url) {
        this.userPointsUrl = url;
    },
    
    setMapContainer: function (mapContainer) {
        this.mapContainer = mapContainer;
    },
    
    setLocationsContainer: function (locationsContainer) {
        this.locationsContainer = locationsContainer;
    },
    
    showMap: function() {
        this.map = createMap(this.mapContainer);
        this.geocoder = new GClientGeocoder();
    },
    
    getLocations: function(response) { // static callback
        Gma.markers = new Array();
        Gma.places = new Array();
        Gma.map.clearOverlays();
        if (!response || response.Status.code != 200) {
            alert("Sorry, we were unable to geocode that address");
        } else {
            var places = response.Placemark;
            Gma.places = places;
            for (var i = 0; i < places.length; i++) {
                var place = places[i];
                var location = place.address;
                var lat = place.Point.coordinates[1];
                var lon = place.Point.coordinates[0];
                
                var point = new GLatLng(lat, lon);
                
                var markerOptions = createMarkerOptions(Gma.defaultMarkerUrl, Gma.shadowMarkerUrl);
                markerOptions.draggable = true;
                Gma.markers[i] = new GMarker(point, markerOptions);

                Gma.map.addOverlay(Gma.markers[i]);
                Gma.markers[i].bindInfoWindowHtml(location);

                GEvent.addListener(Gma.markers[i], "dragend", function (latLng) {
                    Gma.refreshLocation();
                });
            }
        }
        Gma.showPlaces();
    },
    
    refreshLocation: function () {
        for (var i = 0; i < Gma.markers.length; i++) {
            if (Gma.markers[i].getLatLng().lat() != Gma.places[i].Point.coordinates[1] && Gma.markers[i].getLatLng().lng() != Gma.places[i].Point.coordinates[0]) {
                Gma.places[i].Point.coordinates[1] = Gma.markers[i].getLatLng().lat();
                Gma.places[i].Point.coordinates[0] = Gma.markers[i].getLatLng().lng();
                Gma.geocoder.getLocations(new GLatLng(Gma.markers[i].getLatLng().lat(), Gma.markers[i].getLatLng().lng()), this.getLocation);
            }
        }
    },
    
    getLocation: function (response) {
        if (!response || response.Status.code != 200) {
            alert("Sorry, we were unable to geocode that address");
        } else {
            var place = response.Placemark[0];
            var lat = place.Point.coordinates[1];
            var lon = place.Point.coordinates[0];
            for (var i = 0; i < Gma.markers.length; i++) {
                var str = sprintf('%.6f', Gma.places[i].Point.coordinates[1]) + ',' + sprintf('%.6f', Gma.places[i].Point.coordinates[0]);
                if (response.name == str) {
                    Gma.places[i] = place;
                    break;
                }
            }
        }
        Gma.showPlaces();
    },
    
    showLocations: function(address) {
        this.geocoder.getLocations(address, this.getLocations);
    },
    
    showPlaces: function () {
        var links = '';
        var bounds = null;
        if (this.places.length >=2 ) {
            bounds = new GMABounds();
        }
        for (var i = 0; i < this.places.length; i++) {
            var place = this.places[i];
            var country = place.AddressDetails.Country.CountryNameCode;
            var street = '';
            var city = '';
            var zip = '';
            var state = '';
            var lat = place.Point.coordinates[1];
            var lon = place.Point.coordinates[0];
            var locality = null;
            
            if (bounds) {
                bounds.extend(lat, lon);
            }
            
            try {
                street = place.AddressDetails.Country.SubAdministrativeArea.Locality.Thoroughfare.ThoroughfareName;
            } catch (err) {
                try {
                    street = place.AddressDetails.Country.SubAdministrativeArea.Locality.DependentLocality.Thoroughfare.ThoroughfareName;
                } catch (err) {
                    try {
                        street = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.Thoroughfare.ThoroughfareName;
                    } catch (err) {
                        try {
                            street = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.DependentLocality.Thoroughfare.ThoroughfareName;
                        } catch (err) {
                            try {
                                street = place.AddressDetails.Country.AdministrativeArea.Locality.Thoroughfare.ThoroughfareName;
                            } catch (err) {
                                try {
                                    street = place.AddressDetails.Country.AdministrativeArea.Locality.DependentLocality.Thoroughfare.ThoroughfareName;
                                } catch (err) {
                                    try {
                                        street = place.AddressDetails.Country.Locality.Thoroughfare.ThoroughfareName;
                                    } catch (err) {
                                        try {
                                            street = place.AddressDetails.Country.Locality.DependentLocality.Thoroughfare.ThoroughfareName;
                                        } catch (err) {}
                                    }
                                }
                            }
                        }
                    }
                }
            }
            
            try {
                locality = place.AddressDetails.Country.SubAdministrativeArea.Locality;
                city = place.AddressDetails.Country.SubAdministrativeArea.Locality.LocalityName;
                state = place.AddressDetails.Country.AdministrativeAreaName;
            } catch (err) {
                try {
                    locality = place.AddressDetails.Country.SubAdministrativeArea.Locality.DependentLocality;
                    city = place.AddressDetails.Country.SubAdministrativeArea.Locality.DependentLocality.LocalityName;
                    state = place.AddressDetails.Country.AdministrativeAreaName;
                } catch (err) {
                    try {
                        locality = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality;
                        city = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName;
                        state = place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName;
                    } catch (err) {
                        try {
                            locality = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.DependentLocality;
                            city = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.DependentLocality.LocalityName;
                            state = place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName;
                        } catch (err) {
                            try {
                                locality = place.AddressDetails.Country.AdministrativeArea.Locality;
                                city = place.AddressDetails.Country.AdministrativeArea.Locality.LocalityName;
                            } catch (err) {
                                try {
                                    locality = place.AddressDetails.Country.AdministrativeArea.Locality.DependentLocality;
                                    city = place.AddressDetails.Country.AdministrativeArea.Locality.DependentLocality.LocalityName;
                                } catch (err) {
                                    try {
                                        locality = place.AddressDetails.Country.Locality;
                                        city = place.AddressDetails.Country.Locality.LocalityName;
                                    } catch (err) {
                                        try {
                                            locality = place.AddressDetails.Country.Locality.DependentLocality;
                                            city = place.AddressDetails.Country.Locality.DependentLocality.LocalityName;
                                        } catch (err) {}
                                    }
                                }
                            }
                        }
                    }
                }
            }
            
            if (locality) {
                try {
                    zip = locality.DependentLocality.PostalCode.PostalCodeNumber;
                } catch (err) {
                    try {
                        zip = locality.PostalCode.PostalCodeNumber;
                    } catch (err) {}
                }
            }
            if (!state) {
                state = '';
            }
            url = '&country=' + country + '&city=' + city + '&state=' + state + '&street=' + street + '&zip=' + zip + '&lat=' + lat + '&lon=' + lon;
            links = links + '<tr><td>' + place.address + '</td><td><a href="#" onclick="Gma.showMarker(' + i + '); return false;">show</a></td><td><a href="' + this.submitUrl + '" onclick="loadData(this.href, \'' + this.quickFormsContainer.attr('id') + '\'); $(\'#registerHugForm\').attr(\'action\', \'' + this.registerHugUrl + url + '\'); confirmedPoint = true; return false;"><span class="confirm">Confirm</span></a></td></tr>';
        }
        
        // set centered
        if (this.places.length == 1) {
            this.showMarker(0);
        } else if (this.places.length) {
            this.map.setCenter(bounds.getGLatLngBoundsCenter());
            this.map.setZoom(bounds.getZoomLevel(this.map));
        } else {
            this.map.setCenter(new GLatLng(34, 0), 1);
            this.map.setZoom(1);
        }
        if (links) {
            links = '<table class="hugs-table"><tr><th>Location</th><th>Show</th><th>Select</th></tr>' + links + '</table>';
        }
        this.locationsContainer.innerHTML = links;
    },
    
    showMarker: function (index) {
        this.map.setCenter(new GLatLng(this.places[index].Point.coordinates[1], this.places[index].Point.coordinates[0]), 1);
        this.map.setZoom(9);
    },
    
    showPolyline: function (points) {
        var pointsLine = new Array();
        
        var bounds = null;
        if (points.length >= 2) {
            bounds = new GMABounds();
        }
        
        for (var i = 0; i < points.length; i++) {
            if (bounds) {
                bounds.extend(points[i].lat, points[i].lon);
            }
            
            var point = new GLatLng(points[i].lat, points[i].lon, true);
            
            var image = '';
            if (this.loggedUserId == points[i].fromUser || this.loggedUserId == points[i].toUser) {
                image = this.loggedUserMarkerUrl;
            } else {
                image = this.defaultMarkerUrl;
            }
            var markerOptions = createMarkerOptions(image, this.shadowMarkerUrl);
            var marker = new GMarker(point, markerOptions);
            
            marker.bindInfoWindowHtml(points[i].location);
            this.map.addOverlay(marker);
            pointsLine.push(point);
        }
        var rand = Math.random();
        var color = null;
        if (rand < 0.3) {
            color = '#ff0000';
        } else if (rand < 0.6) {
            color = '#00ff00';
        } else {
            color = '#0000ff';
        }
        var poly = new GPolyline(pointsLine, color, 1, 1, { geodesic: true });
        this.map.addOverlay(poly);
        
        if (points.length == 1) {
            this.map.setCenter(new GLatLng(points[0].lat, points[0].lon));
            this.map.setZoom(9);
        } else if (points.length) {
            this.map.setCenter(bounds.getGLatLngBoundsCenter());
            this.map.setZoom(bounds.getZoomLevel(this.map));
        } else {
            this.map.setCenter(new GLatLng(34, 0), 1);
            this.map.setZoom(1);
        }
    },
    
    showPointsToUser: function (points) {
        var markers = new Array();
        var links = '';
        var bounds = null;
        if (points.length >=2 ) {
            bounds = new GMABounds();
        }

        for (var i = 0; i < points.length; i++) {
            var point = new GLatLng(points[i].lat, points[i].lon, true);
            if (this.loggedUserId == points[i].toUser) {
                var markerOptions = createMarkerOptions(this.loggedUserMarkerUrl, this.shadowMarkerUrl);
                var marker = new GMarker(point, markerOptions);
            } else {
                var marker = new GMarker(point);
            }
            
            GEvent.addListener(marker, "mouseover", function (latLng) {
                Gma.showInfoWindow(latLng, points, markers);
            });
            GEvent.addListener(marker, "mouseout", function (latLng) {
                Gma.closeInfoWindow(latLng, points, markers);
            });
            
            var toUser = points[i].toUser;
            markers[i] = marker;
            this.map.addOverlay(marker);
            
            if (bounds) {
                bounds.extend(points[i].lat, points[i].lon);
            }
            
            if (points[i].toUser) {
                GEvent.addListener(marker, "click", function (latLng) {
                    Gma.showNewPointsToUser(latLng, points, markers, new Array());
                });
            }
            
            links = links + '<tr><td>' + (points[i].toUser ? '<a href="' + this.relativeUrl + '/user/' + points[i].user + '">' + points[i].user + '</a>' : '[none]') + '</td><td>' + points[i].date + '</td><td>' + points[i].location + '</td></tr>';
        }
        
        if (points.length == 1) {
            this.map.setCenter(new GLatLng(points[0].lat, points[0].lon));
            this.map.setZoom(9);
        } else if (points.length) {
            this.map.setCenter(bounds.getGLatLngBoundsCenter());
            this.map.setZoom(bounds.getZoomLevel(this.map));
        } else {
            this.map.setCenter(new GLatLng(34, 0), 1);
            this.map.setZoom(1);
        }
        
        if (links) {
            links = '<table class="hugs-table"><tr><th>Person hugged</th><th>Date</th><th>Place</th></tr>' + links + '</table>';
        }
        this.locationsContainer.innerHTML = links;
    },
    
    showInfoWindow: function (latLng, points, markers) {
        for (var i = 0; i < points.length; i++) {
            var ll = markers[i].getLatLng();
            if (ll.lat() == latLng.lat() && ll.lng() == latLng.lng()) {
                markers[i].openInfoWindow(points[i].fromUser + ' and ' + points[i].user + '<br>' + points[i].location + '<br>' + points[i].date + (points[i].comment ? '<br>' + points[i].comment : ''));
            }
        }
    },
    
    closeInfoWindow: function (latLng, points, markers) {
        for (var i = 0; i < points.length; i++) {
            var ll = markers[i].getLatLng();
            if (ll.lat() == latLng.lat() && ll.lng() == latLng.lng()) {
                markers[i].closeInfoWindow();
            }
        }
    },
    
    showNewPointsToUser: function (latLng, points, markers, lines) {
        var removeOverlays = [];
        // remove old lines
        if (lines) {
            for (var i = 0; i < lines.length; i++) {
                var bounds1 = lines[i].getVertex(0);
                var bounds2 = lines[i].getVertex(1);
                if (!((bounds1.lat() == latLng.lat() && bounds1.lng() == latLng.lng()) || (bounds2.lat() == latLng.lat() && bounds2.lng() == latLng.lng()))) {
                    removeOverlays.push(lines[i]);
                    //this.map.removeOverlay(lines[i]);
                }
            }
        }

        // remove old points
        for (var i = 0; i < points.length; i++) {
            var ll = markers[i].getLatLng();
            if (ll.lat() == latLng.lat() && ll.lng() == latLng.lng()) {
                var showedUser = points[i].user;
                var url = this.userPointsUrl + '?id=' + points[i].toUser;
                $.ajax({
                    url: url,
                    dataType: "json",
                    success: function (data, textStatus) {
                        Gma.showLinesToNewPoints(latLng, data, removeOverlays, showedUser);
                    }
                });
            } else {
                removeOverlays.push(markers[i]);
                //this.map.removeOverlay(markers[i]);
            }
        }
    },
    
    showLinesToNewPoints: function (latLng, points, removeOverlays, showedUser) {
        if (!points.length) {
            return;
        } else {
            for (var i = 0; i < removeOverlays.length; i++) {
                this.map.removeOverlay(removeOverlays[i]);
            }
        }
        var markers = new Array();
        var lines = new Array();
        var links = '';
        
        var bounds = new GMABounds();
        bounds.extend(latLng.lat(), latLng.lng());
        for (var i = 0; i < points.length; i++) {
            var point = new GLatLng(points[i].lat, points[i].lon, true);
            if (this.loggedUserId == points[i].toUser) {
                var markerOptions = createMarkerOptions(this.loggedUserMarkerUrl, this.shadowMarkerUrl);
                var marker = new GMarker(point, markerOptions);
            } else {
                var marker = new GMarker(point);
            }
            GEvent.addListener(marker, "mouseover", function (latLng) {
                Gma.showInfoWindow(latLng, points, markers);
            });
            GEvent.addListener(marker, "mouseout", function (latLng) {
                Gma.closeInfoWindow(latLng, points, markers);
            });
            var toUser = points[i].toUser;
            markers[i] = marker;
            this.map.addOverlay(marker);
            
            bounds.extend(points[i].lat, points[i].lon);
            
            var pointsLine = new Array();
            pointsLine.push(latLng);
            pointsLine.push(marker.getLatLng());
            
            var rand = Math.random();
            var color = null;
            if (rand < 0.3) {
                color = '#ff0000';
            } else if (rand < 0.6) {
                color = '#00ff00';
            } else {
                color = '#0000ff';
            }
            var poly = new GPolyline(pointsLine, color, 1, 1, { geodesic: true });
            
            this.map.addOverlay(poly);
            lines[i] = poly;
            
            if (points[i].toUser) {
                GEvent.addListener(marker, "click", function (latLng) {
                    Gma.showNewPointsToUser(latLng, points, markers, lines);
                });
            }
            links = links + '<tr><td>' + (points[i].toUser ? '<a href="' + this.relativeUrl + '/user/' + points[i].user + '">' + points[i].user + '</a>' : '[none]') + '</td><td>' + points[i].date + '</td><td>' + points[i].location + '</td></tr>';
        }

        this.map.setCenter(bounds.getGLatLngBoundsCenter());
        this.map.setZoom(bounds.getZoomLevel(this.map));
        
        if (links) {
            links = '<table class="hugs-table"><tr><th colspan="3">Hugs for user "' + showedUser + '"</th></tr><tr><th>Person hugged</th><th>Date</th><th>Place</th></tr>' + links + '</table>';
        }
        this.locationsContainer.innerHTML = links;
    },
    
    showNewMarker: function (lat, lon, location, fromUser, toUser) {
        var point = new GLatLng(lat, lon);
        
        var image = '';
        if (this.loggedUserId == fromUser || this.loggedUserId == toUser) {
            image = this.loggedUserMarkerUrl;
        } else {
            image = this.defaultMarkerUrl;
        }
        var markerOptions = createMarkerOptions(image, this.shadowMarkerUrl);
        var marker = new GMarker(point, markerOptions);
        
        marker.bindInfoWindowHtml(location);
        this.map.addOverlay(marker);
        this.map.setCenter(new GLatLng(lat, lon, 1));
        this.map.setZoom(9);
    }
};

function createMap(container) {
    var map = new GMap2(container);
    map.setCenter(new GLatLng(34, 0), 1);
    map.addControl(new GLargeMapControl());
    map.addControl(new GLargeMapControl());
    map.addControl(new GMapTypeControl());
    return map;
}

function createMarkerOptions(image, shadow) {
    var userIcon = new GIcon(G_DEFAULT_ICON);
    userIcon.image = image;
    userIcon.shadow = shadow;
    userIcon.iconSize = new GSize(20, 19);
    userIcon.shadowSize = new GSize(50, 24);
    return { icon: userIcon };
}

Gma = new GMA();

function run_script(_div) {
    if (_div) {
    	var divContent = _div.childNodes;
    	if (divContent) {
        	for (var i = 0; i < divContent.length; i++) {
        		var requestItem = divContent.item(i);
        		if (requestItem.tagName) {
                    if (requestItem.tagName.toUpperCase() == 'SCRIPT') {
                        eval(requestItem.text);
                    }
                    else run_script(requestItem);
        		}
        	}
    	}
    }
}


var submitForm = function (idForm, idContainer) {
    var data = $("#" + idForm).serialize();
    var action = $("#" + idForm).attr('action');
    
    $("#" + idContainer).html('<div style="text-align: center"><b>Please wait...</b></div>');
    $.post(
        action,
        data,
        function(output){
            $("#" + idContainer).html(output);
            run_script($("#" + idContainer));
        }
    );
}

var loadScript = function (url, idContainer) {
    $('#' + idContainer).html('<div style="text-align: center"><b>Please wait...</b></div>');
    $.get(
        url,
        [],
        function(output) {},
        "script"
    );
}

var loadData = function (url, idContainer) {
    $('#' + idContainer).html('<div style="text-align: center"><b>Please wait...</b></div>');
    $.get(
        url,
        function(output){
            $('#' + idContainer).html(output);
            run_script($("#quickForms"));
        }
    );
}

function str_repeat(i, m) { for (var o = []; m > 0; o[--m] = i); return(o.join('')); }

function sprintf () {
  var i = 0, a, f = arguments[i++], o = [], m, p, c, x;
  while (f) {
    if (m = /^[^\x25]+/.exec(f)) o.push(m[0]);
    else if (m = /^\x25{2}/.exec(f)) o.push('%');
    else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) {
      if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) throw("Too few arguments.");
      if (/[^s]/.test(m[7]) && (typeof(a) != 'number'))
        throw("Expecting number but found " + typeof(a));
      switch (m[7]) {
        case 'b': a = a.toString(2); break;
        case 'c': a = String.fromCharCode(a); break;
        case 'd': a = parseInt(a); break;
        case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break;
        case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break;
        case 'o': a = a.toString(8); break;
        case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break;
        case 'u': a = Math.abs(a); break;
        case 'x': a = a.toString(16); break;
        case 'X': a = a.toString(16).toUpperCase(); break;
      }
      a = (/[def]/.test(m[7]) && m[2] && a > 0 ? '+' + a : a);
      c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' ';
      x = m[5] - String(a).length;
      p = m[5] ? str_repeat(c, x) : '';
      o.push(m[4] ? a + p : p + a);
    }
    else throw ("Huh ?!");
    f = f.substring(m[0].length);
  }
  return o.join('');
}