/* parse and render map data
 *
 *
 * (c) David Buchmann, Andi Weibel, 2009
 */

/* config */
var transparency = 0.8; //default transparency for arcs
var arcOptions = {geodesic:true}; //options for arcs, of the GPolylineOptions

/* language definitions */
var strTotal = "Total";
var strLoading = "Lade Karte und Daten...";
var strNotGBrowserCompatible = "<h1>Google Maps Inkompatibilit&auml;t</h1><p>Tut mir leid, Google Maps sagt dass Ihr Browser nicht kompatibel ist mit der Karte. Bitte schreiben Sie an webmaster@kriegsmaterial.ch wenn Sie denken, Ihr Browser m&uuml;sste funktionieren.</p>";



/* global variables */
var enableDebug;
var contentcategories;
var currentCategory; //the category currently visible
var catLoading=0; //which category is currently loading
var catsToLoad; //how many categories still need to be loaded

var instances = new Array();  // double hashmap: category -> instance name -> GMarker object
var arcgroups = new Array();  // double hashmap: category -> arcgroup label -> arc array of GPolylines
var map = null;      // google maps instance
var defaultWikiUrl = "/wiki/index.php/Einführung"; //wiki info to load on startup
var currentWikiUrl = ""; //wiki info currently loaded

var dataUrl = "../getgdata.php?category="; //base url to get data from, appends category name to it

/* load map when page has loaded.
 * @param contentcats array of category identifiers to load
 * @param debug wether to write messages to the GLog
 */
function load(contentcats, debug) {
    if (GBrowserIsCompatible()) {
        enableDebug = debug;

        $("#hidemap").html('<h1>'+strLoading+'</h1>');
        initMap();

        contentcategories = contentcats;
        catsToLoad = contentcategories.length;
        catLoading = 0;

        $.get(dataUrl+contentcategories[catLoading], {}, parseGData, "xml");

        loadWikiInfo(defaultWikiUrl);
        setTimeout(showMap, 20000); //show what we have after 10 seconds. in case one category fails, we would never stop

    } else {
        $("#map").html(strNotGBrowserCompatible);
    }
}

/* as recommended by http://code.google.com/intl/en-GB/apis/maps/documentation/reference.html#GUnload.GUnload */
function unload() {
    GUnload();
}


function initMap(){
    map = new GMap2(document.getElementById("map"));
    map.addControl(new GLargeMapControl());
    var mapControl = new GMapTypeControl();
    map.addControl(mapControl);
    map.addControl(new JumpToPlacesControl());
    map.setCenter(new GLatLng(26.977, 10.581), 2);
}

function showMap() {
    //$("#map").fadeIn();
    $("#hidemap").fadeOut(1500);
    if (enableDebug) {
        $("body").children("div:last").css("color", "black");
    }
}

/* parse a document of gdata xml and put it into instances hashmap */
function parseGData(xml) {
    var cat = $("gdata",xml).attr("category");
    debugLog("Got data of type "+cat);

    instances[cat] = new Array();
    $("instance",xml).each(
        function (i) {
            debugLog(" adding instance "+$(this).attr("name"));
            var marker = createInstanceMarker(this);
            if (marker) instances[cat][$(this).attr("name")] = marker;
    });

    arcgroups[cat] = new Array();
    var options = '';
    $("arcgroup",xml).each(
        function (i) {
            var label = $(this).attr("label");
            debugLog(" adding arcgroup"+label);
            var arcgroup = createArcGroup(this, $(this).attr('default') == 'true');
            arcgroups[cat][label] = arcgroup;

            options += '<div class="option';
            if ($(this).attr('default') == 'true') options += ' active default';
            options += '" value="'+label+'">'+label+'</div>';
            //TODO: wikiurl attribute if available
    });

    if ($("arcs",xml).attr("multiselect") == 'true') {
        $("#"+cat).addClass("multiselect");
    } else {
        $("#"+cat).addClass("singleselect");
    }
    $("#"+cat).html(options);
    $("#"+cat+" .option").click(selectArcgroup);

    $("#cat-"+cat).children(".label").click(showCategory);

    if (cat == contentcategories[0]) {
        currentCategory = cat;
        displayCategory(cat);
        showMap();
    } else {
        $("#"+cat).hide();
        $("#cat-"+cat).children(".label").addClass("closed");
    }

    if (catLoading == 0) showMap();
    $.get(dataUrl+contentcategories[++catLoading], {}, parseGData, "xml"); //chain loading of further data
}

