google.load("maps", "2.x"); // Load Google Maps
var map = null; // the google maps object
var quadSearchCircle = null;
var standardZoomLvl = 11; // the standard lvl, based on what type of property search is being performed
var polygon = new Array(); // an array to hold the points in a user defined polygon
var polygonComplete = false;
var baseIcon = null;

// variables for storing the lat/lng in the DB if it doesn't have them
var storeLatLng = {
    index:[],
    id:[],
    address:[],
    lat:[],
    lng:[]
};
var geocoder = null;
var numGeocoded = 0;

/* Sets up the initial functionality of the maps.
 * @param fullMaps = a boolean that tells how to organize the property category selectors.
 */
function setupPropertiesMapUI(fullMaps) {
    if (google.maps.BrowserIsCompatible()) {
        loadPolygonExtensions();
        map = new google.maps.Map2(document.getElementById("GMapCanvas"));
        geocoder = new google.maps.ClientGeocoder();

        quadMarker = new google.maps.Marker(new google.maps.LatLng(40.109259851560125, -88.22669506072998));
        google.maps.Event.addListener(quadMarker, "click", function(point) { // point == quadMarker.getLatLng
            map.setCenter(point, standardZoomLvl);
        });
        
        map.setUIToDefault();
        resetMap();

        // Create a base icon for all of our markers that specifies the
        // shadow, icon dimensions, etc.
        baseIcon = new GIcon(G_DEFAULT_ICON);
        baseIcon.iconSize = new GSize(18, 26);
        baseIcon.shadowSize = new GSize(0, 0);

        for (var i = 0; i < propertyData.length; i++) {
            if (propertyData[i].Latitude == 0 && propertyData[i].Longitude == 0) { // if the property doesn't have a lat/lng
                var address = propertyData[i].address+" "+propertyData[i].city+" "+propertyData[i].state+" "+propertyData[i].zip;
                if (address && address != null && address != "") {
                    storeLatLng.index[storeLatLng.id.length] = i;
                    storeLatLng.address[storeLatLng.id.length] = address;
                    storeLatLng.id[storeLatLng.id.length] = propertyData[i].id;
                }
            } else {
                propertyData[i].marker = createpropertyMarker(new google.maps.LatLng(propertyData[i].Latitude, propertyData[i].Longitude), i);
                map.addOverlay(propertyData[i].marker);
                jQuery("#propertySelection").append("<option value=\""+i+"\">"+propertyData[i].label+"</option");
            }
        }

        geocoder.setCache(null);
        geocodeAll();

        sortPropertySelection();
        
        // populate the propertyCategorySelection menu
        if (fullMaps) {
            for (var i = 11; i < propertyDataKeys.length-2; i++) {
                jQuery("#propertyCategorySelection").append("<option value=\""+propertyDataKeys[i]+"\">"+propertyDataKeys[i]+"</option>");
            }
        } else {
            for (var i = 11; i < propertyDataKeys.length-2; i++) {
                var toAdd = jQuery("<input class=\"button button_class\" type=\"button\" value=\""+propertyDataKeys[i]+"\">");
                toAdd.click(function() {
            		if (jQuery(this).val() == "On_Campus" || jQuery(this).val() == "Platinum") {
             	    	resetMap(13);
            		} else {
                		resetMap(11);
            		}
                    resetPropertiesOnMap();
                    for (var j = 0; j < propertyData.length; j++) {
                        if (propertyData[j][jQuery(this).val()]) {
                            if (propertyData[j].Latitude != 0 && propertyData[j].Longitude != 0) { // if valid latLng
                                addPropertyOnMap(j);
                                map.addOverlay(propertyData[j].marker);
                            }
                        }
                    }
                });
                jQuery("#propertyMapCategories").append("<td></td>").children(":last-child").append("<p class=\"button_class\"></p>").children().append(toAdd);
            }
            jQuery("input.button_class[value='All']").click(function() {
                resetMap(11);
                resetPropertiesOnMap();
                for (var i = 0; i < propertyData.length; i++) {
                    if (propertyData[i].Latitude != 0 && propertyData[i].Longitude != 0) { // if valid latLng
                        addPropertyOnMap(i);
                        map.addOverlay(propertyData[i].marker);
                    }
                }
            });
        }
    }

    // unloads google maps when the user exits the page to prevent memory leaks
    jQuery("body").unload(function() {
        google.maps.Unload();
    });

    jQuery("#propertySelection").change(function() {
        resetPropertiesOnMap();
        var i = jQuery(this).val();
        jQuery("#propertyCategorySelection").val(null);
        if (i) { // if the value isn't null
            resetMap(14, propertyData[i].marker.getLatLng());
            addPropertyOnMap(i);
            map.addOverlay(propertyData[i].marker);
        } else { // if null value is selected
            resetMap();
        }
    });

    jQuery("#propertyCategorySelection").change(function() {
        var category = jQuery(this).val();
        jQuery("#propertySelection").val(null);
        if (category == "all") { // if category type matches
            resetMap(11);
            resetPropertiesOnMap();
            for (var i = 0; i < propertyData.length; i++) {
                if (propertyData[i].Latitude != 0 && propertyData[i].Longitude != 0) { // if valid latLng
                    addPropertyOnMap(i);
                    map.addOverlay(propertyData[i].marker);
                }
            }
        } else if (category == "quadSearch") {
            resetMap(14);
            resetPropertiesOnMap();
            quadSearchCircle = null;
            jQuery("#quadSearchInput").show();
            map.addOverlay(quadMarker);
        } else if(category == "polygonSearch") {
            resetMap(14);
            resetPropertiesOnMap();
            map.disableDoubleClickZoom();
            polygon = new Array();
            polygonComplete = false;
            jQuery("#polySearchInput").show();
        } else if (category == null) { // if the blank option is selected
            resetMap(11);
            resetPropertiesOnMap();
        } else {
            resetMap(11);
            resetPropertiesOnMap();
            for (var i = 0; i < propertyData.length; i++) {
                if (propertyData[i][category]) {
                    if (propertyData[i].Latitude != 0 && propertyData[i].Longitude != 0) { // if valid latLng
                        addPropertyOnMap(i);
                        map.addOverlay(propertyData[i].marker);
                    }
                }
            }
        } // end if category type matches
    });

    jQuery("#findFromCircle").click(function() {
        var radius = jQuery("#circleRadiusInput").val();
        if (radius > 0 && radius == parseFloat(radius)) {
            resetPropertiesOnMap();
            if (quadSearchCircle != null) { // if there is a quadSearchCircle
                map.clearOverlays(); // remove all the markers and circle from the previous search
                map.setCenter(quadMarker.getLatLng(), standardZoomLvl);
                map.addOverlay(quadMarker);
            }
            quadSearchCircle = new CircleOverlay(quadMarker.getLatLng(), radius);
            map.addOverlay(quadSearchCircle);
            for (var i = 0; i < propertyData.length; i++) {
                if (quadSearchCircle.containsLatLng(new google.maps.LatLng(propertyData[i].Latitude, propertyData[i].Longitude))) {
                    addPropertyOnMap(i);
                    map.addOverlay(propertyData[i].marker);
                }
            }
        } else {
            alert("Please set a valid distance (numerical, greater than 0) for your property search radius.");
        }
    });

    google.maps.Event.addListener(map, "click", function(overlay, latLng, overlayLatLng) {
        if (jQuery("#propertyCategorySelection").val() == "polygonSearch") {
            if (overlay == null && !polygonComplete) { // if they haven't clicked on a marker
                polygon[polygon.length] = latLng;
                map.clearOverlays();
                for (var i = 0; i < polygon.length; i++) {
                    map.addOverlay(new google.maps.Marker(polygon[i]));
                    if (polygon.length > 1 && i < polygon.length-1) {
                        map.addOverlay(new google.maps.Polyline([polygon[i], polygon[i+1]]));
                    }
                } // end for each point in polygon
            } else if (!polygonComplete) {
                if (overlayLatLng.lat() == polygon[0].lat() && overlayLatLng.lng() == polygon[0].lng()) {
                    polygonComplete = true;
                    map.clearOverlays();
                    var customPoly = new google.maps.Polygon(polygon);
                    map.addOverlay(customPoly);
                    for (var i = 0; i < propertyData.length; i++) {
                        if (customPoly.containsLatLng(new google.maps.LatLng(propertyData[i].Latitude, propertyData[i].Longitude))) {
                            addPropertyOnMap(i);
                            map.addOverlay(propertyData[i].marker);
                        }
                    }
                }
            } // end if click on overlay
        } // end if category selection is 'polygonSearch'
    });

    jQuery("#polySearchReset").click(function() {
        map.clearOverlays();
        polygon = new Array();
        polygonComplete = false;
    });
}

