// From http://www.slideshare.net/RossC0/javascript-and-jquery-march-2008/
Function.prototype.curry = function() {
    var fn = this, args = Array.prototype.slice.call(arguments);
    return function() {
        return fn.apply(this, args.concat(Array.prototype.slice.call(arguments)));
    }
}
// From http://ajaxian.com/archives/currying-in-javascript
/*function curry (fn, scope) {
    var scope = scope || window;
    var args = [];
    for (var i = 2, len = arguments.length; i < len; ++i) {
        args.push(arguments[i]);
    }
    return function() {
        return fn.apply(scope, args.concat(Array.prototype.slice.call(arguments)));
    };
}*/


var numShown = 0;
var selectedTab = '';

function getNumRatingsString(numRatings) {
    return '(' + numRatings + ' rating' + (numRatings == 1 ? '' : 's') + ')';
}

function getRestaurantHtml(rId, showGlobal, idPrefix, ratingToShow, spanPrefix, addBr) {
    spanPrefix = spanPrefix || '';
    addBr = addBr || false;
    // Can't show non-global data if not logged in.
    if (!loggedIn) {
        showGlobal = true;
    }
    if (typeof ratingToShow == 'undefined') {
        // Calculate the default rating
        if (showGlobal) {
            ratingToShow = restaurantData[rId][0]['avgrating'][0];
        } else {
            if ('rating' in restaurantData[rId][1]) {
                ratingToShow = restaurantData[rId][1]['rating'];
            } else {
                ratingToShow = 0;
            }
        }
    }
    // This code must be kept in sync with the code in sidepanel.html (for
    // the topRatedTab)
    return '<span style="white-space: nowrap;"' + ((spanPrefix != '') ? (' class="' + spanPrefix + rId + '"') : '') + '><a href="javascript:void(0);" onclick="showRestaurantMarker(' + rId + ')">' + restaurantData[rId][0]['name'] + '</a>:' +
        '<span class="rating" id="' + idPrefix + rId + '" title="' + ratingToShow + ' stars">' + ratingToShow + 
        '</span>' +
        (showGlobal ? ('<span>' + getNumRatingsString(restaurantData[rId][0]['avgrating'][1]) + '</span>') : '') +
        (addBr ? '<br>' : '') + '</span>';
}
// If you pass needToShowTabs here, we won't update the global data.
function updateTopRestaurants(needToShowTabs) {
    incLoadingCount();
    $.ajax({
        type: 'GET',
        url: baseURL + 'api/gettopratedrestaurants/',
        data: {'totalnumber': -1, 'usernumber': -1, 'cityId': cityId, 'randomUnused': Math.random()},
        dataType: 'json',
        success: topRestaurantSuccess.curry(needToShowTabs),
        //success: curry(topRestaurantSuccess, window, needToShowTabs),
        error: topRestaurantError
    });
}

function updateSuggestions(needToShowTabs) {
    incLoadingCount();
    $.ajax({
        type: 'GET',
        url: baseURL + 'api/getsuggestions/',
        data: {'randomUnused': Math.random(), 'cityId': cityId},
        dataType: 'json',
        success: suggestionsSuccess.curry(needToShowTabs),
        //success: curry(suggestionsSuccess, window, needToShowTabs),
        error: suggestionsError
    });
}

function updateTags() {
    incLoadingCount();
    $.ajax({
        type: 'GET',
        url: baseURL + 'api/gettags/',
        data: {'cityId': cityId, 'randomUnused': Math.random()},
        dataType: 'json',
        success: updateTagsSuccess,
        error: updateTagsError
    });
}

function topRestaurantError(data) {
    decLoadingCount();
    genericError('loginStatus', 'Failure contacting server.  Please try again. (function: topRestaurant)');
}
function suggestionsError(data) {
    decLoadingCount();
    genericError('loginStatus', 'Failure contacting server.  Please try again. (function: suggestions)');
}
function updateTagsError(data) {
    decLoadingCount();
    genericError('loginStatus', 'Failure contacting server.  Please try again. (function: updateTags)');
}

var tagIdsToInitRating = []
function tagTabShown() {
    for (i in tagIdsToInitRating) {
        init_rating($('span#' + tagIdsToInitRating[i])[0], loggedIn);
    }
    tagIdsToInitRating = [];
}

var suggestionIdsToInitRating = [];
function suggestionsTabShown() {
    for (i in suggestionIdsToInitRating) {
        init_rating($('span#' + suggestionIdsToInitRating[i])[0], loggedIn);
    }
    suggestionIdsToInitRating = [];
}
function updateTagsSuccess(data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#tagsTab').html(data['failure']);
    } else {
        var allTagData = data['data'];
        globalTagData = {};
        $('#tagsTab').html('');
        tagIdsToInitRating = [];
        for (i in allTagData) {
            var tagData = allTagData[i];
            globalTagData[tagData['tagname']] = [];
            $('#tagsTab').append('Tag: ' + tagData['tagname'] + '<br>');
            // list and join for efficiency
            var toAdd = [];
            for (j in tagData['global']) {
                toAdd.push(getRestaurantHtml(tagData['global'][j], false, 'tagUserx' + tagData['tagid'] + 'x' + j + 'x', undefined, 'tabRestaurantSpan', true));
                tagIdsToInitRating.push('tagUserx' + tagData['tagid'] + 'x' + j + 'x' + tagData['global'][j]);
                globalTagData[tagData['tagname']].push(tagData['global'][j]);
            }
            $('#tagsTab').append(toAdd.join(''));
        }
        recalculateFiltering(true);
        // if tags tab is shown, call tagTabShown()
        if (selectedTab == '#tagsTab') {
            tagTabShown();
        }
    }
}

