$(function() {
    var map = L.map('map', {
        center: [30, 0],
        zoom: 2,
        maxBounds: [[-100,-200],[100,200]]
    });
    window.lmap = map;
    L.tileLayer(
        "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
        {
            attribution: gettext("Map data © OpenStreetMap contributors")
        }
    ).addTo(map);
        
    var coordCache = [],
        coordIdCache = [],
        defaultIcon = new L.Icon.Default(),
        orangeIcon = new L.Icon.Default({iconUrl: urls.orange_marker }),
        country_uris = _(countries).pluck("dbpedia_uri"),
        itemCount = 12,
        scaleBase = Math.log(countries[0].nb_notices),
        currentFeature = null,
        stickyFeature = null,
        $overlay = $(".dbpedia-overlay"),
        $map = $("#map");
    
    function getData() {
        var bounds = map.getBounds();
        $.getJSON(
            urls.ajax_geo_coords,
            {
                min_lat: bounds.getSouth(),
                max_lat: bounds.getNorth(),
                min_lng: bounds.getWest(),
                max_lng: bounds.getEast()
            },
            function(data) {
                _(data).each(function(coord) {
                    if (coordIdCache.indexOf(coord.dbpedia_uri) === -1 && country_uris.indexOf(coord.dbpedia_uri) === -1) {
                        coordIdCache.push(coord.dbpedia_uri);
                        coordCache.push(coord);
                    }
                });
                coordCache = _(coordCache).sortBy(function(coord) {
                    return -coord.sum_notices;
                });
                showData();
            }
        );
        showData();
    }
    
    function selectFeature(feature) {
        _(coordCache).each(function(coord) {
            coord.isCurrent = false;
            if (coord.marker) {
                coord.marker.setIcon(defaultIcon);
            }
        });
        _(countries).each(function(c) {
            if (c.layer) {
                c.layer.setStyle({weight: 1, color: "#000080", opacity: .3});
            }
        });
        stickyFeature = feature;
        showDbpedia(feature);
        loadSearchResults({ dbpedia_uri: feature.dbpedia_uri });
    }
    
    function selectMarker(coord) {
        showCoord(coord);
        selectFeature(coord);
        coord.isCurrent = true;
        coord.marker.setIcon(orangeIcon);
        map.setView([coord.latitude, coord.longitude], Math.max(Math.min(12,map.getZoom() + 1),6));
    }
    
    function selectCountry(country) {
        selectFeature(country);
        country.layer.setStyle({weight: 5, color: "#c00000", opacity: .8});
        map.fitBounds(country.bounds);
    }
    
    function showDbpedia(feature) {
        if (stickyFeature && feature !== stickyFeature) {
            return;
        }
        currentFeature = feature;
        if (!feature) {
            dbpediaBox.hide();
            return;
        }
        dbpediaBox.show(feature.dbpedia_uri, (feature === stickyFeature));
        dbpediaBox.setAnchor({type: "callback", callback: moveCurrentFeature, positioning: "bottom"});
        throttledMove();
    }
    
    function moveCurrentFeature() {
        if (!currentFeature) {
            return;
        }
        var fb = currentFeature.bounds,
            mb = map.getBounds();
        if (fb) {
            if (!mb.intersects(fb)) {
                hideDbpedia(true);
                return;
                }
            var c = L.latLngBounds(
                [Math.max(mb.getSouth(),fb.getSouth()), Math.max(mb.getWest(),fb.getWest())],
                [Math.min(mb.getNorth(),fb.getNorth()), Math.min(mb.getEast(),fb.getEast())]
            ).getCenter();
        } else {
            var c = currentFeature.latlng;
        }
        if (!mb.contains(c)) {
            hideDbpedia(true);
            return;
        }
        var p = map.latLngToContainerPoint(c),
            mo = $map.offset(),
            y = p.y + mo.top;
        return ({ top: y - (fb ? 0 : 30), left: p.x + mo.left, bottom: y });
    }
    
    var throttledMove = _.throttle(dbpediaBox.recentre,200);
    
    function hideDbpedia(force) {
        if (!stickyFeature) {
            currentFeature = null;
        }
        if (!stickyFeature || force) {
            dbpediaBox.setAnchor();
            dbpediaBox.hide();
        }
    }
    
    function showCoord(coord) {
        if (!coord.marker) {
            coord.latlng = L.latLng(coord.latitude, coord.longitude);
            coord.marker = L.marker(coord.latlng).addTo(map);
            coord.marker.on("click", function() { selectMarker(coord); });
            coord.marker.on("mouseover", function() { showDbpedia(coord); });
            coord.marker.on("mouseout", function() { hideDbpedia(); });
        }
    }
    
    function showData() {
        var n = 0,
            bounds = map.getBounds(),
            nsew = {
                w: bounds.getWest(),
                e: bounds.getEast(),
                n: bounds.getNorth(),
                s: bounds.getSouth()
            };
        _(coordCache).each(function(coord) {
            if (coord.isCurrent || (n < itemCount && coord.latitude > nsew.s && coord.latitude < nsew.n && coord.longitude > nsew.w && coord.longitude < nsew.e) ) {
                n++;
                showCoord(coord);
            } else {
                if (coord.marker) {
                    map.removeLayer(coord.marker);
                    coord.marker = null;
                }
            }
        });
    }
    
    var gjs = L.geoJson(null, {
        style: function(f) {
            f.dbCountry = _(countries).find(function(c) { return c.iso_code_3 === f.id; });
            var styleobj = { weight: 1, color: "#000080", opacity: .3 };
            if (f.dbCountry && f.dbCountry.nb_notices) {
                var x = Math.min(1, Math.max(0, Math.log(f.dbCountry.nb_notices) / scaleBase)),
                    g = Math.floor(255*(1-.5*x)),
                    b = Math.floor(255*(1-x));
                styleobj.fillColor = "rgb(" + [255,g,b].join(",") + ")";
                styleobj.fillOpacity = .5;
            } else {
                styleobj.fillColor = "#000080";
                styleobj.fillOpacity = .2;
            }
            return styleobj;
        },
        onEachFeature: function(f, l) {
            if (f.dbCountry) {
                f.dbCountry.bounds = l.getBounds();
                f.dbCountry.latlng = f.dbCountry.bounds.getCenter();
                f.dbCountry.layer = l;
                l.on("click", function() { selectCountry(f.dbCountry); });
                l.on("mouseover", function() { showDbpedia(f.dbCountry); });
                l.on("mouseout", function() { hideDbpedia(); });
            }
        }
    });
    
    gjs.addTo(map);
    
    $.getJSON(urls.countries_geo_json, function(data) {
        gjs.addData(data);
    });
    
    var debouncedGetData = _.debounce(getData,500);
        
    debouncedGetData();
    
    map.on("move", throttledMove);
    map.on("moveend", debouncedGetData);
    
    var blockUnsticking = false;
    
    $(".map-search-input").autocomplete({
        source: urls.ajax_geo_search,
        select: function(event, ui) {
            var coord = ui.item,
                countryindex = country_uris.indexOf(coord.dbpedia_uri);
            if (countryindex === -1) {
                if (coordIdCache.indexOf(coord.dbpedia_uri) === -1) {
                    coordIdCache.push(coord.dbpedia_uri);
                    coordCache.push(coord);
                    coordCache = _(coordCache).sortBy(function(coord) {
                        return -coord.sum_notices;
                    });
                    var feature = coord;
                } else {
                    var feature = _(coordCache).find(function(c) { return c.dbpedia_uri === coord.dbpedia_uri; });
                }
                selectMarker(feature);
            } else {
                selectCountry(countries[countryindex]);
            }
            blockUnsticking = true;
            window.setTimeout(function() { blockUnsticking = false;}, 0);
        }
    });
    
    $(document).click(function() {
        if (!blockUnsticking) {
            stickyFeature = null;
        }
    });
});