/* clears overlays, sets the center and zoom based on a standard value or on parameters, and hides category options
 * @param zoomLvl = a custom zoom level, if this is left blank the current standardZoomLvl will be used
 * @param center = a custom center point for the map, if this is left blank, the quad is used as the center
 */
function resetMap(zoomLvl, center) {
    if (!zoomLvl) {
        zoomLvl = standardZoomLvl;
    }
    if (!center) {
        center = quadMarker.getLatLng();
    }
    map.clearOverlays(); // reset the map
    standardZoomLvl = zoomLvl; // reset the zoom level
    map.enableDoubleClickZoom();
    jQuery(".propertySearch").hide();
    map.setCenter(center, standardZoomLvl); // reset the center at the quad
}

/* adds a property to the Properties On Map table/list
 * @param i = the index that the property data is located in for the propertyData array
 */
function addPropertyOnMap(i) {
    jQuery("#propertiesOnMap").append("<tr>"/*+"<td>"+propertyData[i].id+"</td>"*/+"<td><a href=\"search/properties/view/"+propertyData[i].id+"\">"+propertyData[i].label+"</a></td>"/*+"<td>"+propertyData[i].address+"</td>"*/+"<td>"+propertyData[i].city+"</td>"/*+"<td>"+propertyData[i].state+"</td><td>"+propertyData[i].zip+"</td><td>"+"<img id=\""+propertyData[i].id+"\" class=\"propertyThumb\" src=\""+getPropertyThumbnailURL(propertyData[i].label)+"\" alt=\""+propertyData[i].label+"Image\" onerror=\"thumbLoadError(this);\" style=\"max-height:105px\"/>"+"</td>"*/+"</tr>");
}

