// the object containing all the properties read by the broker
var sidePanelProperties = new Object();

// escapes common html characters
function escapeString(str) {
    return str.split("&").join("&amp;").split("<").join("&lt;").split(">").join("&gt;").split("\"").join("&quot;");
}

// gets all descendant elements of node having classname set to 'classname' or classname2'
function getElementsByClassName(classname, classname2, node) 
{
    if (!node) { node = document.getElementsByTagName('body')[0]; }
    var a = [];
    var re = new RegExp('\\b' + classname + '\\b');
    if (classname2 != null)
        re = new RegExp('\\b' + classname + '\\b|' + '\\b' + classname2 + '\\b');
    els = node.getElementsByTagName('*');
    for (var i = 0, j = els.length; i < j; i++) {
        if (re.test(els[i].className) || (classname2!=null && re.test(els[i].className2))) {
            a.push(els[i]);
        }
    }
    return a;
}

// event handler for going to a relative page number.
// the relative page number is the ith page in the current set of pages (1-based counting)
function gotoPage(relativePageNumber) {

    // calculate the absolute page number selected
    var desiredPage = relativePageNumber + sidePanelProperties.currentSet * sidePanelProperties.pagesPerSet;

    // if not currently on the desired page, then set the internal property and call the broker
    if (sidePanelProperties.currentPage != desiredPage) {
        sidePanelProperties.currentPage = desiredPage;
        ResetPagingVisuals();
        CallJSbroker();
    }
}

// event handler for going to the first page
function gotoFirstPage() 
{
    if (sidePanelProperties.currentPage != 1) 
    {
        sidePanelProperties.currentPage = 1;
        ResetPagingVisuals();
        CallJSbroker();
    }
}

// event handler for going to the last page
function gotoLastPage() 
{
    if(sidePanelProperties.currentPage != sidePanelProperties.numPages)
    {
        sidePanelProperties.currentPage = sidePanelProperties.numPages;
        ResetPagingVisuals();
        CallJSbroker();
    }
}

// event handler for going to the previous page
function gotoPrevPage() 
{
    if (sidePanelProperties.currentPage != 1)
    {
        sidePanelProperties.currentPage = sidePanelProperties.currentPage - 1;
        ResetPagingVisuals();
        CallJSbroker();
    }
}

// event handler for going to the next page
function gotoNextPage() 
{
    if (sidePanelProperties.currentPage != sidePanelProperties.numPages) 
    {
        sidePanelProperties.currentPage = sidePanelProperties.currentPage + 1;
        ResetPagingVisuals();
        CallJSbroker();
    }
}

// event handler for resorting which includes changes in either/both the sorttype and sortorder
// handles the visual changes and calling the broker to get new results
function resort(sortType) {

    // ignore sorting of the disabled distance button
    if (sortType == "dist" && (sidePanelProperties.searchText == null || sidePanelProperties.searchText.length == 0))
        return;

    // if clicking the existing sorttype button, just flip the isAsc property (sortorder)
    if (sidePanelProperties.sortType == sortType) {
        sidePanelProperties.isAsc = !sidePanelProperties.isAsc;

        // store the changed ordering for the current sort type
        switch (sortType) {
            case "alph": sidePanelProperties.isAscAlpha = sidePanelProperties.isAsc; break;
            case "pop": sidePanelProperties.isAscPopular = sidePanelProperties.isAsc; break;
            case "dist": sidePanelProperties.isAscDistance = sidePanelProperties.isAsc; break;
        }
    }
    // a new sort-type has been selected
    else {
        sidePanelProperties.sortType = sortType;

        // set the current asc/desc option based on the existing ordering of the new sorttype button
        switch (sortType) {
            case "alph": sidePanelProperties.isAsc = sidePanelProperties.isAscAlpha; break;
            case "pop": sidePanelProperties.isAsc = sidePanelProperties.isAscPopular; break;
            case "dist": sidePanelProperties.isAsc = sidePanelProperties.isAscDistance; break;
        }
    }

    // reset visuals
    ResetSortingVisuals();

    // call the broker to get new results
    CallJSbroker();
}