function suggestionsSuccess(needToShowTabs, data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#suggestionsTab').html(data['failure']);
    } else {
        if (needToShowTabs && !showingSuggestionsTab) {
            showingSuggestionsTab = true;
            $('#suggestionsTab').css('display', 'block');
            $('#sidebarTabs').tabs('add', '#suggestionsTab', 'Suggested');
        }
        $('#suggestionsTab').html('');
        for (i in data['suggestions']) {
            $('#suggestionsTab').append(getRestaurantHtml(data['suggestions'][i]['rId'], false, 'suggestion', data['suggestions'][i]['rating'], 'tabRestaurantSpan', true));
            suggestionIdsToInitRating.push('suggestion' + data['suggestions'][i]['rId']);
        }
        if (suggestionIdsToInitRating.length == 0) {
            $('#suggestionsTab').html('<p>No suggestions available.  Try rating more restaurants!</p>');
        }
        if (needToShowTabs) {
            $('#sidebarTabs').tabs();
            $('#sidebarTabs').tabs('select', '#suggestionsTab');
            $('#sidebarTabs').tabs('select', 0);
        }
        // if suggestions tab is shown, call suggestionsTabShown()
        if (selectedTab == '#suggestionsTab') {
            suggestionsTabShown();
        }
    }
}
var showingUserTab = false;
var showingSuggestionsTab = false;
function topRestaurantSuccess(needToShowTabs, data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#userRatingsTabHeader').css('display', 'none');
        $('#userRatingsTab').css('display', 'none');
        $('#topRatedTab').html(data['failure']);
    } else {
        var userRatingsToDo = [];
        var ratingsToDo = [];
        if ('user' in data) {
            if (needToShowTabs && !showingUserTab) {
                showingUserTab = true;
                $('#userRatingsTab').css('display', 'block');
                $('#sidebarTabs').tabs('add', '#userRatingsTab', 'Your ratings', 1);
            }
            $('#userRatingsTab').html('');
            // list and join for efficiency
            var toAdd = [];
            for (i in data['user']) {
                toAdd.push(getRestaurantHtml(data['user'][i]['id'], false, 'tabUser', undefined, 'tabRestaurantSpan', true));
                userRatingsToDo.push(data['user'][i]['id']);
            }
            $('#userRatingsTab').append(toAdd.join(''));
        } else {
            if (needToShowTabs) {
                if (showingUserTab) {
                    showingUserTab = false;
                    $('#userRatingsTabHeader').css('display', 'none');
                    $('#sidebarTabs').tabs('remove', 1);
                }
                if (showingSuggestionsTab) {
                    showingSuggestionsTab = false;
                    $('#suggestionsTabHeader').css('display', 'none');
                    $('#sidebarTabs').tabs('remove', 4);
                }
            }
        }
        if (!needToShowTabs) {
            $('#topRatedTab').html('');
            // This code must be kept in sync with the code in sidepanel.html (for
            // the topRatedTab)
            // list and join for efficiency
            var toAdd = [];
            for (i in data['overall']) {
                toAdd.push(getRestaurantHtml(data['overall'][i]['id'], true, 'tabAvg', undefined, 'tabRestaurantSpan') + '<br>');
                ratingsToDo.push(data['overall'][i]['id']);
            }
            $('#topRatedTab').append(toAdd.join(''));
        }
        if (needToShowTabs) {
            $('#sidebarTabs').tabs();
            $('#sidebarTabs').tabs('select', 1);
            $('#sidebarTabs').tabs('select', 0);
        }
        for (i in userRatingsToDo) {
            init_rating($('span#tabUser' + userRatingsToDo[i])[0], true);
        }
        for (i in ratingsToDo) {
            init_rating($('span#tabAvg' + ratingsToDo[i])[0], false);
        }
    }
}

var coloringCriteria = 'avg_rating';
function changeCriteria(criteria) {
    if (coloringCriteria != criteria) {
        coloringCriteria = criteria;
        recalculateColoring();
    }
}

function getDesiredMarkerColor(criteria, i) {
    var dataindex = 0;
    var property = '';
    var value = 0;
    var rId = locationData[i][0]['restaurant'];
    // Alternative is to use restaurant data.
    var useLocationData = false;
    switch (criteria) {
        case 'user_rating':
            dataindex = 1;
            property = 'rating';
            break;
        case 'avg_rating':
            property = 'avgrating';
            break;
        case 'timetofood':
            property = 'timetofood';
            useLocationData = true;
            break;
        case 'style':
            property = 'style';
            break;
        case 'locality':
            property = 'locality';
            break;
        case 'price':
            property = 'averageentreeprice';
            break;
        default:
            return;
    }
    if (useLocationData) {
        if (property in locationData[i][0]) {
            value = locationData[i][0][property];
        }
    } else {
        if (property in restaurantData[rId][dataindex]) {
            value = restaurantData[rId][dataindex][property];
        }
    }
    // Massage data
    switch (criteria) {
        case 'avg_rating':
            value = value[0];
            break;
        case 'timetofood':
            if (value == -1) {
                value = 0;
            } else {
                value = value / 5;
                value = value + 1;
                if (value > 5) {
                    value = 5;
                }
            }
            break;
        case 'style':
            switch (value) {
                case '':
                    value = 0;
                    break;
                case 'ff':
                    value = 1;
                    break;
                case 'cf':
                    value = 2;
                    break;
                case 'bf':
                    value = 4;
                    break;
                case 'sd':
                    value = 5;
                    break;
            }
            break;
        case 'locality':
            switch (value) {
                case '':
                    value = 0;
                    break;
                case 'city':
                    value = 1;
                    break;
                case 'state':
                    value = 2;
                    break;
                case 'region':
                    value = 4;
                    break;
                case 'all':
                    value = 5;
                    break;
            }
            break;
        case 'price':
            if (value == -1) {
                value = 0;
            } else {
                if (value <= 4) {
                    value = 1;
                } else if (value <= 7) {
                    value = 2;
                } else if (value <= 10) {
                    value = 3;
                } else if (value <= 15) {
                    value = 4;
                } else {
                    value = 5;
                }
            }
            break;
    }
    value = Math.round(value);
    // Make sure we're in range
    if (value < 0 || value > 5) {
        value = 0;
    }

    return value;
}

function recalculateColoring() {
    for (var i in locationData) {
        changeMarkerColor(i, getDesiredMarkerColor(coloringCriteria, i));
    }
    updateCriteriaInfo();
}