// clears the Properties On Map table/list
function resetPropertiesOnMap() {
    var title = jQuery("#propertiesOnMap").children(":first-child");
    jQuery("#propertiesOnMap").html("");
    jQuery("#propertiesOnMap").append(title);
}

/* binds click event to the marker that shows a property-type popup when clicked
             * @param point = the coordinates to set the google maps marker at
             * @param i = the index of propertyData that is used to reference information about this property
             * @return the google maps marker set at point
             */
function createpropertyMarker(point, i) {
    var icon = new GIcon(baseIcon);
    icon.image = "/includes/images/CPM-mapIcon.png";

    var markerOptions = {
        icon:icon
    };
    var marker = new GMarker(point, markerOptions);

    //    var marker = new GMarker(point);
    GEvent.addListener(marker, "click", function() {
        map.setCenter(point);
        marker.openInfoWindowHtml(getpropertyPopupHTML(i));
    });
    return marker;
}

/* Creates the html for the popup window that is shown when the correlating point is clicked
             * @param i = the index of propertyData that is used to reference information about this property
             * @return the html for the popup window
             */
function getpropertyPopupHTML(i) {
    return "<div class=\"propertyMarkerPopup\" style=\"height:180px;\"><b>"+propertyData[i].label+"</b><br />"+propertyData[i].address+"<br />"+propertyData[i].city+", "+propertyData[i].state+" "+propertyData[i].zip+"<br /><a href=\"search/properties/view/"+propertyData[i].id+"\">Property Page</a><br /><img id=\""+propertyData[i].id+"\" class=\"propertyThumb\" src=\""+getPropertyThumbnailURL(propertyData[i].label)+"\" alt=\""+propertyData[i].label+"Image\" onerror=\"thumbLoadError(this);\" style=\"max-height:105px\"/></div>";
}

