var map, mc, windowFeed, icons;

var ICONS = [
    new GIcon({
        image: 'icons/circle-star.png',
        iconSize: new GSize(16, 16),
        iconAnchor: new GPoint(8, 8),
        infoWindowAnchor: new GPoint(8, 8)
    }),
    new GIcon({
        image: 'icons/circle-red.png',
        iconSize: new GSize(16, 16),
        iconAnchor: new GPoint(8, 8),
        infoWindowAnchor: new GPoint(8, 8)
    }),
    new GIcon({
        image: 'icons/circle-skyblue.png',
        iconSize: new GSize(16, 16),
        iconAnchor: new GPoint(8, 8),
        infoWindowAnchor: new GPoint(8, 8)
    }),
    new GIcon({
        image: 'icons/circle-blue.png',
        iconSize: new GSize(16, 16),
        iconAnchor: new GPoint(8, 8),
        infoWindowAnchor: new GPoint(8, 8)
    }),
    new GIcon({
        image: 'icons/circle-darkblue.png',
        iconSize: new GSize(16, 16),
        iconAnchor: new GPoint(8, 8),
        infoWindowAnchor: new GPoint(8, 8)
    }),
    new GIcon({
        image: 'icons/circle-green.png',
        iconSize: new GSize(16, 16),
        iconAnchor: new GPoint(8, 8),
        infoWindowAnchor: new GPoint(8, 8)
    }),
    new GIcon({
        image: 'icons/circle-black.png',
        iconSize: new GSize(16, 16),
        iconAnchor: new GPoint(8, 8),
        infoWindowAnchor: new GPoint(8, 8)
    }),
    new GIcon({
        image: 'icons/obs.png',
        iconSize: new GSize(24, 24),
        iconAnchor: new GPoint(12, 20),
        infoWindowAnchor: new GPoint(12, 20)
    })
];

function _(str) {
    return LANG[str] || str;
}

function parseJSON(txt) {
    if (typeof (this.JSON) === 'object' &&
        typeof (this.JSON.parse) === 'function') {
        return this.JSON.parse(txt);
    } else {
        return eval('('+txt+')');
    }
    
}

function zoomGhStr(zoom, pt) {
    return GX._tr.charAt(zoom)+GX.encode(pt.lat(), pt.lng(), 16+2*zoom)
}

function WhoSThereControl() {

}

WhoSThereControl.prototype.printable = function () {
    return false;
};

WhoSThereControl.prototype.selectable = function () {
    return false;
};

WhoSThereControl.prototype.initialize = function (map) {
    var jbutton, jpane;
    jbutton = this.jbutton = $('<div class="fmcontrol"></div>').text(_("Кто здесь?.."));
    jpane = this.jpane = $('#srchoutput');

    jbutton.click(function () {
        map.savePosition();
        var ext = map.getBounds(), i, ul = $('<ul class="srchres"></ul>');
        var found = 0;
        jpane.empty();
        for (i = icons.length-1; i; --i) {
            var pt, gh;
            if (ext.contains(pt = icons[i].getPoint())) {
                found += 1;
                (function (m, pt, zoom) {
                ul.append($('<li></li>')
                          .append($('<a></a>')
                                  .attr('href', '#'+zoomGhStr(zoom, pt))
                                  .text(icons[i].getTitle())
                                  .click(function (evt) {
                                      map.setCenter(pt, zoom);
                                      clickListener(m, pt, null);
                                      evt.stopPropagation();

                                      return true;
                                  })
                                 )
                          .append($('<br></br>'))
                          .append($('<span></span>').text(deg2hms(pt.lat())
                                                          +' '
                                                          +deg2hms(pt.lng()))));
                }(icons[i], pt, Number(pts[i].z)));
            }
        }
        jpane.append(found
                     ? ul
                     : $('<div class="srchnthng"></div>').text(
                         _("Ничего не найдено...")));
        $("#srchspace").fadeIn("fast");
    });

    map.getContainer().appendChild(jbutton.get(0));
    return jbutton.get(0);
};

WhoSThereControl.prototype.getDefaultPosition = function () {
    return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(220, 7));
};

// This method is not documented, but some buggy version of Google
// Maps API require it.
WhoSThereControl.prototype.allowSetVisibility = function () {
    return false;
};

