/* Set up constants for IE */
if (!document.ELEMENT_NODE) {
    document.ELEMENT_NODE = 1;
}

/* Localization strings */
var lang = {'unsupported': '<p class="errmsg">Your browser is not supported by <a href="http://maps.google.com">Google Maps</a>.</p><p>Sorry for inconvenience.  Try <a href="http://www.getfirefox.com">better brower</a>.</p>',
    'searchresults': 'Search results'};

var ywc = {
    image: "11x11b/ywc.png",
    iconSize: new GSize(11, 11),
    iconAnchor: new GPoint(5, 5),
    infoWindowAnchor: new GPoint(6, 6)
};
var yellow = {
    image: "11x11b/y.png",
    iconSize: new GSize(11, 11),
    iconAnchor: new GPoint(5, 5),
    infoWindowAnchor: new GPoint(6, 6)
};
var white = {
    image: "11x11b/w.png",
    iconSize: new GSize(11, 11),
    iconAnchor: new GPoint(5, 5),
    infoWindowAnchor: new GPoint(6, 6)
};
var varied = {
    image: "11x11b/v.png",
    iconSize: new GSize(11, 11),
    iconAnchor: new GPoint(5, 5),
    infoWindowAnchor: new GPoint(6, 6)
};
var mixed = {
    image: "11x11b/m.png",
    iconSize: new GSize(11, 11),
    iconAnchor: new GPoint(5, 5),
    infoWindowAnchor: new GPoint(6, 6)
};

var colmap = {
    "YWC": ywc,
    "Y"  : yellow,
    "W"  : white,
    "V"  : varied,
    "M"  : mixed
};

var markers = [];
var searchIdx = [];
var map=null;


/* Push obj into the array unless it is already in the array */
function pushNew(array, obj) {
    for (var i in array) {
        if (array[i] === obj) { return; }
    }
    // nothing is found
    array.push(obj);
}

function addMarkerToIndex(marker, term) {
  if (term) {
      term = term.toLowerCase();
      if (!searchIdx[term]) {
          searchIdx[term] = [];
      }
      pushNew(searchIdx[term], marker);
  }
}

/* Convert string like "CM 062" into canonical form "CM62". */
function canonicalFieldnum(str) {
    return str && str.replace(/([^0-9]*[^0-9 ]) *0*([1-9].*)/, "$1$2");
}


/* Tooltip for locality: semicolon-separated list of taxons */
function localityTooltip(taxon, defaultGenus) {
    var title=taxon.getAttribute("n")+": ";
    var tl=taxon.getAttribute("t");
    if (tl == "y") {
        title += "TL ";
    }
    if (taxon.getAttribute("g")) {
        title += taxon.getAttribute("g");
    } else {
        title += defaultGenus;
    }
    title += " "+taxon.getAttribute("s");

    var subsp = taxon.getAttribute("u");
    if (subsp) {
        title += ' ssp. '+subsp;
    }

    var vari = taxon.getAttribute("v");
    if (vari) {
        title += ' v. '+vari;
    }

    var form = taxon.getAttribute("f");
    if (form) {
        title += " '"+form+"'";
    }

    var comment = taxon.getAttribute("c");
    if (comment) {
        title += " ("+comment+")";
    }
    return title;
}