var desc="";
/* recursively translate the current node to a string.
 * keeping all attribute names and values, text nodes and children.
 *
 * result is stored in global variable desc. do not forget to reset this var before calling domToString
 */
function domToString() {
    if (this.nodeName == "#text") {
        if (this.nodeValue != 'undefined') desc += this.nodeValue;
        return;
    }
    desc += "<" + this.nodeName;
    for (i=0; i<this.attributes.length;i++) {
        if (this.attributes[i].specified) {
            desc += " " + this.attributes[i].nodeName+"='"+this.attributes[i].nodeValue+"'";
        }
    }

    desc += ">";
    $(this).contents().each(domToString);
    desc += "</" + this.nodeName + ">";
}

/* create map item for one instance and add it to the map.
 * @return the GMarker or false on error
 */
function createInstanceMarker(node) {
    var point = new GLatLng($("coordinates", node).attr("lat"), $("coordinates", node).attr("lng"));
    if (isNaN(point.lat()) || isNaN(point.lng()) || point.lat()==undefined || point.lng()==undefined) return false; //sanity check

    var icon  = $("icon",node);
    var instIcon =  new GIcon(G_DEFAULT_ICON);
    instIcon.image = icon.attr("url");
    if (icon.attr("size")=="big") {
        instIcon.iconSize = new GSize(35, 35);
        instIcon.iconAnchor = new GPoint(17, 35);
        instIcon.shadowSize = new GSize(50, 35);
        instIcon.imageMap = new Array(4,4, 0,17, 5,25, 17,35, 29,25, 35,17, 31,4, 17,0);
    } else {
        instIcon.iconSize = new GSize(20, 20);
        instIcon.iconAnchor = new GPoint(10, 20);
        instIcon.shadowSize = new GSize(30, 20);
        instIcon.imageMap = new Array(2,2, 0,10, 3,15, 10,20, 17,15, 20,10, 18,2, 10,0);
    }
    instMarkerOptions = { icon:instIcon, title: $(node).attr("name") };

    var marker = new GMarker(point, instMarkerOptions);
    /*
     * bug in gmaps seems to prevent ordering markers by creation.
     * make z-indexes of markers according to order of addition. *
     function orderOfCreation(marker) {
     return 1;
     }
    , {zIndexProcess:orderOfCreation});
    */
    var infoWinOptions = {maxWidth:300};

    desc="";
    $("a[class*=Reference]",node).each(function() {
        //selector .Reference seems to work only on html, not on xml
        //and callback seems to not have this, so we need to send the info in the attribute specification
        cat = false;
        for(var i in contentcategories) {
            if ($(this).hasClass(contentcategories[i])) {
                cat = contentcategories[i];
            }
        }
        if (! cat) {
            debugLog("did find no suitable category for "+$(this).text());
            debugLog("  "+$(this).attr("class"));
            for(var i in contentcategories) {
                debugLog("     :"+contentcategories[i]);
            }
            return;
        }
        $(this).attr("onClick",'javascript:clickReference("'+cat+'","'+$(this).text()+'")');
    });
    $("a[class*=Reference]",node).attr("href","#");
    $("text",node).children().each(domToString);

    marker.bindInfoWindowHtml(desc);

    GEvent.addListener(marker, "infowindowopen", function() {
        loadWikiInfo($(node).attr("wikiurl"));
    });
    GEvent.addListener(marker, "infowindowbeforeclose", function() {
        unloadWikiInfo($(node).attr("wikiurl"));
    });

    //make the marker know its position so that country links inside scandals work
    map.addOverlay(marker);
    marker.hide();

    debugLog("...at "+point.lat()+"/"+point.lng());
    return marker;
}