// event handler for when an activist is selected
// toggles the visual's of the sidebar and calls the broker to notify of the change if the callBroker parameter is true
// the input rowIndex is a zero-based index
function activistSelected(rowIndex, callBroker) {

    if (rowIndex == -1) {

        // make sure we always mark selectedActivistId as -1 if we get rowIndex = -1
        sidePanelProperties.selectedActivistId = -1;
    }

    // ignore selections of the current selected row
    if (rowIndex != sidePanelProperties.lastSelectedRowIndex) {

        // get all repeat panels 
        var repeatPanels = getElementsByClassName("repeatPanel", "repeatPanelSelected", document.getElementById("contentPanel"));

        // if a row is currently selected, unselect it
        if (sidePanelProperties.lastSelectedRowIndex >= 0)
            repeatPanels[sidePanelProperties.lastSelectedRowIndex].className = "repeatPanel";

        // store the last selected index including the clearing value of -1
        sidePanelProperties.lastSelectedRowIndex = rowIndex;

        // reset the selected activistId -- will be properly set below if rowIndex is a valid index
        sidePanelProperties.selectedActivistId = -1;

        // only set selected if the rowIndex is in the valid range (i.e. ignore the -1 reset)
        if (rowIndex >= 0 && rowIndex < sidePanelProperties.resultsPerPage) {
            // set the select style on the selected row
            repeatPanels[rowIndex].className = "repeatPanelSelected";

            // get the Id of the activist at this row and store for usage by the broker
            sidePanelProperties.selectedActivistId = sidePanelProperties.activists[rowIndex].id;

            // TODO: call the broker to center on the selected activist
            if (callBroker) {
                // alert("Call Broker to select activist with Id: " + sidePanelProperties.selectedActivistId.toString());
                activeActivistSelected(sidePanelProperties.selectedActivistId.toString());
            }
        }
    }
}

// function which re-selects currently selected activist after sidepanel changes
function updateActivistIfSelected() {

    if (sidePanelProperties.selectedActivistId != -1) {

        // determine the number of activists shown on the current page
        var resultsCount = sidePanelProperties.activists.length;

        // flag saying if row to select was found
        var found = false;

        // check the collection of shown activists for the desired id
        for (var i = 0; i < resultsCount; i++) {

            // if we've found the activist select that row but don't call the broker
            if (sidePanelProperties.activists[i].id == sidePanelProperties.selectedActivistId) {
                found = true;
                activistSelected(i, false);
                break;
            }
        }
    }
}

// event handler for when an activist is selected via the map 
// toggles the visual's of the row containing the activist if found (may not be in the search results or on another page)
function activistSelectedViaMap(activistId) {

    // determine the number of activists shown on the current page
    var resultsCount = sidePanelProperties.activists.length;

    // flag saying if row to select was found
    var found = false;

    // check the collection of shown activists for the desired id
    for (var i = 0; i < resultsCount; i++) {

        // if we've found the activist select that row but don't call the broker
        if (sidePanelProperties.activists[i].id == activistId) {
            found = true;
            activistSelected(i, false);
            break;
        }
    }

    // if proper row was not found cancel any selection
    // so just reset any active sidebar selection
    if (!found)
        activistSelected(-1, false);
}

