function arrayContains(arr, element) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] == element) {
            return true;
        }
    }
    return false;
};

function updateMarkerPosition(i) {
    // Update the marker text - easiest way is to close and reshow.
    incLoadingCount();
    $.ajax({
        type: "POST",
        url: baseURL + 'api/moverestaurant/',
        data: {'restaurant': i, 'latitude': markers[i].getLatLng().lat(), 'longitude': markers[i].getLatLng().lng(), 'csrfmiddlewaretoken' : csrfToken},
        dataType: 'json',
        success: moveSuccess,
        error: rateError
    });
    
    if (whichMarkerOpen == i) {
        markers[i].closeInfoWindow();
        showMarkerWindow(markers[i], i);
    }
}

function moveSuccess(data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#loginStatus').text(data['failure']);
    } else {
        var i = data['id'];
        locationData[i]['latitude'] = data['latitude'];
        locationData[i]['longitude'] = data['longitude'];
    }
}

function startDrag(i) {
    markers[i].enableDragging();
    if (!(i in tempRestaurantData)) {
        tempRestaurantData[i] = {};
    }
    tempRestaurantData[i]['draggable'] = true;
    // Update the marker text - easiest way is to close and reshow.
    markers[i].closeInfoWindow();
    showMarkerWindow(markers[i], i);
    tempRestaurantData[i]['dragendlistener'] =
        GEvent.addListener(markers[i], "dragend", function() {
            updateMarkerPosition(i);
    });
}

function stopDrag(i) {
    markers[i].disableDragging();
    // Update the marker text - easiest way is to close and reshow.
    tempRestaurantData[i]['draggable'] = false;
    markers[i].closeInfoWindow();
    showMarkerWindow(markers[i], i);
    GEvent.removeListener(tempRestaurantData[i]['dragendlistener']);
    delete tempRestaurantData[i]['dragendlistener'];
}