function recalculateColoringAfterUserRating(restaurantId) {
    if (getCriteriaInfo(coloringCriteria)['changesWithUserRating']) {
        for (var i in locationData) {
            if (locationData[i][0]['restaurant'] == restaurantId) {
                changeMarkerColor(i, getDesiredMarkerColor(coloringCriteria, i));
            }
        }
    }
}

function getCriteriaInfo(criteria) {
    info = {'used': [true, true, true, true, true, true], 'changesWithUserRating': false}
    switch (criteria) {
        case 'user_rating':
        case 'avg_rating':
            info['labels'] = ['None', '1', '2', '3', '4', '5'];
            info['changesWithUserRating'] = true;
            break;
        case 'timetofood':
            info['labels'] = ['Unknown', '0-4 mins', '5-9 mins', '10-14 mins', '15-19 mins', '20+ mins'];
            break;
        case 'style':
            info['used'][3] = false;
            info['labels'] = ['Unknown', 'Fast food', 'Counter service', '', 'Buffet', 'Sit down'];
            break;
        case 'locality':
            info['used'][3] = false;
            info['labels'] = ['Unknown', 'City', 'State', '', 'Multi-state region', 'Everywhere'];
            break;
        case 'price':
            info['labels'] = ['Unknown', '$0-4', '$5-7', '$8-10', '$11-15', '$16+'];
    }
    return info;
}

function updateCriteriaInfo() {
    info = getCriteriaInfo(coloringCriteria);
    $('#criteriaInfo').html('');
    // We use a list and join for efficiency.
    var newHtml = [];
    newHtml.push('<table><tr>');
    for (var i = 0; i <= 5; ++i) {
        if (info['used'][i]) {
            newHtml.push('<td><img src="' + getMarkerPathFromValue(i) + '"></td>');
        }
    }
    newHtml.push('</tr><tr>');
    for (var i = 0; i <= 5; ++i) {
        if (info['used'][i]) {
            newHtml.push('<td>' + info['labels'][i] + '</td>');
        }
    }
    newHtml.push('</tr></table>');
    $('#criteriaInfo').html(newHtml.join(''));
}

var filteringCriteria = {}
var filteringInfos = [['Average rating', 'avg_rating'], ['User rating', 'user_rating'], ['Time to food', 'timetofood'], ['Style', 'style'], ['Locality','locality'], ['Price', 'price']];
var tagFilteringCriteria = {};
function initFilterInfo() {
    for (i in filteringInfos) {
        filteringCriteria[filteringInfos[i][1]] = [0, 5, true];
    }
    var numLocations = 0;
    for (i in locationData) {
        numLocations++;
        tagFilteringCriteria[i] = 1;
    }
    $('#numMarkersShown').text(numLocations + ' ');
    updateNumTotalMarkers(numLocations);
}
function updateNumTotalMarkers(numLocations) {
    if (numLocations == undefined) {
        numLocations = 0;
        for (i in locationData) {
            numLocations++;
        }
    }
    $('#numTotalMarkers').text(numLocations);
}
function recalculateFilteringOnLocation(i, force) {
    var visible = true;
    for (curType in filteringCriteria) {
        // Ignore if the criteria is user_rating and we're not logged in.
        if (!(!loggedIn && curType == 'user_rating')) {
            var color = getDesiredMarkerColor(curType, i);
            if ((color != 0 && !(filteringCriteria[curType][0] <= color && color <= filteringCriteria[curType][1])) || (color == 0 && !filteringCriteria[curType][2])) {
                visible = false;
                break;
            }
        }
    }
    if (visible && (!(i in tagFilteringCriteria))) {
        visible = false;
    }
    setMarkerVisibility(i, visible, force);
    return visible;
}
function showHideUserRatingControls() {
    if (loggedIn) {
        $('#userRatingColoringP').css('display', 'block');
        $('#user_ratingSliderTable').css('display', 'block');
    } else {
        $('#userRatingColoringP').css('display', 'none');
        $('#user_ratingSliderTable').css('display', 'none');
    }
}
function recalculateFiltering(force) {
    numShown = 0;
    for (i in locationData) {
        if (recalculateFilteringOnLocation(i, force)) {
            numShown++;
        }
    }
    $('#numMarkersShown').text(numShown + ' ');
}
function recalculateFilteringAfterUserRating(restaurantId) {
   for (var i in locationData) {
        if (locationData[i][0]['restaurant'] == restaurantId) {
            var curVisibility = getMarkerVisibility(i);
            recalculateFilteringOnLocation(i, false);
            var newVisibility = getMarkerVisibility(i);
            if (curVisibility && !newVisibility) {
                numShown--;
            } else if (!curVisibility && newVisibility) {
                numShown++;
            }
        }
    }
    $('#numMarkersShown').text(numShown + ' ');
}
function changeFilterUnknown(type, unknown, force) {
    var doChange = force;
    if (!doChange && (filteringCriteria[type][2] != unknown)) {
        doChange = true;
    }
    if (doChange) {
        filteringCriteria[type][2] = unknown;
        recalculateFiltering(false);
    }

}
function changeFilter(type, val1, val2, force) {
    var doChange = force;
    if (!doChange && (filteringCriteria[type][0] != val1 || filteringCriteria[type][1] != val2)) {
        doChange = true;
    }
    if (doChange) {
        filteringCriteria[type][0] = val1;
        filteringCriteria[type][1] = val2;
        recalculateFiltering(false);
    }
}

function clearAllFilters() {
    for (var i in filteringCriteria) {
        if (filteringCriteria[i][1] != 5) {
            $('#' + i + 'Slider').slider("values", 1, 5);
        }
        if (filteringCriteria[i][0] != 0) {
            $('#' + i + 'Slider').slider("values", 0, 0);
        }
        if (filteringCriteria[i][2] != true) {
            $('#' + i + 'checkbox').attr('checked', 'checked');
            // Because this callback is on click(), call it manually.
            changeFilterUnknown(i, true, false);
        }
        // Turn off all the tags.
        $('#filterWithTags').attr('checked', '');
        for (tagName in globalTagData) {
            var elem = $('#filterTag' + tagName);
            if (elem.hasClass('tagOn')) {
                elem.removeClass('tagOn');
            }
            if (!elem.hasClass('tagOff')) {
                elem.addClass('tagOff');
            }

        }
        applyTagFilter();
    }
}