// updates the entire sidebar based on the input parameters
// activists: JSON collection of activists returned for the specified page (currentPage) of the user-initiated search
// searchText: the search text input by the user in the silverlight control
// latitude: the latitude of the location returned by bing for the search text
// longitude: the latitude of the location returned by bing for the search text
// sortType: one of 'alph' | 'pop' | 'dist' representing the sort type of the results
// isAsc: a boolean representation of the sort order
// currentPage: the current page for which the activists results are for
// numTotalResults: the total number of activists for the search performed
function updateSidePanel(activists, searchText, latitude, longitude, sortType, isAsc, currentPage, numTotalResults ) {

    // store necessary parameters for inter-call usuage
    sidePanelProperties.searchText = searchText;
    sidePanelProperties.sortType = sortType;
    sidePanelProperties.isAsc = (isAsc=="false" ? false : (isAsc=="true" ? true : isAsc));
    sidePanelProperties.currentPage = parseInt(currentPage);
    sidePanelProperties.numPages = Math.ceil(parseInt(numTotalResults) / sidePanelProperties.resultsPerPage);
    if (sidePanelProperties.numPages <= 0) // always show at least 1 page, even if zero results
        sidePanelProperties.numPages = 1;
    sidePanelProperties.latitude = latitude;
    sidePanelProperties.longitude = longitude;
    sidePanelProperties.numTotalResults = parseInt(numTotalResults);
    sidePanelProperties.lastSelectedRowIndex = -1;
    sidePanelProperties.activists = activists;

    // get the controls for the search text and preamble text 
    var searchTextElement = document.getElementById("searchText");
    var searchPreambleElement = document.getElementById("searchPreamble");

    // get the repeating rows encapsulating each activist result
    var repeatPanels = getElementsByClassName("repeatPanel", "repeatPanelSelected", document.getElementById("contentPanel"));

    // get all the title / distance / location / links elements for each activist result
    var titles = getElementsByClassName("title", null, document.getElementById("contentPanel"));
    var distances = getElementsByClassName("distance", null, document.getElementById("contentPanel"));
    var locations = getElementsByClassName("location", null, document.getElementById("contentPanel"));
    var links = getElementsByClassName("visitButton", null, document.getElementById("contentPanel"));

    // set visuals of the sorting controls based on sidePanelProperties settings
    ResetSortingVisuals();

    // set the search preamble text
    if (searchText != null && searchText.length > 0) {
        searchPreambleElement.innerHTML = "<b>" + numTotalResults.toString() + "</b> search results for:";
    }
    else {
        searchPreambleElement.innerHTML = "All <b>" + numTotalResults.toString() + "</b> results.";
    }

    // set the search text
    searchTextElement.innerHTML = escapeString(searchText);

    // get the number of results returned for the current page
    var resultsCount = (activists == null ? 0 : activists.length);
        
    // update the first x rows needed
    for (var i = 0; i < resultsCount; i++) {
        // try to round the distance if available and there was searchText
        var dist = null;
        if (searchText != null && searchText.length > 0 && activists[i].distance != null && activists[i].distance.length > 0 && !isNaN(parseFloat(activists[i].distance))) {
            // if the distance is  < 100 perserve 2 decimal places, otherwise show none
            var num = parseFloat(activists[i].distance);
            if (num < 100)
                dist = Math.round(num * 100) / 100;
            else
                dist = Math.round(num);
        }

        titles[i].innerHTML = escapeString(activists[i].title);
        distances[i].innerHTML = (dist != null ? escapeString(dist.toString()) + " mi" : "");
        locations[i].innerHTML = escapeString(activists[i].city) + " / " + escapeString(activists[i].state);
        links[i].href = "http://www.facebook.com/group.php?gid="+ activists[i].fbGroupId.toString();
        repeatPanels[i].style.visibility = "visible"; // may be previously hidden
        repeatPanels[i].className = "repeatPanel";  // clear any selected style
    }
            
    // hide those rows not needed
    for (var i = resultsCount; i < sidePanelProperties.resultsPerPage; i++) {
        repeatPanels[i].style.visibility = "hidden";
    }

    // determine which page-set we're in
    sidePanelProperties.currentSet = Math.floor((currentPage-1) / sidePanelProperties.pagesPerSet);

    // set visuals of the paging controls based on sidePanelProperties settings
    ResetPagingVisuals();

    // make sure that after sidepanel has changed, we highlight currently selected pushpin on the map
    updateActivistIfSelected();

    // set the loading panel visibility to false
    SetBlockingPanelVisibility(false);

    GetActivistsDataFromFb(activists);
}