function createArcGroup(node, display) {
    var arcgroup = new Array();
    var srcPoint = new GLatLng($("source", node).attr("lat"), $("source", node).attr("lng"));
    $("arc", node).each(
        function(i) {
            var localtransparency = transparency;
            if ($(this).attr("transparency")!='') {
                localtransparency = $(this).attr("transparency");
            }
            var targPoint = new GLatLng(parseFloat($("target",this).attr("lat")), parseFloat($("target",this).attr("lng")));
            var line = new GPolyline([srcPoint, targPoint],
                                     $(this).attr("color"),
                                     $(this).attr("weight"),
                                     localtransparency, arcOptions);
            if (! display) line.hide();
            arcgroup[i] = line;
    });

    if($("instance",node).size()) {
        debugLog("arcgroup has instance");
        var srcMark = createInstanceMarker($("instance", node));
        //todo: add action to highlight this markers lines
    }
    return arcgroup;
}


function displayCategory(cat) {
    $("#cat-"+cat).append("<div id='pleasewait'>Lade Daten...</div>");
    $("#cat-"+cat).children(".label").removeClass("closed");
    $("#cat-"+cat).children(".label").addClass("opened");
    setTimeout(_doDisplayCategory, 100);
}
function _doDisplayCategory() {
    cat = currentCategory;
    if (! $.browser.safari) {
        for(var label in arcgroups[cat]) {
            for(var i in arcgroups[cat][label]) {
                //for now, createArcGroup makes singleselect arcs that are not default hidden
                map.addOverlay(arcgroups[cat][label][i]);
            }
        }
    } else {
        $("#help").html("Um die Karte vollständig nutzen zu können, verwenden sie am besten Firefox. Mit Safari können die Verbindungslinien nicht dargestellt werden.")
    }
    for(var i in instances[cat]) {
        map.addOverlay(instances[cat][i]);
    }
    setTimeout(_cleanupDisplayCategory, 300);
}
function _cleanupDisplayCategory() {
    $("#pleasewait").remove();
    $("#"+currentCategory).slideDown();
}

function hideCategory(cat) {
    $("#cat-"+cat).children(".label").removeClass("opened");
    $("#cat-"+cat).children(".label").addClass("closed");
    $("#"+cat).hide();
}

/*** map interaction ***/

/* click name of other thing in popup -> open the popup of the other thing. */
function clickReference(cat, nodename) {
    marker = instances[cat][nodename];
    if (marker) {
        GEvent.trigger(marker,"click");
    } else {
        debugLog("Marker "+cat +">"+nodename+" not found");
    }
    return false;
}

function showCategory() {
    if ($(this).hasClass("opened")) return; //already open

    var cat = $(this).parent().attr("id").substring(4);
    map.clearOverlays();
    hideCategory(currentCategory);
    currentCategory = cat;
    displayCategory(cat);
}

/* click on an entry in one category.
 * show an arcgroup, hide others
 */
function selectArcgroup() {
    var cat = $(this).parent().attr("id");
    var label = $(this).attr("value");
    var isdefault = $(this).hasClass("default");
    debugLog (cat + " " + label);

    if ($(this).parent().hasClass("singleselect")) {
        if ($(this).hasClass("active")) return; //do not unselect current selection

        $(this).parent(".singleselect").children(".active").each(function() {
            var oldlabel = $(this).attr("value");
            var oldisdefault = $(this).hasClass("default");
            hideArcs(arcgroups[cat][oldlabel], oldisdefault); //if oldisdefault -> fadeArcs instead of hide
            $(this).removeClass("active");
        });

        showArcs(arcgroups[cat][label]); //if isdefault: highlightArcs
    } else {
        //multiselect: we can select or unselect an entry
        if ($(this).hasClass("active")) {
            hideArcs(arcgroups[cat][label], isdefault); //TODO: fadeArcs(cat, label)
        } else {
            showArcs(arcgroups[cat][label]); //TODO: highlightArcs
        }
    }

    $(this).toggleClass("active");
}