function setColoringTypeOnLogin() {
    if (loggedIn) {
        if (coloringCriteria == 'avg_rating') {
            coloringCriteria = 'user_rating';
            $("#criteriaUserRating").attr('checked', 'checked');
        }
    } else {
        if (coloringCriteria == 'user_rating') {
            coloringCriteria = 'avg_rating';
            $("#criteriaAvgRating").attr('checked', 'checked');
        }
    }
}

function editExistingRestaurantChange() {
    var rId = $('#editExistingRestaurant').val(); 
    var rData = restaurantData[rId][0];
    $('#editRestaurantUrl').val(('url' in rData) ? rData['url'] : '');
    $('#editRestaurantName').val(unescapeComment(rData['name']));
    $('#editRestaurantStyle option[value="' + rData['style'] + '"]').attr('selected', 'selected');
    $('#editRestaurantEntreePrice').val(('averageentreeprice' in rData && rData['averageentreeprice'] != '-1') ? rData['averageentreeprice'] : '');
    $('#editRestaurantLocality option[value="' + ('locality' in rData ? rData['locality'] : '') + '"]').attr('selected', 'selected');
    var foundTags = {};
    for (i in rData['tags']) {
        var tagName = rData['tags'][i];
        foundTags[tagName] = 1;
        var elem = $('#editRestaurantTag' + tagName);
        if (elem.hasClass('tagOff')) {
            elem.removeClass('tagOff');
        }
        if (!elem.hasClass('tagOn')) {
            elem.addClass('tagOn');
        }
    }
    for (tagName in globalTagData) {
        if (!(tagName in foundTags)) {
            var elem = $('#editRestaurantTag' + tagName);
            if (elem.hasClass('tagOn')) {
                elem.removeClass('tagOn');
            }
            if (!elem.hasClass('tagOff')) {
                elem.addClass('tagOff');
            }
        }
    }
}

function editRestaurantLocationChange(lId) {
    var lData = locationData[lId][0];
    if (isAdmin || lData['owner'] == userId) {
        var rId = lData['restaurant'];
        $('#editRestaurantLocationId').val(lId);
        $('#editRestaurantLocation option[value="' + rId + '"]').attr('selected', 'selected');
        $('#editLocationAddress').val(lData['address']);
        $('#editLocationZipcode').val(lData['zipcode']);
        $('#editLocationTimeToFood option[value="' + (('timetofood' in lData && lData['timetofood'] != -1) ? lData['timetofood'] : '')+ '"]').attr('selected', 'selected');
        $('#editRestaurantLocationStatus').removeClass('invalidForm');
        $('#editLocationCommit').attr('disabled', false);
        $('#deleteExistingLocationCommit').attr('disabled', false);
    } else {
        $('#editRestaurantLocationStatus').addClass('invalidForm');
        $('#editRestaurantLocationStatus').html("You do not have permission to edit this location.");
        $('#editLocationCommit').attr('disabled', true);
        $('#deleteExistingLocationCommit').attr('disabled', true);
    }
}

function addRestaurantClose() {
    if (dialogCreated && $('#addRestaurantDialog').dialog("isOpen")) {
        $('#restaurantNameInvalid').css('display', 'none');
        $('#restaurantEntreePriceInvalid').css('display', 'none');
        $('#editRestaurantNameInvalid').css('display', 'none');
        $('#editRestaurantEntreePriceInvalid').css('display', 'none');
        $('#newLocationAddressInvalid').css('display', 'none');
        $('#newLocationZipcodeInvalid').css('display', 'none');
        $('#editLocationAddressInvalid').css('display', 'none');
        $('#editLocationZipcodeInvalid').css('display', 'none');
        $('#createRestaurantLocationStatus').html();
        $('#createRestaurantLocationStatus').removeClass('invalidForm');
        $('#createRestaurantStatus').html();
        $('#createRestaurantStatus').removeClass('invalidForm');
        $('#editRestaurantStatus').html();
        $('#editRestaurantStatus').removeClass('invalidForm');
        $('#editRestaurantLocationStatus').html();
        $('#editRestaurantLocationStatus').removeClass('invalidForm');

        $('#restaurantName').val('');
        $('#restaurantUrl').val('');
        $('#restaurantStyle option[value="ff"]').attr('selected', 'selected');
        $('#restaurantEntreePrice').val('');
        $('#restaurantLocality option[value=" "]').attr('selected', 'selected');
        $('#newLocationAddress').val('');
        $('#newLocationZipcode').val('');
        $('#newLocationTimeToFood option[value=" "]').attr('selected', 'selected');
        editExistingRestaurantChange();
        $('#editRestaurantLocationId').val('');
        $('#editLocationAddress').val('');
        $('#editLocationZipcode').val('');
        $('#editLocationRecalculateLocation').attr('checked', '');
        $('#editLocationTimeToFood option[value=" "]').attr('selected', 'selected');
        $('#editLocationCommit').attr('disabled', false);
        $('#deleteExistingLocationCommit').attr('disabled', false);
    }
}

$(window).unload(addRestaurantClose);

function updateExistingRestaurants() {
    $('#existingRestaurant').empty();
    $('#editExistingRestaurant').empty();
    $('#editRestaurantLocation').empty();
    var toInsert = [];
    var toInsertEdit = [];
    var restaurants = [];
    for (rId in restaurantData) {
        restaurants.push([rId, restaurantData[rId][0]['name']]);
    }
    restaurants.sort(function(a,b) { if (a[1] < b[1]) return -1; if (a[1] > b[1]) return 1; return 0;});
    var canEditOne = false;
    for (r in restaurants) {
        toInsert.push('<option value="' + restaurants[r][0] + '">' + restaurants[r][1] + '</option>');
        if (isAdmin || restaurantData[restaurants[r][0]][0]['owner'] == userId) {
            toInsertEdit.push('<option value="' + restaurants[r][0] + '">' + restaurants[r][1] + '</option>');
            canEditOne = true;
        }
    }
    $('#existingRestaurant').append(toInsert.join(''));
    $('#editRestaurantLocation').append(toInsert.join(''));
    $('#editExistingRestaurant').append(toInsertEdit.join(''));
    $('#editExistingRestaurant').attr('disabled', !canEditOne);
    $('#editExistingRestaurantCommit').attr('disabled', !canEditOne);
    $('#deleteExistingRestaurantCommit').attr('disabled', !canEditOne);
    if (canEditOne) {
        editExistingRestaurantChange();
    }
}