function GetActivistsDataFromFb(activists) {
    if (activists == null)
        return;
    
    var groupStr = "";
    for (var i = 0; i < activists.length-1; i++) {
        groupStr += activists[i].fbGroupId + ",";
    }
    groupStr += activists[activists.length - 1].fbGroupId;

    var query = "SELECT gid, name FROM group WHERE gid in(" + groupStr + ")";
    
    FB.api(
        {
            method: 'fql.query',
            format: 'json',
            query: query
        },
        function (data) {
            if (!data || data.error) {
                SetBlockingPanelVisibility(false);
                throw data;
            }
            else {
                updateSidePanelFromFb(data);
            }
        }
    );
}

function updateSidePanelFromFb(names) {

    var titles = getElementsByClassName("title", null, document.getElementById("contentPanel"));
    
    var resultsCount = sidePanelProperties.activists.length;
    
    // check the collection of shown activists for the desired id
    for (var i = 0; i < resultsCount; i++) {
        for (var j = 0; j < names.length; j++) {
            if (sidePanelProperties.activists[i].fbGroupId == names[j].gid) {
                sidePanelProperties.activists[i].title = names[j].name;
                titles[i].innerHTML = escapeString(names[j].name);
                break;
            } 
        }
    }
    
    SetBlockingPanelVisibility(false);
}

function ResetSortingVisuals() {
    // get the sorttype buttons
    var sortPopular = document.getElementById("sortPopular");
    var sortDistance = document.getElementById("sortDistance");

    // get the sort order images for each sorttype
    var popularImage = document.getElementById("popularImage");
    var distanceImage = document.getElementById("distanceImage");

	// initially set all sorttype buttons to the unselected mode
    sortPopular.className = "";
    sortDistance.className = "";
	
    // select the sorttype and sortorder button/image to work with depending on the input
    // also set the asc/desc based on the input
    var selectedButton = null;
    var selectedImage = null;
    switch (sidePanelProperties.sortType) {
        case "pop":
            selectedButton = sortPopular;
            selectedImage = popularImage;
            sidePanelProperties.isAscPopular = sidePanelProperties.isAsc;
            break;
        case "dist":
            selectedButton = sortDistance;
            selectedImage = distanceImage;
            sidePanelProperties.isAscDistance = sidePanelProperties.isAsc;
            break;
    }

    // set the style of the selected button
    if (selectedButton != null)
        selectedButton.className = "selected";

    // set the source of the selected image
    if (selectedImage != null) {

        // set source depending on the asc/desc input
        selectedImage.src = (sidePanelProperties.isAsc ? "graphics/arrowUpWhite.png" : "graphics/arrowDownWhite.png");

        // flip the color of the last selected image (if different than the current) but preserve direction of arrow
        if (sidePanelProperties.lastSelectedImage.id != selectedImage.id) {
            sidePanelProperties.lastSelectedImage.src = (sidePanelProperties.lastSelectedImage.src.toString().indexOf("up") != -1 ? "graphics/arrowUpBlue.png" : "graphics/arrowDownBlue.png");
        }

        // set the last selected image to the current selected
        sidePanelProperties.lastSelectedImage = selectedImage;
    }

    // disable the distance button if no search text
    if (sidePanelProperties.searchText == null || sidePanelProperties.searchText.length == 0)
        sortDistance.className = "disabled";

}