/* String of sequence <dt>FIELDNUM</dt><dd>SPECIES</dd> describing
   the locality
*/
function localityHtml(taxon, defaultGenus) {
    // TODO: Link
    var num=taxon.getAttribute("n");
    // TODO: open window as a layer on map; if use clicks central
    // button, href is used.
    var title="<dt><a target='_new' href='http://www.lithops.info/en/gallery/lithops_"+canonicalFieldnum(num).toLowerCase()+".html'>"+num+"</a></dt><dd>";
    var tl=taxon.getAttribute("t");
    if (tl == "y") {
        title += "TL ";
    }
    title += '<i>';
    if (taxon.getAttribute("g")) {
        title += taxon.getAttribute("g");
    } else {
        title += defaultGenus;
    }
    title += " "+taxon.getAttribute("s")+'</i>';

    var subsp = taxon.getAttribute("u");
    if (subsp) {
        title += ' ssp.&nbsp;<i>'+subsp+'</i>';
    }

    var vari = taxon.getAttribute("v");
    if (vari) {
        title += ' v.&nbsp;<i>'+vari+'</i>';
    }

    var form = taxon.getAttribute("f");
    if (form) {
        title += " '"+form+"'";
    }

    var comment = taxon.getAttribute("c");
    if (comment) {
        title += " ("+comment+")";
    }
    title += '</dd>';
    return title;
}

function createLocalityMarker(placeElement, defaultGenus) {
    var titleArr=[];
    var titleHtmlArr=["<dl>"];
    var taxons = placeElement.getElementsByTagName("t");
    var point=new GLatLng(parseFloat(placeElement.getAttribute("a")),
    		      parseFloat(placeElement.getAttribute("o")));
    for (var i=0; i<taxons.length; ++i) {
        titleArr.push(localityTooltip(taxons[i], defaultGenus));
        titleHtmlArr.push(localityHtml(taxons[i], defaultGenus));
    }
    titleHtmlArr.push("</dl>");

    // Remove "; " et end of title
    var title=titleArr.join("; ");
    var titleHtml=titleHtmlArr.join("");
    titleArr=null;
    titleHtmlArr=null;

    var col = placeElement.getAttribute("c");
    var icon = colmap[col] || yellow;
    var opt = { title: title, icon: icon };

    var locality=placeElement.getAttribute("n");
    var marker=new GMarker(point, opt);

    addMarkerToIndex(marker, locality);
    for (i=0; i<taxons.length; ++i) {
        addMarkerToIndex(marker, canonicalFieldnum(taxons[i].getAttribute("n")));
        addMarkerToIndex(marker, taxons[i].getAttribute("g"));
        addMarkerToIndex(marker, taxons[i].getAttribute("s"));
        addMarkerToIndex(marker, taxons[i].getAttribute("u"));
        addMarkerToIndex(marker, taxons[i].getAttribute("v"));
        // TODO: strip "*"
        addMarkerToIndex(marker, taxons[i].getAttribute("c"));
    }

    markers.push(marker);
    
    marker.showInfo = function() {
        marker.openInfoWindowHtml("<div class='lithopscard'>"+titleHtml+"<p>"+locality+"</p></div>");
    };

    marker.showBlowUp = function() {
        if (map.getZoom() < 14) { marker.showMapBlowup(15); }
    };

    GEvent.addListener(marker, "click", marker.showInfo);
    GEvent.addListener(marker, "dblclick", marker.showBlowUp);

    return marker;
}

function pointsExtent(points) {
    var ext = new GLatLngBounds(points[0].getPoint(), points[0].getPoint());
    for (var i in points) {
        ext.extend(points[i].getPoint());
    }
    return ext;
}

function enlargeExtent(ext, q) {
    var size = ext.toSpan();
    var sw   = ext.getSouthWest();
    var ne   = ext.getNorthEast();

    return new GLatLngBounds(new GLatLng(sw.lat()-q*size.lat(),
                                         sw.lng()-q*size.lng()),
                             new GLatLng(ne.lat()+q*size.lat(),
                                         ne.lng()+q*size.lng()));
}

function resizeMap() {
    if (map) {
        var currentCenter = map.getCenter();
        map.checkResize();
        map.setCenter(currentCenter);
    }
}

var mapState = { lat: null, lng: null, zoom: null, s: null };

function updateAnchorOnMapMoveListener() {
    var center = map.getCenter();
    mapState.lat = center.lat();
    mapState.lng = center.lng();
    mapState.zoom = map.getZoom();
    updateAnchor(mapState);
}