var dialogCreated = false;
function showAddRestaurant() {
    //$('#addRestaurantDialog').dialog({title: 'Add a restaurant', width: 500, height: 360, modal: true, overlay: {opacity: 0.5, background: 'black'}, close: addRestaurantClose});
    if (!dialogCreated) {
        $('#addRestaurantDialog').dialog({title: 'Add a restaurant', width: 575, height: 450, modal: false, close: addRestaurantClose});
        $('#addRestaurantDialogTabs').tabs();
        addDialogTags();
    }
    if (!dialogCreated || !$('#addRestaurantDialog').dialog("isOpen")) {
        updateExistingRestaurants();
        $('#editLocationCommit').attr('disabled', true);
        $('#deleteExistingLocationCommit').attr('disabled', true);
    }
    if (!$('#addRestaurantDialog').dialog("isOpen")) {
        $('#addRestaurantDialog').dialog("open");
    }
    dialogCreated = true;
}


function checkAddLocation() {
    var isValid = true;
    var restaurant = $('#existingRestaurant').val(); 
    var address = jQuery.trim($('#newLocationAddress').val());
    if (address == '') {
        $('#newLocationAddressInvalid').css('display', 'inline');
        isValid = false;
    } else {
        $('#newLocationAddressInvalid').css('display', 'none');
    }
    var zipcode = jQuery.trim($('#newLocationZipcode').val());
    if (zipcode == '') {
        $('#newLocationZipcodeInvalid').css('display', 'inline');
        isValid = false;
    } else {
        $('#newLocationZipcodeInvalid').css('display', 'none');
    }
    var timetofood = $('#newLocationTimeToFood').val();
    if (timetofood == ' ') {
        timetofood = '';
    }
    if (isValid) {
        addLocation({'restaurant': restaurant, 'address': address, 'city': cityId, 'zipcode': zipcode, 'timetofood': timetofood});
    }
}

function addLocation(postData) {
    incLoadingCount();
    postData['csrfmiddlewaretoken'] = csrfToken;
    $.ajax({
        type: 'POST',
        url: baseURL + 'api/addrestaurantlocation/',
        data: postData,
        dataType: 'json',
        success: addLocationSuccess,
        error: addLocationError
    });
}

function addLocationSuccess(data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#createRestaurantLocationStatus').addClass('invalidForm');
        $('#createRestaurantLocationStatus').html(data['failure']);
    } else {
        $('#createRestaurantLocationStatus').removeClass('invalidForm');
        $('#createRestaurantLocationStatus').html("Successfully added location!");
        // Update our info
        var locationToShow = -1;
        // We should only get one of these...
        for (i in data) {
            locationData[i] = data[i];
            addMarker(i);
            locationToShow = i;
        }
        calculateNextPrevLocations();
        applyTagFilter();
        recalculateFiltering(true);
        updateNumTotalMarkers();
        recalculateColoring();
        updateTopRestaurants(false);
        updateSuggestions(false);
        if (locationToShow != -1) {
            // Close the dialog and show the marker.
            $('#addRestaurantDialog').dialog("close");
            showLocationMarker(locationToShow);
        }
    }
}

function addLocationError(data) {
    decLoadingCount();
    genericError('loginStatus', 'Failure contacting server.  Please try again. (function: addLocation)');
    $('#createRestaurantLocationStatus').addClass('invalidForm');
    $('#createRestaurantLocationStatus').html('Failure contacting server.  Please try again. (function: addLocation)');
}

function checkEditLocation() {
    var isValid = true;
    var restaurant = $('#editRestaurantLocation').val(); 
    var address = jQuery.trim($('#editLocationAddress').val());
    if (address == '') {
        $('#editLocationAddressInvalid').css('display', 'inline');
        isValid = false;
    } else {
        $('#editLocationAddressInvalid').css('display', 'none');
    }
    var zipcode = jQuery.trim($('#editLocationZipcode').val());
    if (zipcode == '') {
        $('#editLocationZipcodeInvalid').css('display', 'inline');
        isValid = false;
    } else {
       $('#editLocationZipcodeInvalid').css('display', 'none');
    }
    var recalculateLocation = $('#editLocationRecalculateLocation').attr('checked');
    var timetofood = $('#editLocationTimeToFood').val();
    if (timetofood == ' ') {
        timetofood = '';
    }
    var lId = $('#editRestaurantLocationId').val();
    if (isValid) {
        editLocation({'id': lId, 'recalculatelocation': (recalculateLocation ? 1 : 0), 'restaurant': restaurant, 'address': address, 'city': 1, 'zipcode': zipcode, 'timetofood': timetofood});
    }
}

function editLocation(postData) {
    incLoadingCount();
    postData['csrfmiddlewaretoken'] = csrfToken;
    $.ajax({
        type: 'POST',
        url: baseURL + 'api/editrestaurantlocation/',
        data: postData,
        dataType: 'json',
        success: editLocationSuccess,
        error: editLocationError
    });
}

function editLocationSuccess(data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#editRestaurantLocationStatus').addClass('invalidForm');
        $('#editRestaurantLocationStatus').html(data['failure']);
    } else {
        $('#editRestaurantLocationStatus').removeClass('invalidForm');
        $('#editRestaurantLocationStatus').html("Successfully changed location!");
        // Update our info
        var locationToShow = -1;
        // We should only get one of these...
        for (var i in data) {
            locationData[i] = data[i];
            reAddMarker(i);
            locationToShow = i;
        }
        calculateNextPrevLocations();
        recalculateColoring();
        if (locationToShow != -1) {
            // Close the dialog and show the marker.
            //$('#addRestaurantDialog').dialog("close");
            showLocationMarker(locationToShow);
        }
    }
}