function trim(str) {
    return str.replace(/(^\s+)|(\s+$)/g, "");
}

function deg2hms(deg) {
    var h = Math.floor(deg);
    deg = 60*(deg - h);
    var m = Math.floor(deg);
    deg = 60*(deg - m);
    var s = Math.floor(deg);

    return String(h)+'°'+m+"'"+s+'"';
}

function addPoint(pt, form) {
    map.closeInfoWindow();
    var txt = trim($('.inp', form).val());
    $.ajax({
        url: 'ajax',
        type: 'POST',
        data: {
            cmd: 'insert',
            lat: String(pt.lat()),
            lon: String(pt.lng()),
            txt: txt,
            zoom: Math.min(20, map.getZoom())
        },
        success: function(msg) {
            try {
                var resp = parseJSON(msg);
                if (resp.status === 'OK') {
                    var m = createMarker(pt, resp.id, txt, true);
                    icons.push(m);
                    mc.addMarker(m);
                    //map.addOverlay(m);
                } else {
                    // TODO Report error
                }
            } catch (e) {
                // TODO Report error
                if (window.console) {
                    window.console.debug('Exception in addPoint:', e);
                }
            }
        }
    });
    return false;
}

function updPoint(marker, form) {
    map.closeInfoWindow();
    var txt = trim($('.inp', form).val());
    $.ajax({
        url: 'ajax',
        type: 'POST',
        data: {
            cmd: 'updattr',
            id: marker.frid,
            txt: txt
        },
        success: function(msg) {
            var idx = jQuery.inArray(marker, icons), m;
            mc.removeMarker(marker);
//             map.removeOverlay(marker);
            mc.addMarker(m = createMarker(marker.getPoint(), marker.frid, txt, true));
            if (idx >= 0) {
                icons[idx] = m;
            } else {
                icons.push(m);
            }
        }
    });
    return false;
}

function cancel() {
    map.closeInfoWindow();
    return false;
}

function delPoint(marker) {
    map.closeInfoWindow();
    marker.disableDragging();
    marker.hide();
    $.ajax({
        url: 'ajax',
        type: 'POST',
        data: {
            cmd: 'del',
            id: marker.frid
        },
        success: function(msg) {
            mc.removeMarker(marker);
        }
    });
    return false;
}

function createMarker(pt, id, txt, draggable) {
    var mark = new GMarker(pt, {
        title: txt,
        icon: ICONS[0],
        draggable: draggable,
        bouncy: true
    });
    mark.frid = id;
    if (draggable) {
        GEvent.addListener(mark, 'dragend', dragListener);
    }
    return mark;
}


function dragListener() {
    var pt = this.getPoint();
    $.ajax({
        url: 'ajax',
        type: 'POST',
        data: {
            cmd: 'updgeom',
            id: this.frid,
            lat: String(pt.lat()),
            lon: String(pt.lng()),
            zoom: Math.min(20, map.getZoom())
        }
    });
    return false;
}