function updateAnchor(mapState) {
    var str='';
    for (var prop in mapState) {
        if (mapState[prop]) {
            if (str) { str += '_'; }
            str += prop + '_' + mapState[prop];
        }
    }
    window.location.href = '#' + encodeURIComponent(str);
}

function addMarkersFrom(node, parent, species, minLevel, maxLevel) {
   if (node.nodeType == document.ELEMENT_NODE) {
      if (node.nodeName == "p") {
         var localityMarker = createLocalityMarker(node, species);
         if (parent) {
            parent.mergeKeywords(localityMarker);
         }
      } else if (node.nodeName == "g") {
         // Recursively add
         // TODO: Not implemented
      }
   }
}

/* Generic function for parsing '_'-separated anchors.
   For example, s_terricolor_lat_-32.575_lng_22.5833 is parsed as

   { "s"   : "terricolor",
     "lat" : "-32.575",
     "lng" : "22.5833"
   }
*/
function parseAnchor() {
    var params = {};
    var elts = window.location.href.split("#");
    var anchor = elts.pop();
    var anchor_elements = anchor.split("_");
    var ael_len = anchor_elements.length;

    for (var i = 1; i < ael_len; i += 2) {
        params[anchor_elements[i-1]] = decodeURIComponent(anchor_elements[i]);
    }

    return params;
}

/* Do application-specific preprocessing for anchor params (like
   converting strings into floats */
function preprocessAnchorParams(params) {
    if (params.lat) {
        params.lat = parseFloat(params.lat);
    }
    if (params.lng) {
        params.lng = parseFloat(params.lng);
    }
    if (params.zoom) {
        params.zoom = parseInt(params.zoom, 10);
    }
    return params;
}

function load() {
  var container = document.getElementById("map");
  if (GBrowserIsCompatible()) {
    map = new GMap2(container);
    map.addMapType(G_PHYSICAL_MAP);
    if (window['G_SATELLITE_3D_MAP']) {
        map.addMapType(window['G_SATELLITE_3D_MAP']);
    }
    map.addControl(new GLargeMapControl());
    map.addControl(new GMapTypeControl());
    map.addControl(new GScaleControl());
    map.addControl(new GOverviewMapControl());

    GEvent.addListener(map, "moveend", updateAnchorOnMapMoveListener);
    GEvent.addListener(map, "zoomend", updateAnchorOnMapMoveListener);

    GDownloadUrl("grouped.xml", function(data, responseCode) {
        var xml = GXml.parse(data);
        //var subnodes = xml.documentElement.childNodes;
        var subnodes = xml.documentElement.getElementsByTagName("p");
        for (var i = 0; i<subnodes.length; ++i) {
            addMarkersFrom(subnodes[i], null, "Lithops", 0, 17);
            //map.addOverlay(createLocalityMarker(places[i], "Lithops"));
        }
        var mapExtent = pointsExtent(markers);

        // TODO: calculate extent of points and its center
        mapState = preprocessAnchorParams(parseAnchor());

        if (!mapState.lat) { mapState.lat = mapExtent.getCenter().lat(); }
        if (!mapState.lng) { mapState.lng = mapExtent.getCenter().lng(); }

        if (!mapState.zoom) {
            mapState.zoom = Math.min(5, map.getBoundsZoomLevel(mapExtent));
        }

        map.setCenter(new GLatLng(mapState.lat, mapState.lng), 
                      mapState.zoom,
                      G_HYBRID_MAP);

        for (i in markers) {
            map.addOverlay(markers[i]);
        }
        if (mapState.s) {
            $('#searchterm').val(mapState.s);
            searchText(mapState.s);
        }
    });
  } else {
    container.innerHTML = lang['unsupported'];
  }
}