/* transparency is immutable. recreate the arcs and remove the old ones */
function fadeArcs(cat, label) {
    for (i=0; i<arcgroups[cat][label].length; i++) {
        arc = arcgroups[cat][label][i];
        newarc = false; //TODO create new arc with correct transparency, add it to the map
        arcgroups[cat][label][i] = newarc;
        arc.remove();
    }
}

/**
  * hides a list of arcs.
  * if it is a default group, we would prefer to set a high transparency - but transparency is immutable.
  * could this help: http://www.maptiler.org/google-maps-overlay-opacity-control/
  * @param arclist array of arcs
  * @param isdefault boolean whether this is a default arcgroup
  */
function hideArcs(arclist, isdefault) {
    for (i=0; i<arclist.length; i++) {
        /*if (isdefault) {
            arclist[i].
        } else {*/
            arclist[i].hide();
        //}
    }
}
/** @param arclist array of arcs */
function showArcs(arclist) {
    for (i=0; i<arclist.length; i++) {
        arclist[i].show();
    }
}
/**
 * load information from the wiki to display
 * if the url is empty, the #noWikiInfo div is loaded
 *
 * @param url string with the url to load the wiki data from or empty to reference the notification that there is no wiki info
 */
var loading = false;
function loadWikiInfo(url) {
    if (loading) return;
    if ($.browser.msie && parseInt($.browser.version) < 7) {
        //loading the wiki failes with IE7
        $("#wikiinfo").html('<div class="about"><a href="'+url+ '" target="_blank">Informationen aus dem Wiki</a> (Keine Vorschau mit Internet Explorer 6. Bitte Firefox oder Internet Explorer 7 verwenden.)</div>');
       return;
    }
    if (url==undefined) url = defaultWikiUrl;
    loading=true;
    if (currentWikiUrl != url) {
        currentWikiUrl = url;
        $("#wikiinfo").slideUp(1000);
        if (url!="") {
            debugLog("loading "+url);
            $.get(url, {}, receiveWikiInfo, "html"); //looks like a case for load(), but we need to control when content is loaded
        } else {
            $.get(defaultWikiUrl, {}, receiveWikiInfo, "html");
        }
        setTimeout("loading=false", 10000); //stop blocking after 10 seconds in case the ajax failed
    } else {
        setTimeout("loading=false", 500); //stop blocking after unloadWikiInfo has fired if it tries to fire
    }
}
/**
 * unload wiki info
 * if there is an url, only if this is the one that is currently shown.
 * if the url is empty, only hide the notification that there is no content.
 * if the condition is not matched, the info window stays unchanged.
 *
 * @param url string with the url of current wiki data or empty for notification that there is no wiki info
 */
function unloadWikiInfo(url) {
    if (currentWikiUrl == url) {
        setTimeout("loadWikiInfo(defaultWikiUrl)", 200);
    }
}

/**
 * callback when wiki info is loaded
 */
var receivedHtml;
function receiveWikiInfo(html) {
    debugLog("wiki info received");
    /* IE6 crashed somewhere around here, so loading info is disabled. */
    if($("#wikiinfo").queue().length>0) {
        receivedHtml = html; //setTimeout evaluates in global scope
        setTimeout("receiveWikiInfo(receivedHtml)", 100); //javascript does not do sleep(), do some kind of recursive wait...
        return;
    }
    $("#wikiinfo").html($("#bodyContent", html));
    $("#wikiinfo a").attr("target","_blank");
    $("#wikiinfo").prepend("<div class='about'>Informationen zu <a href='"+currentWikiUrl+"' target='_blank'>"+$(".firstHeading", html).text()+"</a></div>");
    $("#wikiinfo").slideDown(1000);
    loading=false;
    //});
}

function debugLog(txt){
  if(enableDebug == true)
    GLog.write(txt,1);
}