function showMarkerWindow(marker, i) {
    i = parseInt(i);
    whichMarkerOpen = i;
    location.hash = "location=" + i;
    var lData = locationData[i][0];
    var rId = lData['restaurant'];
    var rData = restaurantData[rId][0];
    var rUserData = restaurantData[rId][1];
    var html = '';
    if (rData['url'] && rData['url'] != '') {
        html += '<a href="' + rData['url'] + '">' + rData['name'] + '</a>';
    } else {
        html += rData['name'];
    }
    html += '<br>' + lData['address'] + ' ' + lData['zipcode'];
    // We add extra padding here (the &nbsp;'s) so the info window will open
    // wider than it needs to be, so when the rating is replaced by stars
    // there will be extra room.
    html += '<br><span>Average rating:</span><span class="rating" id="avg' + i + '">' + rData['avgrating'][0] + '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span>' + getNumRatingsString(rData['avgrating'][1]);
    if (loggedIn) {
        if ('rating' in rUserData) {
            html += '<br>Your rating:</span><span class="rating" id="user' + rId + '">' + rUserData['rating'] + '</span>';
        } else {
            html += '<br>Your rating:</span><span class="rating" id="user' + rId + '">0</span>';
        }
        if (isAdmin || lData['owner'] == userId) {
            if ((i in tempRestaurantData) && ('draggable' in tempRestaurantData[i]) && tempRestaurantData[i]['draggable']) {
                html += '<br><a href="javascript:void(0);" onclick="stopDrag(' + i + ');">Disable dragging</a>'
            } else {
                html += '<br><a href="javascript:void(0);" onclick="startDrag(' + i + ');">Enable dragging</a>'
            }
        }
    }
    if (rData['style'] in RESTAURANT_STYLES) {
        html += '<br>Style: ' + RESTAURANT_STYLES[rData['style']];
    }
    if (lData['timetofood'] in TIME_TO_FOOD) {
        html += '<br>Average time to food: ' + TIME_TO_FOOD[lData['timetofood']];
    }
    if (rData['averageentreeprice'] > 0) {
        html += '<br>Average entree price: $' + rData['averageentreeprice'];
    }
    if (rData['locality'] in LOCALITY) {
        html += '<br>Locality: ' + LOCALITY[rData['locality']];
    }
    tags = [];
    if ('tags' in rUserData) {
        tags = tags.concat(rUserData['tags']);
    }
    if ('tags' in rData) {
        tags = tags.concat(rData['tags']);
    }
    if (tags.length > 0) {
        html += '<br>Tags: ' + tags.join(' ');
    }
    if (lData['nextPrev'][0] != 1) {
        html += '<br><a href="javascript:void(0);" onclick="showLocationMarker(' + lData['nextPrev'][2] + ');">Previous</a>&nbsp;&nbsp;&nbsp;&nbsp;' + lData['nextPrev'][0] + ' locations &nbsp;&nbsp;&nbsp;<a href="javascript:void(0);" onclick="showLocationMarker(' + lData['nextPrev'][1] + ');">Next</a>';
    }
    GEvent.addListener(marker, 'infowindowopen', function() {
        init_rating($('span#avg' + i)[0], false);
        if (loggedIn) {
            init_rating($('span#user' + rId)[0], true);
        }
        GEvent.clearListeners(this, 'infowindowopen');
    });
    marker.openInfoWindowHtml(html, {'maxWidth': 1000});
    loadRestaurantInfo(rId);
    if ($('#addRestaurantDialog').dialog("isOpen")) {
        editRestaurantLocationChange(i);
    }
    whichMarkerOpen = i;
}
function loadRestaurantInfo(rId) {
    incLoadingCount();
    $.ajax({
        type: "GET",
        url: baseURL + 'api/getrestaurantinfo/',
        data: {'restaurant': rId, 'csrfmiddlewaretoken': csrfToken, 'randomUnused': Math.random()},
        dataType: 'json',
        success: restaurantInfoSuccess,
        error: restaurantInfoError});
}
function hideMarkerWindow(marker, i) {
    if (whichMarkerOpen == i) {
        whichMarkerOpen = -1;
        location.hash = '#';
    }
}
var userRatingFunctions = [];
function registerUserRatingFunction(fn) {
    userRatingFunctions.push(fn);
}
function restaurantInfoError(data) {
    genericError('loginStatus', "Failure contacting server. Please try again.");
}
function unescapeComment(str) {
    str = str.replace(/&lt;/g, "<");
    str = str.replace(/&gt;/g, ">");
    str = str.replace(/&apos;/g, "'");
    str = str.replace(/&quot;/g, "\"");
    str = str.replace(/&amp;/g, "&");
    return str;
}
function escapeComment(str, isComment) {
    str = str.replace(/\&/g,"&amp;");
    str = str.replace(/\</g,"&lt;");
    str = str.replace(/\>/g,"&gt;");
    str = str.replace(/\'/g,"&apos;");
    str = str.replace(/\"/g,"&quot;");
    if (isComment) {
        str = str.replace(/\n/g,"<br>");
    }
    return str;
}
var timeLengths = [];
timeLengths.push(['second', 1000]);
timeLengths.push(['minute', timeLengths[0][1] * 60]);
timeLengths.push(['hour', timeLengths[1][1] * 60]);
timeLengths.push(['day', timeLengths[2][1] * 24]);
timeLengths.push(['week', timeLengths[3][1] * 7]);
timeLengths.push(['month', timeLengths[3][1] * 30]);
timeLengths.push(['year', timeLengths[3][1] * 365]);
function formatDate(d) {
    var date = new Date(d);
    //return "&nbsp;&nbsp;<i>" + date.toDateString() + " " + date.toTimeString() + "</i>";
    var now = new Date();
    var difference = now.getTime() - date.getTime();
    var friendlyDifference = '';
    for (var i = timeLengths.length - 1; i >= 0 && friendlyDifference == ''; --i) {
        // neato cast to integer here
        var num = (difference/timeLengths[i][1]) | 0;
        if (num > 0) {
            friendlyDifference = num + ' ' + timeLengths[i][0] + (num > 1 ? 's' : '');
        }
    }
    if (friendlyDifference == '') {
        friendlyDifference = 'less than 1 second';
    }

    //return "&nbsp;&nbsp;<i>" + date.toDateString() + " (" + friendlyDifference + " ago)</i>";
    return "&nbsp;&nbsp;<i>(" + friendlyDifference + " ago)</i>";
}
function restaurantInfoSuccess(data) {
    function getRatingId(user, rId) {
        return 'specificRating' + user['id'] + 'x' + rId;
    }
    decLoadingCount();
    if ('failure' in data) {
        $('#loginStatus').text(data['failure']);
    } else {
        var comments = data['comments'];
        var ratings = data['ratings'];
        var rId = data['restaurant'];
        var userRating = 0;
        if ('rating' in restaurantData[rId][1]) {
            userRating = restaurantData[rId][1]['rating'];
        }
        whichRestaurantDataShown = rId;
        var rData = restaurantData[rId][0];
        var htmlStringArray = ['<b>'];
        if (rData['url'] && rData['url'] != '') {
            htmlStringArray.push('<a href="' + rData['url'] + '">' + rData['name'] + '</a>');
        } else {
            htmlStringArray.push(rData['name']);
        }
        htmlStringArray.push('</b><br>');
        var usersWithComments = [];
        for (i in comments) {
            usersWithComments.push(comments[i]['user']['id']);
        }
        // show the rating distribution
        htmlStringArray.push('<table style="float: right;"><caption>All ratings</caption>');
        var barLength = 150;
        var totalRating = 0;
        for (var i = 4; i >= 0; --i) {
            totalRating += ratings[i];
        }
        for (var i = 4; i >= 0; --i) {
            var length = (totalRating == 0) ? 0 : (barLength * (ratings[i] / totalRating));
            htmlStringArray.push('<tr><td>');
            for (j = 0; j < (i + 1); ++j) {
                htmlStringArray.push('<img src="/djangomedia/img/stars/rating_tenths_10.png">');
            }
            for (j = 0; j < (4 - i); ++j) {
                htmlStringArray.push('<img src="/djangomedia/img/stars/rating_tenths_0.png">');
            }
            htmlStringArray.push(':</td><td><span class="ratingbars"><span class="' + (userRating == (i+1) ? 'ratingusers' : 'ratingon') + '" style="width: ' + length + 'px; left: 0px;"></span><span class="ratingoff" style="width: ' + (barLength - length) + 'px; left: ' + length + 'px;"></span></span></td><td>' + ((ratings[i] != 0) ? ('(' + ratings[i] + ')') : '') + '</td></tr>');
        }
        htmlStringArray.push('</table>');
        var ratingsToInit = [];
        for (i in comments) {
            var rating = comments[i]['userRating'];
            if (rating != 0) {
                ratingId = getRatingId(comments[i]['user'], rId) + 'x' + comments[i]['id'];
                htmlStringArray.push('<b>' + escapeComment(comments[i]['user']['username']) + '</b>' + '<span class="rating" id="' + ratingId + '">' + rating + '</span>' + escapeComment(comments[i]['comment'], true) + formatDate(comments[i]['createdTime']) + "<br>");
                ratingsToInit.push(ratingId);
            } else {
                htmlStringArray.push('<b>' + escapeComment(comments[i]['user']['username']) + '</b>: ' + escapeComment(comments[i]['comment'], true) + formatDate(comments[i]['createdTime']) + "<br>");
            }
        }
        /*for (i in ratings) {
            var user = ratings[i]['user'];
            if (!arrayContains(usersWithComments, user['id'])) {
                //ratingId = 'specificRating' + user['id'] + 'x' + rId;
                ratingId = getRatingId(user, rId);
                htmlStringArray.push('<b>' + escapeComment(user['username']) + '</b>:' + '<span class="rating" id="' + ratingId + '">' + ratings[i]['rating'] + '</span><br>');
                ratingsToInit.push(ratingId);
            }
        }*/
        if (loggedIn) {
            htmlStringArray.push('<table style="float: left;"><tr><td>Add comment:</td><td><textarea cols="48" rows="8" id="newComment"></textarea></td></tr><tr><td></td><td><button style="float: right;" onclick="addComment(' + rId + ');">Submit</button></td></tr></table><br clear="both">');
        }
        $('#restaurantInfo').html(htmlStringArray.join(''));
        for (i in ratingsToInit) {
            init_rating($('span#' + ratingsToInit[i])[0], false);
        }
    }
}
function addComment(rId) {
    var commentText = $('#newComment').val();
    // Don't need to preprocess here - we'll escapeComment() when we read it.
    incLoadingCount();
    $.ajax({
        type: "POST",
        url: baseURL + 'api/addcomment/',
        data: {'restaurant': rId, 'comment': commentText, 'csrfmiddlewaretoken' : csrfToken},
        dataType: 'json',
        success: addCommentSuccess,
        error: addCommentError
    });
}
function addCommentError(data) {
    genericError('loginStatus', "Failure contacting server. Please try again.");
}
function addCommentSuccess(data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#loginStatus').text(data['failure']);
    } else {
        loadRestaurantInfo(data['restaurant']);
    }
}
 
function rateError(data) {
    genericError('loginStatus', "Failure contacting server. Please try again.");
}
function rateSuccess(data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#loginStatus').text(data['failure']);
    } else {
        var lId = data['location_id'];
        var rId = data['restaurant_id'];
        var rating = data['rating'];
        // Calculate the new average rating.
        var oldAverage = restaurantData[rId][0]['avgrating'][0];
        var oldAverageNumber = restaurantData[rId][0]['avgrating'][1];
        var weightedTotal = oldAverage * oldAverageNumber;
        var newAverageRating;
        var newAverageNumber = oldAverageNumber;
        if (rating == 0) {
            if ('rating' in restaurantData[rId][1]) {
                weightedTotal -= restaurantData[rId][1]['rating'];
                newAverageNumber = oldAverageNumber - 1;
            }
        } else {
            if ('rating' in restaurantData[rId][1]) {
                weightedTotal += (rating - restaurantData[rId][1]['rating']);
            } else {
                weightedTotal += rating;
                newAverageNumber = oldAverageNumber + 1;
            }
        }
        newAverageRating = weightedTotal / newAverageNumber;
        // Update the restaurant data
        restaurantData[rId][0]['avgrating'][0] = newAverageRating;
        restaurantData[rId][0]['avgrating'][1] = newAverageNumber;
        if (rating != 0) {
            restaurantData[rId][1]['rating'] = rating;
        } else {
            delete restaurantData[rId][1]['rating'];
        }
        if (whichMarkerOpen in locationData && (locationData[whichMarkerOpen]['restaurant'] == rId)) {
            // If we're going to reshow this guy anyway, don't do it here.
            // We have to cheat a little and look ahead to whether the color
            // will actually change.
            if (!(getCriteriaInfo(coloringCriteria)['changesWithUserRating'] && !markerHasColor(whichMarkerOpen, getDesiredMarkerColor(coloringCriteria, whichMarkerOpen)))) {
                // Update the marker text - easiest way is to close and reshow.
                markers[whichMarkerOpen].closeInfoWindow();
                showMarkerWindow(markers[whichMarkerOpen], i);
            }
        }
        if (whichRestaurantDataShown == rId) {
            // Update the restaurant data below (comments, etc.)
            loadRestaurantInfo(rId);
        }
        for (j in userRatingFunctions) {
            userRatingFunctions[j](rId);
        }
    }
}