function clickListener(overlay, pt) {
    var marker = overlay, form, okFunc;
    function keyHandler(e) {
        var code = (e.keyCode ? e.keyCode : e.which);
        if (code == 13 && e.originalTarget.tagName != 'BUTTON') {
            okFun();
        } else if (code == 27) cancel();
    }
    if (arguments.length == 3) {
        // Click on marker
        if (marker.draggable && marker.draggable()) {
            pt = marker.getPoint();
            form = $('<div></div')
                .append($('<p class="pointid"></p>').text('#'+(marker.frid || '')))
                .append($(_("<div class='req'>Ваше имя или название места:<br /></div>")))
                .append($(_('<input class="inp"></input>'))
                        .attr('value', marker.getTitle())
                        .keyup(function () {
                            // Disable OK button if value is empty
                            $('.ok', form).attr('disabled', trim($(this).val()).length == 0 ? 'disabled' : null);
                        }))
                .append($('<p class="coords"></p>').text(deg2hms(pt.lat())
                                          +', '
                                          +deg2hms(pt.lng())))
                .append($(_("<button class='ok'>OK</button>")).click(
                    okFun = function() {return updPoint(marker, form);}
                )).append($(_("<button class='del'>Удалить</button>")).click(
                    function() {return delPoint(marker);}
                )).append($(_("<button class='cancel'>Отмена</button>")).click(
                    cancel
                ));
            form.bind('keypress', keyHandler);
            map.openInfoWindow(pt, form.get(0));
        } else if (marker.openInfoWindow) {
            var title = marker.getTitle();
            pt = marker.getPoint();
            marker.openInfoWindow($('<div class="usercard"></div>')
                                  .append($('<p class="pointid"></p>').text('#'+(marker.frid || '')))
                                  .append($('<h3></h3>').text(title))
                                  .append($('<p class="coords"></p>').text(deg2hms(pt.lat())
                                                            +', '
                                                            +deg2hms(pt.lng())))
                                  .get(0));
        }
    } else {
        if (overlay) return true;
        // New marker
        form = $(_("<div class='req'>Ваше имя или название места:<br /></div>"))
            .append($("<input class='inp'></input>")
                    .keyup(function () {
                        // Disable OK button if value is empty
                        $('.ok', form).attr('disabled', trim($(this).val()).length == 0 ? 'disabled' : null);
                    }))
            .append($('<p class="coords"></p>').text(deg2hms(pt.lat())
                                      +', '
                                      +deg2hms(pt.lng())))
            .append($(_("<button class='ok' disabled='disabled'>OK</button>")).click(
                okFun = function() {return addPoint(pt, form);}
            )).append($(_("<button class='cancel'>Отмена</button>")).click(
                cancel
            ));
        form.bind('keypress', keyHandler);

        map.openInfoWindow(pt, form.get(0));
    }
    return false;
}

function load() {
    var container = document.getElementById("map");
    if (GBrowserIsCompatible()) {
        map = new GMap2(container, {
	    googleBarOptions: {
               style: 'new',
               adsOptions: {
		  client: "partner-google-maps-api",
                  channel: "7502731280",
                  adsafe: "high",
                  language: "ru"
               }
            }
        });

        //map.enableScrollWheelZoom();
        map.enableContinuousZoom();
	map.enableGoogleBar();

        var mcOptions = {gridSize: 20, maxZoom: 15};
        mc = new MarkerClusterer(map, [], mcOptions);

        if (window['G_SATELLITE_3D_MAP']) {
            map.addMapType(window['G_SATELLITE_3D_MAP']);
        }
        if (type == 'compact') {
            map.addControl(new GSmallMapControl());
            map.addControl(new GMenuMapTypeControl());
        } else {
            map.addControl(new GLargeMapControl());
            map.addControl(new GMapTypeControl());
            map.addControl(new GScaleControl());
            map.addControl(new GOverviewMapControl());
            map.addControl(new WhoSThereControl());
        }

        icons = Array(pts.length);

        var i = 0;
        function batchAddMarker() {
            for (var j = 0; j < 100 && i < pts.length; ++j, ++i) {
                var p = pts[i], lat, lon, m;
                // WKT point is never valid GeoHash as it contains OI and ()
                if (GX.checkValid(p.pt)) {
                    var d = GX.decode(p.pt);
                    lat = d.lat;
                    lon = d.lon;
                } else if ((m = p.pt.match(/POINT\(([^ ]*) ([^)]*)\)$/i))) {
                    lat = Number(m[1]);
                    lon = Number(m[2]);
                }
                mc.addMarker(icons[i] = createMarker(new GLatLng(lat, lon),
                                                     p.id,
                                                     p.title,
                                                     p.drag == "1"));
            }
            if (i < pts.length) {
                setTimeout(batchAddMarker, 10);
            }
        }
        setTimeout(batchAddMarker, 10);

//         windowFeed = $('<link rel="alternate" type="application/atom+xml" title="Atom Feed of New Points in current View"></link>');
//         $("head").append(windowFeed);

        function onMove() {
            var zoom = map.getZoom();
            var center = map.getCenter();
//             var ext = map.getBounds();
//             var lb = ext.getSouthWest();
//             var rt = ext.getNorthEast();
//             windowFeed[0].setAttribute('href',
//                             'atom?mode=window&lt='+
//                             GX.encode(lb.lat(), lb.lng(),  16+2*zoom)+
//                             '&rt='+
//                             GX.encode(rt.lat(), rt.lng(),  16+2*zoom));

            var lnk = '#'+GX._tr.charAt(zoom)+GX.encode(center.lat(), center.lng(), 16+2*zoom);
            window.location.href = lnk;
        }

        GEvent.addListener(map, 'click', clickListener);
        GEvent.addListener(map, 'moveend', onMove);
        $(window).resize(function () { map.checkResize(); });

        var anchor = window.location.href.split("#").pop();
        if (anchor.length > 1 && GX.checkValid(anchor.substr(1)) && GX._tr.indexOf(anchor.charAt(0)) >= 0) {
            var zoom=GX._tr.indexOf(anchor.charAt(0));
            var gx = GX.decode(anchor.substr(1));
            map.setCenter(new GLatLng(gx.lat, gx.lon), zoom, G_HYBRID_MAP);
        } else {
            if (window['extent'] == 'russia') {
                map.setCenter(new GLatLng(59.95, 95), 3, G_HYBRID_MAP);
            } else if (window['extent'] == 'world') {
                map.setCenter(new GLatLng(0.0, 0.0), 2, G_HYBRID_MAP);
            } else {
                map.setCenter(new GLatLng(54.95, 83), 10, G_HYBRID_MAP);
            }
        }
        $('#srchclose').click(function () {
            $("#srchspace").fadeOut("fast");
        });
    } else {
        $("#map").hide();
        $("#error").show();
    }
}


