function Local(Parent) {
    var types = ['brewery', 'place', 'forum_topic'];

    var lv;
    var gmap;
    var blocks = {};

    var modIcon = function(Path) {
        var i = new GIcon(G_DEFAULT_ICON);
        i.image = Path;
        return i;
    };

    var options = {
        'brewery': {},
        'place': {icon:modIcon("/img/marker-green.png")},
        'forum_topic': {icon:modIcon("/img/marker-blue.png")},
        'summary':{
            'brewery': {icon:modIcon("/img/marker-plus-red.png")},
            'place': {icon:modIcon("/img/marker-plus-green.png")},
            'forum_topic': {icon:modIcon("/img/marker-plus-blue.png")}
        }
    };

    var pointLabel = function(pt, locations) {
        var lat = pt.lat();
        var lon = pt.lng();
        for (var i = locations.length-1; i >= 0; i--)
            if (lat == locations[i].lat && lon == locations[i].lon)
                return ['<div>',locations[i].location,'</div>'].join('');
        return '';
    };

    var infofuns = {
        'brewery':function() {
            var marker = this;
            $.getJSON('/api/brewery/'+marker.beerriotId, [],
                      function(brewery) {
                          var addr = pointLabel(marker.getLatLng(),
                              brewery.locations);
                          marker.openInfoWindowHtml(
                              ['<h3><a href="/brewery/',marker.beerriotId,'/">',
                               brewery.name,'</a></h3>',
                               addr,
                               '<div>',brewery.beers.length,' beer',
                               (brewery.beers.length == 1 ? '':'s'),'</div>',
                               '<div>',brewery.comments.length,' comment',
                               (brewery.comments.length == 1 ? '':'s'),'</div>'].join(''));
                      });
        },
        'place':function() {
            var marker = this;
            $.getJSON('/api/place/'+marker.beerriotId, [],
                      function(place) {
                          var addr = pointLabel(marker.getLatLng(),
                              place.locations);
                          marker.openInfoWindowHtml(
                              ['<h3><a href="/place/',marker.beerriotId,'/">',
                               place.name,'</a></h3>',
                               addr,
                               '<div>',place.beers.length,' beer',
                               (place.beers.length == 1 ? '':'s'),'</div>',
                               '<div>',place.comments.length,' comment',
                               (place.comments.length == 1 ? '':'s'),'</div>'].join(''));
                      });
        },
        'forum_topic':function() {
            var marker = this;
            $.getJSON('/api/topic/'+marker.beerriotId, [],
                      function(topic) {
                          var addr = pointLabel(marker.getLatLng(),
                              topic.locations);
                          marker.openInfoWindowHtml(
                              ['<h3><a href="/forum/events/',marker.beerriotId,'/">',
                               topic.title,'</a></h3>',
                               addr,
                               //TODO: dates
                               '<div>',topic.comments.length,' comment',
                               (topic.comments.length == 1 ? '':'s'),'</div>'].join(''));
                      });
        },
        'summary':{
            'brewery':function() {
                this.openInfoWindowHtml(
                    ['<div>',this.beerriotCount,
                     ' brewer',(this.beerriotCount == 1 ? 'y':'ies'),
                     ' near this location'].join(''));
            },
            'place':function() {
                this.openInfoWindowHtml(
                    ['<div>',this.beerriotCount,
                     ' place',(this.beerriotCount == 1 ? '':'s'),
                     ' near this location'].join(''));
            },
            'forum_topic':function() {
                this.openInfoWindowHtml(
                    ['<div>',this.beerriotCount,
                     ' event',(this.beerriotCount == 1 ? '':'s'),
                     ' near this location'].join(''));
            }
        }
    };

    var checks = {};

    var include = function(type) {
        return checks[type].attr('checked');
    };

    var iterTypes = function(callback) {
        for (var i in types) callback(types[i]);
    };

    var blockBounds = function(path) {
        var minlat = -180.0;
        var minlon = -180.0;
        var maxlat = 180.0;
        var maxlon = 180.0;

        var parts = path.split("/");
        for (var i = 1; i < parts.length-1; i++) {
            var midlat = (minlat+maxlat)/2;
            var midlon = (minlon+maxlon)/2;
            switch (parts[i]) {
            case "ne": minlat = midlat; minlon = midlon; break;
            case "se": maxlat = midlat; minlon = midlon; break;
            case "sw": maxlat = midlat; maxlon = midlon; break;
            case "nw": minlat = midlat; maxlon = midlon; break;
            }
        }

        return {minlat:minlat, minlon:minlon,
                maxlat:maxlat, maxlon:maxlon};
    };

    var mapBounds = function() {
        var mapb = gmap.getBounds();
        var sw = mapb.getSouthWest();
        var ne = mapb.getNorthEast();
        return {minlat: sw.lat(),
                minlon: sw.lng(),
                maxlat: ne.lat(),
                maxlon: ne.lng()};
    };

    var addMarker = function(kind, item) {
        if (!item.marker) {
            item.marker = new GMarker(new GLatLng(item.lat, item.lon),
                                      options[kind]);
            item.marker.beerriotId = item.id;
            GEvent.addListener(item.marker, "click", infofuns[kind]);
        }
        gmap.addOverlay(item.marker);
    };

    var showMarkers = function(kind, block) {
        if (block.display[kind] != 2) {
            block.display[kind] = 2;
            var list = block[kind];
            for (var i = list.length-1; i >= 0; i--)
                addMarker(kind, list[i]);
        }
    };

    var hideMarkers = function(kind, block) {
        if (block.display[kind] == 2) {
            block.display[kind] = 0;
            var list = block[kind];
            for (var i = list.length-1; i >= 0; i--)
                if (list[i].marker) gmap.removeOverlay(list[i].marker);
        }
    }

    var addSummary = function(kind, sub) {
        if (sub[kind].count > 0) {
            if (!sub[kind].marker) {
                sub[kind].marker = new GMarker(new GLatLng(sub[kind].lat, sub[kind].lon),
                                               options.summary[kind]);
                sub[kind].marker.beerriotCount = sub[kind].count;
                GEvent.addListener(sub[kind].marker, "click", infofuns.summary[kind]);
            }
            gmap.addOverlay(sub[kind].marker);
        }
    };

    var addSummaries = function(sub) {
        iterTypes(function(t) {
            if (include(t)) {
                if (!sub[t].display) {
                    sub[t].display = 1;
                    addSummary(t, sub);
                }
            } else {
                if (sub[t].marker) gmap.removeOverlay(sub[t].marker);
                sub[t].display = 0;
            }
        });
    };

    var removeSummaries = function(sub) {
        iterTypes(function(t) {
            if (sub[t].marker) gmap.removeOverlay(sub[t].marker);
            sub[t].display = 0;
        });
    };

    var showSub = function(mapbounds, block, rel, subbounds) {
        if ((subbounds.maxlat-subbounds.minlat)
            /(mapbounds.maxlat-mapbounds.minlat) > 0.3) {
            if (block.subs[rel].display == 1)
                removeSummaries(block.subs[rel]);
             
            block.subs[rel].display = 2;
            getBlock(block.path+rel, loadBlock);
        } else {
            if (block.subs[rel].display == 2)
                hideAll(block.path+rel);
            
            block.subs[rel].display = 1;
            addSummaries(block.subs[rel]);
        } 
    };

    var hideSub = function(block, rel) {
        if (block.subs[rel].display == 1) {
            iterTypes(function(t) {
                if (block.subs[rel][t].marker)
                    gmap.removeOverlay(block.subs[rel][t].marker);
                block.subs[rel][t].display = 0;
            });
        } else if (block.subs[rel].display == 2) {
            hideAll(block.path+rel);
        }

        block.subs[rel].display = 0;
    };

    var hideAll = function(path) {
        var block = blocks[path];
        if (block) {
            for (var rel in block.subs)
                hideSub(block, rel);
            iterTypes(function(t) {
                hideMarkers(t, block);
            });
        }
    };

    var getBlock = function(path, callback) {
        if (blocks[path])
            callback(blocks[path]);
        else
            $.getJSON('/api/mt2'+path, [],
                      function(block) {
                          blocks[path] = block;
                          block.display = {};
                          callback(block);
                      });
    };
    
    var loadBlock = function(block) {
        if (block.subs['ne/']) {
            var mapbounds = mapBounds();
            for (var rel in block.subs) {
                var bounds = block.subs[rel].bounds;
                if (!bounds) bounds = blockBounds(block.path+rel);

                if (mapbounds.minlat < bounds.maxlat
                    && mapbounds.maxlat > bounds.minlat
                    && ((mapbounds.minlon < bounds.maxlon
                         && mapbounds.maxlon > bounds.minlon)
                        || (mapbounds.minlon > mapbounds.maxlon //cross-IDL
                            && (mapbounds.minlon < bounds.maxlon
                                || mapbounds.maxlon > bounds.minlon))))
                    showSub(mapbounds, block, rel, bounds);
                else
                    hideSub(block, rel);
            }
        } else
            iterTypes(function(t) {
                if (include(t))
                    showMarkers(t, block);
                else 
                    hideMarkers(t, block);
            });
    };

    var loadMap = function() {
        $('body').unload(GUnload);

        if(GBrowserIsCompatible()) {
            gmap = new GMap2(document.getElementById('map'));
            gmap.setCenter(new GLatLng(lat, lon), zoom);
            gmap.addControl(new GLargeMapControl());
            gmap.addControl(new GMapTypeControl());
            gmap.addControl(new GScaleControl());

            GEvent.addListener(gmap, "moveend",
                               function() { getBlock('/', loadBlock); });
        }
    };

    var loadTemplate = function(data) {
        lv = $(data).appendTo(Parent);
        checks.brewery = $('#include-breweries', lv);
        checks.place = $('#include-places', lv);
        checks.forum_topic = $('#include-events', lv);
        loadMap();
        getBlock('/', loadBlock);
        $('.include-check', lv).click(function() { getBlock('/', loadBlock); });
    };

    $.get('/'+stv+'/html/local.html', [], loadTemplate);
};

$(function() {
    Local($('#view'));
});