function editLocationError(data) {
    decLoadingCount();
    genericError('loginStatus', 'Failure contacting server.  Please try again. (function: editLocation)');
    $('#editRestaurantLocationStatus').addClass('invalidForm');
    $('#editRestaurantLocationStatus').html('Failure contacting server.  Please try again. (function: editLocation)');
}
function checkDeleteLocation() {
    deleteLocation({'id': $('#editRestaurantLocationId').val()});
}
function deleteLocation(postData) {
    incLoadingCount();
    postData['csrfmiddlewaretoken'] = csrfToken;
    $.ajax({
        type: 'POST',
        url: baseURL + 'api/deleterestaurantlocation/',
        data: postData,
        dataType: 'json',
        success: deleteLocationSuccess,
        error: deleteLocationError
    });
}
function deleteLocationSuccess(data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#editRestaurantLocationStatus').addClass('invalidForm');
        $('#editRestaurantLocationStatus').html(data['failure']);
    } else {
        $('#editRestaurantLocationStatus').removeClass('invalidForm');
        $('#editRestaurantLocationStatus').html("Successfully deleted restaurant!");
        // Update our info
        // Delete the location.
        var lId = data['lId'];
        removeLocation(lId);
        // Refresh data in tabs and stuff
        for (var j in userRatingFunctions) {
            userRatingFunctions[j](rId);
        }
       
        updateExistingRestaurants();
        // figure out how to update tag info
        // we have the tag data in restaurantData - need to update
        // globalTagData and the tags tab.
        // Or we could just punt and call gettags again.
        updateTags();
    }
}
function deleteLocationError(data) {
    decLoadingCount();
    genericError('loginStatus', 'Failure contacting server.  Please try again. (function: deleteLocation)');
    $('#editRestaurantLocationStatus').addClass('invalidForm');
    $('#editRestaurantLocationStatus').html('Failure contacting server.  Please try again. (function: deleteLocation)');
}

function checkAddRestaurant() {
    var isValid = true;
    var restaurantName = jQuery.trim($('#restaurantName').val());
    if (restaurantName == '') {
        $('#restaurantNameInvalid').css('display', 'inline');
        isValid = false;
    } else {
        $('#restaurantNameInvalid').css('display', 'none');
    }
    var url = jQuery.trim($('#restaurantUrl').val());
    var style = $('#restaurantStyle').val(); 
    var price = jQuery.trim($('#restaurantEntreePrice').val());
    if (price != '') {
        price = parseInt($('#restaurantEntreePrice').val()); 
        if (isNaN(price)) {
            $('#restaurantEntreePriceInvalid').css('display', 'inline');
            isValid = false;
        } else {
            $('#restaurantEntreePriceInvalid').css('display', 'none');
        }
    }
    var locality = $('#restaurantLocality').val(); 
    if (locality == ' ') {
        locality = '';
    }
    if (isValid) {
        tags = [];
        for (tagName in globalTagData) {
            if ($('#createRestaurantTag' + tagName).hasClass('tagOn')) {
                tags.push(tagName);
            }
        }
        addRestaurant({'name': restaurantName, 'style': style, 'url': url, 'price': price, 'locality': locality, 'tag' : tags});
    }
}

function checkEditRestaurant() {
    var isValid = true;
    var restaurantName = jQuery.trim($('#editRestaurantName').val());
    if (restaurantName == '') {
        $('#editRestaurantNameInvalid').css('display', 'inline');
        isValid = false;
    } else {
        $('#editRestaurantNameInvalid').css('display', 'none');
    }
    var url = jQuery.trim($('#editRestaurantUrl').val());
    var style = $('#editRestaurantStyle').val(); 
    var price = jQuery.trim($('#editRestaurantEntreePrice').val());
    if (price != '') {
        price = parseInt($('#editRestaurantEntreePrice').val()); 
        if (isNaN(price)) {
            $('#editRestaurantEntreePriceInvalid').css('display', 'inline');
            isValid = false;
        } else {
            $('#editRestaurantEntreePriceInvalid').css('display', 'none');
        }
    }
    var locality = $('#editRestaurantLocality').val(); 
    var tags = [];
    for (tagName in globalTagData) {
        if ($('#editRestaurantTag' + tagName).hasClass('tagOn')) {
            tags.push(tagName);
        }
    }
    if (isValid) {
        editRestaurant({'id': $('#editExistingRestaurant').val(), 'name': restaurantName, 'style': style, 'url': url, 'price': price, 'locality': locality, 'tag': tags});
    }
}
function checkDeleteRestaurant() {
    deleteRestaurant({'id': $('#editExistingRestaurant').val()});
}
function deleteRestaurant(postData) {
    incLoadingCount();
    postData['csrfmiddlewaretoken'] = csrfToken;
    $.ajax({
        type: 'POST',
        url: baseURL + 'api/deleterestaurant/',
        data: postData,
        dataType: 'json',
        success: deleteRestaurantSuccess,
        error: deleteRestaurantError
    });
}
function deleteRestaurantSuccess(data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#editRestaurantStatus').addClass('invalidForm');
        $('#editRestaurantStatus').html(data['failure']);
    } else {
        $('#editRestaurantStatus').removeClass('invalidForm');
        $('#editRestaurantStatus').html("Successfully deleted restaurant!");
        // Update our info
        // Delete the restaurant and all locations.
        // First, delete the locations.
        var rId = data['rId'];
        removeAllLocations(rId);
        // Now, delete the restaurant.
        delete restaurantData[rId];
        // Refresh data in tabs and stuff
        for (var j in userRatingFunctions) {
            userRatingFunctions[j](rId);
        }
       
        updateExistingRestaurants();
        // figure out how to update tag info
        // we have the tag data in restaurantData - need to update
        // globalTagData and the tags tab.
        // Or we could just punt and call gettags again.
        updateTags();
    }
}
function removeAllLocations(rId) {
    for (var i in locationData) {
        if (locationData[i][0]['restaurant'] == rId) {
            removeLocation(i);
        }
    }
}
function removeLocation(lId) {
    // Remove the marker first.
    removeMarker(lId);
    delete locationData[lId];
}
function deleteRestaurantError(data) {
    decLoadingCount();
    genericError('loginStatus', 'Failure contacting server.  Please try again. (function: deleteRestaurant)');
    $('#editRestaurantStatus').addClass('invalidForm');
    $('#editRestaurantStatus').html('Failure contacting server.  Please try again. (function: deleteRestaurant)');
}
function addRestaurant(postData) {
    incLoadingCount();
    postData['csrfmiddlewaretoken'] = csrfToken;
    $.ajax({
        type: 'POST',
        url: baseURL + 'api/addrestaurant/',
        data: postData,
        dataType: 'json',
        success: addRestaurantSuccess,
        error: addRestaurantError
    });
}