// set visuals of the paging controls based on sidePanelProperties settings
function ResetPagingVisuals() {

    // get the paging navigation controls
    var pageGotoFirst = document.getElementById("pageGotoFirst");
    var pageGotoPrev = document.getElementById("pageGotoPrev");
    var pageGotoNext = document.getElementById("pageGotoNext");
    var pageGotoLast = document.getElementById("pageGotoLast");

    // get the individual page links
    var pages = getElementsByClassName("selectedPage", "unSelectedPage", document.getElementById("pagingPanel"));

    // reset the current page selection 
    for (var i = 1; i <= sidePanelProperties.pagesPerSet; i++) {

        // calculate the ith page number
        var pageNumber = (sidePanelProperties.currentSet * sidePanelProperties.pagesPerSet + i);

        // set the selected/unselected page style as needed
        pages[i - 1].className = (pageNumber == sidePanelProperties.currentPage ? "selectedPage" : "unSelectedPage");

        // output the page number or empty if we've exceeded the total number of pages
        pages[i - 1].innerHTML = (pageNumber <= sidePanelProperties.numPages ? pageNumber.toString() : "");

        // hide or show appropriately
        pages[i - 1].style.visibility = (pageNumber <= sidePanelProperties.numPages ? "visible" : "hidden");

    }

    // set the paging navigation button styles depending on the currentPage and number of total pages
    pageGotoFirst.className = (sidePanelProperties.currentPage != 1 ? "pageButton" : "pageButtonDisabled");
    pageGotoPrev.className = (sidePanelProperties.currentPage != 1 ? "pageButton" : "pageButtonDisabled");
    pageGotoNext.className = (sidePanelProperties.currentPage != sidePanelProperties.numPages ? "pageButton" : "pageButtonDisabled");
    pageGotoLast.className = (sidePanelProperties.currentPage != sidePanelProperties.numPages ? "pageButton" : "pageButtonDisabled");
}

// Sets default properties for the sidebar
// this should be called upon window/page load
function initializeSidePanel() {
    // the default selected sorttype is the alphabetical one but only set on first load not on re-init (clearsearch)
    if(sidePanelProperties.lastSelectedImage == null)
        sidePanelProperties.lastSelectedImage = document.getElementById("popularImage");

    // the page can contain a maximum of 10 results per page 
    sidePanelProperties.resultsPerPage = 6;

    // the paging controls show links to 5 pages at a time
    sidePanelProperties.pagesPerSet = 5;

    // initially we're showing the first (0-based index) set of pages
    sidePanelProperties.currentSet = 0;

    // the search initial text is empty
    sidePanelProperties.searchText = "";

    // either alph, pop, or dist -- default is pop
    sidePanelProperties.sortType = "pop"; 

    // indicates the sort direction asc/desc of the currently selected sorttype
    sidePanelProperties.isAsc = false;

    // these properties keep the asc/desc state of each sorttype
    // when an unselected sorttype becomes selected, the last state of the sort order is preserved
    sidePanelProperties.isAscAlpha = true; 
    sidePanelProperties.isAscPopular = true;
    sidePanelProperties.isAscDistance = true;

    // the 1-based-index representing the current page
    sidePanelProperties.currentPage = 1;

    // stores the number of (total) results the search returned
    sidePanelProperties.numTotalResults = 0;

    // stores the number of (total) pages returned in the search -- default and minimum is 1
    sidePanelProperties.numPages = 1;

    // stores the collection of JSON activists for the currently displayed page
    sidePanelProperties.activists = null;

    // stores the latitude and longitude of the search location
    sidePanelProperties.latitude = 0.0;
    sidePanelProperties.longitude = 0.0;
    sidePanelProperties.radius = 0.0;

    // stores the Id of the selected activist
    sidePanelProperties.selectedActivistId = -1;

    // stores the 0-based row-index of the selected activist
    sidePanelProperties.lastSelectedRowIndex = -1;
}

// Call the broker which will retrieve all required data from the sidePanelProperties object
// before the call is made, the blocking loading panel/image is shown
function CallJSbroker() {

    // set the loading panel visibility
    SetBlockingPanelVisibility(true);

    searchActivistsFromJavascript();
}

// sets the visibility of the blockPanel based on the parameter
function SetBlockingPanelVisibility(isVisible) {

    // get the blocking panel
    var blockingPanel = document.getElementById("blockingPanel");

    // get the panel containing the listing of results
    var contentPanel = document.getElementById("contentPanel");

    // set the visibility
    contentPanel.style.visibility = (isVisible == false ? "visible" : "hidden");

    // set the visibility
    blockingPanel.style.visibility = (isVisible ? "visible" : "hidden");

}

// init call to the loaded function for setup
//window.onload = loaded;