function submitRating(evt) {
    // tmp is now "<some div id>number_number"
    // first number is restaurant id
    // second number is new rating
    var tmp = evt.target.getAttribute('id').substr(5);
    // Find the first number and start there.
    tmp = tmp.substr(tmp.search(/[-0-9]/));
    var restaurantId = parseInt(tmp.substr(0, tmp.indexOf('_')));
    var starNbr = parseInt(tmp.substr(tmp.indexOf('_')+1));
    //var restaurantId = locationData[locationId][0]['restaurant'];
    incLoadingCount();
    $.ajax({
        type: "POST",
        url: baseURL + 'api/raterestaurant/',
        data: {'restaurant': restaurantId, 'rating': starNbr, 'csrfmiddlewaretoken' : csrfToken},
        dataType: 'json',
        success: rateSuccess,
        error: rateError
    });
}

function getArbitraryLocationId(restaurantId) {
    for (i in locationData) {
        if (locationData[i][0]['restaurant'] == restaurantId) {
            return i;
        }
    }
    return -1;
}

function calculateNextPrevLocations() {
    restaurantsToLocations = {};
    for (i in locationData) {
        var rId = locationData[i][0]['restaurant'];
        if (!(rId in restaurantsToLocations)) {
            restaurantsToLocations[rId] = [];
        }
        restaurantsToLocations[rId].push(parseInt(i));
    }
    for (rId in restaurantsToLocations) {
        var locations = restaurantsToLocations[rId];
        if (locations.length == 1) {
            locationData[locations[0]][0]['nextPrev'] = [1, -1, -1];
        } else {
            locations.sort(function(a,b) { return locationData[a][0]['latitude'] - locationData[b][0]['latitude']; });
            for (i = 0; i < locations.length; ++i) {
                var next = (i < locations.length - 1) ? (i + 1) : 0;
                var prev = (i > 0) ? (i - 1) : (locations.length - 1);
                locationData[locations[i]][0]['nextPrev'] = [locations.length, locations[next], locations[prev]];
            }
        }
    }

}