function addRestaurantSuccess(data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#createRestaurantStatus').addClass('invalidForm');
        $('#createRestaurantStatus').html(data['failure']);
    } else {
        $('#createRestaurantStatus').removeClass('invalidForm');
        $('#createRestaurantStatus').html("Successfully added restaurant!");
        $('#createRestaurantLocationStatus').removeClass('invalidForm');
        $('#createRestaurantLocationStatus').html("Successfully added restaurant!");
        // Update our info
        var restaurantToShow = -1;
        // There should only be one of these...
        for (var i in data) {
            restaurantData[i] = data[i];
            restaurantToShow = i;
        }
        // Refresh data in tabs and stuff
        for (var j in userRatingFunctions) {
            userRatingFunctions[j](rId);
        }
        updateExistingRestaurants();
        // Highlight this restaurant in the create location tab
        if (restaurantToShow != -1) {
            $('#addRestaurantDialogTabs').tabs("select", '#createRestaurantLocationTab');
            // We know what the value is, so use jQuery magic!
            $('#existingRestaurant option[value="' + restaurantToShow + '"]').attr('selected','selected');
        }
        // figure out how to update tag info
        // we have the tag data in restaurantData - need to update
        // globalTagData and the tags tab.
        // Or we could just punt and call gettags again.
        updateTags();
    }
}

function addRestaurantError(data) {
    decLoadingCount();
    genericError('loginStatus', 'Failure contacting server.  Please try again. (function: addRestaurant)');
    $('#createRestaurantStatus').addClass('invalidForm');
    $('#createRestaurantStatus').html('Failure contacting server.  Please try again. (function: addRestaurant)');
}

function editRestaurant(postData) {
    incLoadingCount();
    postData['csrfmiddlewaretoken'] = csrfToken;
    $.ajax({
        type: 'POST',
        url: baseURL + 'api/editrestaurant/',
        data: postData,
        dataType: 'json',
        success: editRestaurantSuccess,
        error: editRestaurantError
    });
}

function editRestaurantSuccess(data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#editRestaurantStatus').addClass('invalidForm');
        $('#editRestaurantStatus').html(data['failure']);
    } else {
        $('#editRestaurantStatus').removeClass('invalidForm');
        $('#editRestaurantStatus').html("Successfully edited restaurant!");
        // Update our info
        var restaurantToShow = -1;
        // There should only be one of these...
        for (var i in data) {
            restaurantData[i] = data[i];
            restaurantToShow = i;
        }
        // Refresh data in tabs and stuff
        for (var j in userRatingFunctions) {
            userRatingFunctions[j](rId);
        }
        updateExistingRestaurants();
        // Re-select this restaurant
        if (restaurantToShow != -1) {
            $('#editExistingRestaurant option[value="' + restaurantToShow + '"]').attr('selected','selected');
            editExistingRestaurantChange();
        }
        // figure out how to update tag info
        // we have the tag data in restaurantData - need to update
        // globalTagData and the tags tab.
        // Or we could just punt and call gettags again.
        updateTags();
    }
}

function editRestaurantError(data) {
    decLoadingCount();
    genericError('loginStatus', 'Failure contacting server.  Please try again. (function: editRestaurant)');
    $('#editRestaurantStatus').addClass('invalidForm');
    $('#editRestaurantStatus').html('Failure contacting server.  Please try again. (function: editRestaurant)');
}

function changeCity() {
    var newCityId = $('#changeCity').val();
    if (newCityId == cityId) {
        // No change
        return;
    }
    reloadRestaurantData(newCityId);
}

function reloadRestaurantData(newCityId) {
    document.location = baseURL + 'cities/' + newCityId + '/';
    // This is what we would do if we stayed on the same page.
    /*incLoadingCount();
    $.ajax({
        type: 'GET',
        url: baseURL + 'api/getallrestaurantdata/',
        data: {'cityId': newCityId, 'randomUnused': Math.random()},
        dataType: 'json',
        success: reloadRestaurantSuccess,
        error: reloadRestaurantError
    });*/
}

function reloadRestaurantSuccess(data) {
    decLoadingCount();
    if ('failure' in data) {
        $('#loginStatus').text(data['failure']);
    } else {
        $('#loginStatus').text('');
        // Clear existing restaurant and location data
        locationData = data['data'][0];
        restaurantData = data['data'][1];
        cityId = data['cityId'];
        whichMarkerOpen = -1;
        whichRestaurantDataShown = -1;
        markers = {};
        // Recenter map
        map.setCenter(new GLatLng(cityData[cityId]['latitude'], cityData[cityId]['longitude']), cityData[cityId]['zoomlevel']);
        // Set city indicators correctly.
        var displayedCity = cityData[cityId]['name'] + ', ' + cityData[cityId]['state'];
        $('#currentCityName').text(displayedCity);
        document.title = "Where's lunch? - Lunch restaurants for " + displayedCity;
        location.hash = "city=" + cityId;
        $('#changeCity option[value="' + cityId + '"]').attr('selected', 'selected');
        map.clearOverlays();
        for (i in locationData) {
            addMarker(parseInt(i));
        }
        calculateNextPrevLocations();
        for (i in changeCityFunctions) {
            changeCityFunctions[i]();
        }
    }
}
function reloadRestaurantError(data) {
    decLoadingCount();
    genericError('loginStatus', 'Failure contacting server.  Please try again. (function: reloadRestaurant)');
}