/* (C) 2009 Ivan Boldyrev
 * GX is a fast Geohash library in JavaScript.
 */

window['GX'] = {
    decode: function (str) {
        /* Strings longer 12 chars will lead to 32bit int overflow, but
           precision is more than necessary.
         */
        str = str.substr(0, 11);
        var l = str.length, i, l1=0, l2=0, ll1=0, ll2=0, al1, al2, c, i1;
        for (i=0; i<l; ++i) {
            c = GX._tr.indexOf(str.charAt(i));
            i1 = i&1;
            ll1 += (al1 = 3-i1);
            ll2 += (al2 = 2+i1);
            l1 = (l1 << al1) + GX._dm[c>>i1];
            l2 = (l2 << al2) + GX._dm[c>>(1-i1)];
        }
        l1 = l1/Math.pow(2.0, ll1) - 0.5;
        l2 = l2/Math.pow(2.0, ll2) - 0.5;
        return {lat:  180.0*l2, lon: 360.0*l1};
    },

    encode: function (lat, lon, bits) {

        lat = lat/180.0+0.5;
        lon = lon/360.0+0.5;

        /* We generate two symbols per iteration; each symbol is 5
         * bits; so we divide by 2*5 == 10.
         */
        var r = '', l = Math.ceil(bits/10), hlt, hln, b2, hi, lo, i;

        for (i = 0; i < l; ++i) {
            lat *= 0x20;
            lon *= 0x20;

            hlt = Math.min(0x1F, Math.floor(lat));
            hln = Math.min(0x1F, Math.floor(lon));

            lat -= hlt;
            lon -= hln;

            b2 = GX._sparse(hlt) | (GX._sparse(hln) << 1);

            hi = b2 >> 5;
            lo = b2 & 0x1F;

            r += GX._tr.charAt(hi) + GX._tr.charAt(lo);
        }

        r = r.substr(0, Math.ceil(bits/5));
        return r;
    },

    checkValid: function(str) {
        return !!str.match(/^[0-9b-hjkmnp-z]+$/);
    },

    _tr: "0123456789bcdefghjkmnpqrstuvwxyz",
    /* This is a table of i => "even bits of i combined".  For example:
     * #b10101 => #b111
     * #b01111 => #b011
     * #bABCDE => #bACE
     */
    _dm: [0, 1, 0, 1, 2, 3, 2, 3, 0, 1, 0, 1, 2, 3, 2, 3,
          4, 5, 4, 5, 6, 7, 6, 7, 4, 5, 4, 5, 6, 7, 6, 7],

    /* This is an opposit of _tr table: it maps #bABCDE to
     * #bA0B0C0D0E.
     */
    _dr: [0, 1, 4, 5, 16, 17, 20, 21, 64, 65, 68, 69, 80,
          81, 84, 85, 256, 257, 260, 261, 272, 273, 276, 277,
          320, 321, 324, 325, 336, 337, 340, 341],

    _sparse: function (val) {
        var acc = 0, off = 0;

        while (val > 0) {
            low = val & 0xFF;
            acc |= GX._dr[low] << off;
            val >>= 8;
            off += 16;
        }
        return acc;
    }
};