function showLocationMarker(locationId) {
    showMarkerWindow(markers[locationId], locationId);
}
function showRestaurantMarker(restaurantId) {
    // FFV - don't know how to choose which one, really.
    var i = getArbitraryLocationId(restaurantId);
    showMarkerWindow(markers[i], i);
}

function getMarkerPathFromColor(color) {
    return "/djangomedia/img/markers/mm_20_" + color + ".png";
}

function getMarkerPathFromValue(value) {
    return getMarkerPathFromColor(iconNames[value]);
}

var gicons = [];
var iconNames = ['white', 'blue', 'green', 'yellow', 'orange', 'red'];
for (i in iconNames) {
    var tinyIcon = new GIcon();
    tinyIcon.image = getMarkerPathFromValue(i);
    tinyIcon.shadow = "/djangomedia/img/markers/mm_20_shadow.png";
    tinyIcon.iconSize = new GSize(12, 20);
    tinyIcon.shadowSize = new GSize(22, 20);
    tinyIcon.iconAnchor = new GPoint(6, 20);
    tinyIcon.infoWindowAnchor = new GPoint(5, 1);
    gicons.push(tinyIcon);
}
function addMarkerCommon(i, marker) {
    marker.disableDragging();
    GEvent.addListener(marker, "click", function() {
        showMarkerWindow(marker, i);
    });
    GEvent.addListener(marker, "infowindowbeforeclose", function() {
        hideMarkerWindow(marker, i);
    });
    markers[i] = marker;
    map.addOverlay(marker);
}
function addMarker(i) {
    var marker = new GMarker(new GLatLng(locationData[i][0].latitude, locationData[i][0].longitude), {'draggable': true, 'icon': gicons[5]});
    addMarkerCommon(i, marker);
}
function reAddMarker(i) {
    map.removeOverlay(markers[i]);
    addMarker(i);
}
function removeMarker(i) {
    map.removeOverlay(markers[i]);
    delete markers[i];
}
function markerHasColor(i, testColor) {
    return (gicons[testColor] == markers[i].getIcon());
}
function changeMarkerColor(i, newValue) {
    // Don't do anything if we're already that color.
    if (!markerHasColor(i, newValue) && (getMarkerVisibility(i) == true)) {
        var reshowMarker = (whichMarkerOpen == i);
        var existingMarker = markers[i];
        var newMarker = new GMarker(existingMarker.getLatLng(), {'draggable': true, 'icon': gicons[newValue]});
        addMarkerCommon(i, newMarker);
        map.removeOverlay(existingMarker);
        if (reshowMarker) {
            // Reopen the marker
            showMarkerWindow(markers[i], i);
        }
    }
}
function getMarkerVisibility(i) {
    return !(markers[i].isHidden());
}
function setMarkerVisibility(i, visibility, force) {
    // Don't do anything if we're already displayed/hidden.
    var currentVisibility = getMarkerVisibility(i);
    if (visibility != currentVisibility || force) {
        var rId = locationData[i][0]['restaurant'];
        if (visibility) {
            // Show location on map.
            markers[i].show();
            // Make sure color's OK.
            changeMarkerColor(i, getDesiredMarkerColor(coloringCriteria, i));
            // Show restaurant in sidebar
            $('.tabRestaurantSpan' + rId).show();
        } else {
            // Hide location in map
            markers[i].hide();
            // Make sure all other locations of this restaurant are
            // hidden before actually hiding.
            var doHide = true;
            for (var j in locationData) {
                if (locationData[j][0]['restaurant'] == rId && getMarkerVisibility(j) == true) {
                    doHide = false;
                }
            }
            if (doHide) {
                // Hide restaurant in sidebar
                $('.tabRestaurantSpan' + rId).hide();
            }
        }
    }
}