function addFilteringTags() {
    $('#addFilteringTagsHere').html('');
    var toAdd = '<b>Tags:</b><br><form style="display:inline;"><label for="filterWithTags">Filter by tags below</label><input type="checkbox" name="filterWithTags" id="filterWithTags" onclick="tagFilterSet();"><br>';
    toAddArray = [];
    for (tagName in globalTagData) {
        toAddArray.push('<a href="javascript:void(0);" onclick="tagFilterClicked(\'' + tagName + '\');" id="filterTag' + tagName + '" class="tagOff">' + tagName + '</a> ');
    }
    toAdd += toAddArray.join('');
    toAdd += '</form>';
    $('#addFilteringTagsHere').html(toAdd);
}

function addDialogTags() {
    addTagsGeneric('createRestaurantTag', 'createRestaurantTags');
    addTagsGeneric('editRestaurantTag', 'editRestaurantTags');
}
function addTagsGeneric(prefix, id) {
    $('#' + id).html('');
    var toAdd = '<form style="display:inline;">';
    var toAddArray = [];
    for (tagName in globalTagData) {
        toAddArray.push('<a href="javascript:void(0);" onclick="' + prefix + 'Clicked(\'' + tagName + '\');" id="' + prefix + tagName + '" class="tagOff">' + tagName + '</a> ');
    }
    toAdd += toAddArray.join('');
    toAdd += '</form>';
    $('#' + id).html(toAdd);
}
function tagFilterSet() {
    if ($('#filterWithTags').attr('checked') == '') {
        // Turn off all the tags since we're not using them.
        for (tagName in globalTagData) {
            var elem = $('#filterTag' + tagName);
            if (elem.hasClass('tagOn')) {
                elem.removeClass('tagOn');
            }
            if (!elem.hasClass('tagOff')) {
                elem.addClass('tagOff');
            }
        }
    }
    applyTagFilter();
}

function createRestaurantTagClicked(tagName) {
    var elem = $('#createRestaurantTag' + tagName);
    if (elem.hasClass('tagOn')) {
        elem.removeClass('tagOn').addClass('tagOff');
    } else {
        elem.removeClass('tagOff').addClass('tagOn');
    }
}
function editRestaurantTagClicked(tagName) {
    var elem = $('#editRestaurantTag' + tagName);
    if (elem.hasClass('tagOn')) {
        elem.removeClass('tagOn').addClass('tagOff');
    } else {
        elem.removeClass('tagOff').addClass('tagOn');
    }
}
function tagFilterClicked(tagName) {
    var elem = $('#filterTag' + tagName);
    if (elem.hasClass('tagOn')) {
        elem.removeClass('tagOn').addClass('tagOff');
    } else {
        elem.removeClass('tagOff').addClass('tagOn');
    }
    $('#filterWithTags').attr('checked', 'checked');
    applyTagFilter();
}

function applyTagFilter() {
    tagFilteringCriteria = {};
    var filterWithTags = $('#filterWithTags').attr('checked');
    if (!filterWithTags || filterWithTags == '') {
        // all restaurants pass!
        for (i in locationData) {
            tagFilteringCriteria[i] = 1;
        }
    } else {
        // First see which restaurants pass
        restaurantsAllowed = {};
        for (tagName in globalTagData) {
            if ($('#filterTag' + tagName).hasClass('tagOn')) {
                // Any restaurant that has this tag is allowed.
                for (i in globalTagData[tagName]) {
                    rId = globalTagData[tagName][i];
                    if (!(rId in restaurantsAllowed)) {
                        restaurantsAllowed[rId] = 1;
                    }
                }
            }
        }
        // Now see which locations pass.
        for (lId in locationData) {
            if (locationData[lId][0]['restaurant'] in restaurantsAllowed) {
                tagFilteringCriteria[lId] = 1;
            }
        }
    }
    recalculateFiltering(false);
}

$(document).ready(function() {
    locationMatch = /location=(\d+)/.exec(location.hash);
    if (locationMatch) {
        var locationId = parseInt(locationMatch[1]);
        showMarkerWindow(markers[locationId], locationId);
    }
    tagFilteringCriteria = {};
    for (i in locationData) {
        tagFilteringCriteria[i] = 1;
    }
    registerLoginLogoutFunction(setColoringTypeOnLogin);
    registerLoginLogoutFunction(updateTopRestaurants.curry(true));
    //registerLoginLogoutFunction(curry(updateTopRestaurants, window, true));
    registerLoginLogoutFunction(updateSuggestions.curry(true));
    //registerLoginLogoutFunction(curry(updateSuggestions, window, true));
    registerLoginLogoutFunction(recalculateColoring);
    registerLoginLogoutFunction(showHideUserRatingControls);
    registerLoginLogoutFunction(recalculateFiltering.curry(true));
    registerLoginLogoutFunction(updateTags);
    registerUserRatingFunction(recalculateColoringAfterUserRating);
    registerUserRatingFunction(updateTopRestaurants.curry(false));
    //registerUserRatingFunction(curry(updateTopRestaurants, window, false));
    registerUserRatingFunction(updateSuggestions.curry(false));
    //registerUserRatingFunction(curry(updateSuggestions, window, false));
    registerUserRatingFunction(recalculateFilteringAfterUserRating);
    registerChangeCityFunction(updateTopRestaurants.curry(false));
    //registerChangeCityFunction(curry(updateTopRestaurants, window, false));
    registerChangeCityFunction(updateSuggestions.curry(false));
    //registerChangeCityFunction(curry(updateSuggestions, window, false));
    registerChangeCityFunction(recalculateColoring);
    registerChangeCityFunction(recalculateFiltering.curry(true));
    registerChangeCityFunction(updateNumTotalMarkers);
    registerChangeCityFunction(updateTags);
    registerChangeCityFunction(applyTagFilter);
    updateTags();
});