/* Determines the property thumbnail's url as a relative url.
             * @param label = the property's label
             * @return property thumbnail url
             */
function getPropertyThumbnailURL(label) {
    return "images/apt/"+label.replace(/ /g, "_").replace(/\//g, "-").replace(/,/g, "").toLowerCase()+"/building.jpg";
}

/* If a thumbnail doesn't exist (didn't load), then remove the image error and say that it doesn't exist.
             * @param element = the image element
             */
function thumbLoadError(element) {
    jQuery(element).parent().append("<br />Thumbnail Doesn't Exist");
    jQuery(element).remove();
}

function geocodeAll() {
    if (numGeocoded < storeLatLng.id.length) {
        geocoder.getLocations(storeLatLng.address[numGeocoded], addressResolved);
    } else if (numGeocoded > 0) {
        var toSend = {
            id:storeLatLng.id,
            lat:storeLatLng.lat,
            lng:storeLatLng.lng
        };
        jQuery.ajax({ // send the lat/lng to the server for storage.  the user isn't concerned with the success / failure of this as they already have the data
            url:'includes/php/PropertyInfo.php',
            type:'POST',
            data:'latlng='+JSON.stringify(toSend)
        });
    }
}

function addressResolved(response) {
    var delay = 0;
    if (response.Status.code == 620) {
        // Too fast, try again, with a small pause
        delay = 500;
    } else {
        if (response.Status.code == 200) { // success
            propertyData[storeLatLng.index[numGeocoded]].marker = createpropertyMarker(response, storeLatLng.index[numGeocoded]);
            map.addOverlay(propertyData[storeLatLng.index[numGeocoded]].marker);
            jQuery("#propertySelection").append("<option value=\""+storeLatLng.index[numGeocoded]+"\">"+propertyData[storeLatLng.index[numGeocoded]].label+"</option");
            sortPropertySelection();

            storeLatLng.lat[numGeocoded] = response.Placemark[0].Point.coordinates[1];
            storeLatLng.lng[numGeocoded] = response.Placemark[0].Point.coordinates[0];
        }
        // Move onto the next address; this skips bad addresses, too.
        numGeocoded += 1;
    }
    window.setTimeout(geocodeAll, delay);
}

function sortPropertySelection() {
    var mylist = jQuery("#propertySelection");
    var listitems = mylist.children('option').get();
    listitems.sort(function(a, b) {
        var compA = jQuery(a).text().toUpperCase();
        var compB = jQuery(b).text().toUpperCase();
        return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
    })
    jQuery.each(listitems, function(idx, itm) { mylist.append(itm); });
}


// GENERAL FUNCTIONS

/* Gets the keys of an object (treats it like an associative array). i.e. it'd get id if object test had the property id: test.id
 * @param obj - the object
 * @return keys - the keys of that object
 */
function getObjKeys(obj) {
    var keys = [];
    for (var key in obj) {
        keys.push(key);
    }
    return keys;
}

/* Compares two labels and then determines which should be placed ahead of the other
 * @param a = label to be compared
 * @param b = label to be compared
 * @return 1 if a goes first, 2 if b goes first
 */
function sortLabels(a, b) {
    var x = a.label.toLowerCase();
    var y = b.label.toLowerCase();
    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
}