function matches(word, key, fieldp) {
    var off=key.indexOf(word);
    return (off>=0 && (!fieldp || (/^([^0-9]|$)/.test(key.substr(off+word.length))) && (off===0 || /^[^0-9]/.test(key.substr(off-1)))));
}

function searchResultsOnClick(marker, coord) {
    return function () {
        if (window.urchinTracker) {
            window.urchinTracker('/map/lithops/show/' + encodeURIComponent(coord));
        }
        marker.showInfo();
        return false;
    };
}

function searchText(text) {
    // Check if it is a field number with leading zeroes
    text=text.replace(/^([^0-9]*[^0-9 ])? *0*([1-9][0-9]*)/, "$1$2");
    var fieldp=/[0-9]/.test(text);
    var found = [];
    
    if (text) {
    	for (var i in markers) {
    	    // Reset
    	    markers[i].setImage(markers[i].getIcon().image);
    	}

    	// Search
        for (var key in searchIdx) {
    	    if (matches(text, key, fieldp)) {
                var toMark = searchIdx[key];
                for (i in toMark) {
    		    toMark[i].setImage("11x11b/h.png");
                    pushNew(found, toMark[i]);
                }
    	    }
        }
    }

    $('#searchquery').text(text);
    
    if (found) {
        found.sort(function (a, b) { 
            if (a.getTitle() == b.getTitle()) {
                return 0;
            } else if (a.getTitle() < b.getTitle()) {
                return -1;
            } else {
                    return 1;
            }
        });
        var ul = document.createElement('ul');
        for (midx in found) {
            var marker = found[midx];
            var coord = marker.getPoint();
            
            var an = document.createElement('a');
            // Actually this href is not used
            an.href='#s_'+text+'_lat_'+coord.lat()+'_lng_'+coord.lng();
            an.onclick = searchResultsOnClick(marker, coord);
            an.innerHTML = marker.getTitle().replace(/;/g, "<br />");
            
            var li = document.createElement('li');
            li.appendChild(an);
            
            ul.appendChild(li);
        }
        $('#searchheader').show();
        $('#searchresults').empty().append(ul).show('fast');
    }

    return found;
}

function search() {
    if (map) {
        var text=$('#searchterm').val().toLowerCase();
        var button=$('#searchbutton');
        var body=$(document.body);
        
        button.attr("disabled", "disabled");
        body.css("cursor", "wait");

        setTimeout(function () {
            var found = searchText(text);
            if (found.length > 0) {
                if (window.urchinTracker) {
                    window.urchinTracker('/map/lithops/search/' + encodeURIComponent(text));
                }
                var ext = enlargeExtent(pointsExtent(found), 0.1);
                var center = ext.getCenter();
                if (found.length > 1) {
                    // We have found enough points to calculate extent
                    var zoom = map.getBoundsZoomLevel(ext);
                    // If there is only three or two points, change zoom
                    // only if current zoom is too detailed, so points
                    // wouldn't fit the viewport.
                    // TODO: insert extra Math.min(8, ...)?
                    map.setCenter(center, (found.length > 3) ? zoom : Math.min(map.getZoom(), zoom));
                } else {
                    // Too little points for changing zoom
                    map.panTo(center);
                }
            }
            
            button.removeAttr("disabled");
            body.css("cursor", "default");

            mapState.s = text;
            updateAnchor(mapState);
        },
                   1);
    }
}

$(document).ready(function () {
    $(window).resize(resizeMap);
    $('#searchterm').change(search);
    $('.frame').hover(function () {
        $(this).css('opacity', 1.0);
    }, function () {
        $(this).css('opacity', 0.7);
    });
    $('h2.frame-child').click(function () {
        $(this).next().slideToggle("fast");
    });
});

$(window).load(function () {
    setTimeout(function () {
        $('#searchbutton').removeAttr("disabled").click(search);
        $('#searchframe div.frame-child').slideUp("slow");
        $('#legendframe div.frame-child').slideUp("slow");
    }, 2000);
});
