﻿
//
//This file supports VE Address queries and Parcel layer queries. It also provides general support
//for the auto-complete techniques used in the Default.aspx page. It contains at least one method
//that is referenced by script emitted from RichmondMapDotNetVEBase.cs. Verify that elements referenced in 
//the GetMatches() method exist in the implementing page when using this file.
//
//History:
//2008.09.30  Released version 1.0.
//2008.10.16  Updated PopulateSelectFromWM() to include feedback on query results
//



//a timer and interval used by the auto-complete controls to avoid calling the lookup service when typing 
var keystrokeTimer = null;
var keystrokeInterval = 500; 

//string to append to all VEMap.Find calls
var findLocationAppendString=', Richmond, Virginia, USA';

//VE references; note that vePlaceResults includes objects with latlongs and other info
var veSelect;
var filteredVEPlaces;

//Call the appropriate query method for the given textbox if its value meets the required length.
//These queries all rely on PageMethod calls to the data access layer.
function GetMatches(textboxID, selectID)
{
    var select = document.getElementById(selectID);
    var textbox = document.getElementById(textboxID);
    if (select && textbox)
    {
        //require three characters for lookup
        if (textbox.value.length < 3)
        {
            select.style.display='none';
        }
        else
        {
            //determine which text box is receiving input and call the appropriate method
            if (textboxID == 'uxLocationTextVE')
            {
                //use VE
                try
                {
                    veSelect = select;
                    var location = textbox.value + findLocationAppendString;
                    MDNGetVEMap().Find(null, location, null, null, null, 20, null, null, false, false, GetVEMatchesCallback);
                }
                catch(e)
                {
                    alert(e.message);
                }
            }
            //must be a layer query; use the script method; 
            //signature: GetAutoCompleteMatches(mapID, layerID, fields, fieldDelimiter, resultFormat, sortField, queryCriteria, maxRows, uxSelectID, doDrillDown)
            else if (textboxID == 'uxOwnerText')
            {
                GetAutoCompleteMatches("Parcel", "Parcels", "OwnerName,PIN", ",", "{0} ({1})", "OwnerName", "OwnerName like '" + textbox.value + "%'", 200, selectID, true);
            }
            else if (textboxID == 'uxPinText')
            { 
                GetAutoCompleteMatches("Parcel", "Parcels", "PIN", null, null, "PIN", "PIN like '" + textbox.value + "%'", 200, selectID, true);
            }
        }
    }
    keystrokeTimer = null;
}

//Handle results callback from VE.
function GetVEMatchesCallback(veShapeLayer, veFindResults, vePlaces, moreResults, errorString)
{
    if (vePlaces != null && vePlaces.length > 0)
    {
        try
        {
            //remove VEPlace elements that do not contain 'Richmond, VA'
            filteredVEPlaces = vePlaces.slice();
            filteredVEPlaceNames = new Array();
            var i=0;
            for (var k=0; k < vePlaces.length && k < 200; k++)
            {
                //add place name for select element and increment
                name=filteredVEPlaces[i].Name;
                //if (name.indexOf("Richmond, VA") < 0) //|| name.indexOf("Richmond, Virginia") < 0)
                if (filteredVEPlaces[i].MatchConfidence > 0) 
                {
                    //not a good match
                    filteredVEPlaces.splice(i,1);
                }
                else
                {
                    //add place name for select element and increment
                    filteredVEPlaceNames[i]=name;
                    i+=1;
                }
            }

            if (filteredVEPlaces.length > 0)
            {
                //add filtered place names to select element; 
                //note that the first result is assumed to be the best match for VE places
                PopulateSelect(veSelect, filteredVEPlaceNames, 20, 1);
                HandleVESelect(veSelect);
            }
            else
            {
                //no valid matches; hide select element
                veSelect.style.display='none';
            }               
        }
        catch(e)
        {
            alert(e.message);
        }
    }
    else
    {
        //no matches; hide select element
        veSelect.style.display='none';
    }
}

//Sets a timer to process matches when there is a pause in keystrokes.
function HandleKeystroke(textbox, selectID)
{
    //if there's a timer, clear it
    if (keystrokeTimer != null)
    {
        window.clearTimeout(keystrokeTimer);
    }
    
    keystrokeTimer = window.setTimeout("GetMatches('" + textbox.id + "','" + selectID + "');", keystrokeInterval);
}

function HandleVESelect(select)
{
    name = select.value;
    for (var i=0; i < filteredVEPlaces.length; i++)
    {
        if (filteredVEPlaces[i].Name == name)
        {
            var vePlace = filteredVEPlaces[i];        
            var latLong = vePlace.LatLong;

            //zoom in; TODO: use the envelope instead...
            MDNGetVEMap().SetCenterAndZoom(latLong,19);
            
            //add pushpin for this VEPlace;
            // -OR- 
            //see commented-out alternative below this block for executing page method drilldown 
            //instead of building pushpin in script
            window.MDNGetVEMap().HideInfoBox();
            
            //reset the infobox style since the result here is always short and does not require scroll bars
            MDNGetVEMap().SetDefaultInfoBoxStyles();
            
            if (window.MyHighlightLayer) window.MDNGetVEMap().DeleteShapeLayer(window.MyHighlightLayer);
            window.MyHighlightLayer = new VEShapeLayer();
            window.MDNGetVEMap().AddShapeLayer(window.MyHighlightLayer);
            var pushpin = new VEShape(VEShapeType.Pushpin, latLong);
            pushpin.SetDescription("<a href='javascript:HandleVEMouseClick(" + latLong.Longitude + ", " + latLong.Latitude + ", 19)'>Drill down</a> if over a parcel.");
            pushpin.SetTitle(vePlace.Name);
            window.MyHighlightLayer.AddShape(pushpin);
            MDNGetVEMap().ShowInfoBox(pushpin);

            ////alternatively, just execute drilldown;
            ////this has a drawback though: the address may or may not be over a parcel; 
            ////if not over an address then no pushpin will be generated
            //drillDownCompleted = false;
            //DoDrillDown();
                
            //change select value back to "[ select one ]" so that onchange event can trigger a re-query
            select.selectedIndex = 0;
                
            //exit loop
            break;
        }
    }
}

function PopulateSelect(select, arr, maxRows, selectedIndex)
{
    select.options.length = 0;   
    
    var option = document.createElement('OPTION');
    option.text = option.value = '[ select one ]';
    select.options[0] = option;
    
    //zero value for maxRows can be used to specify no limit on the query, but 
    //this would not be a valid value for the upper limit on the select element
    if (maxRows == 0)
    {
        maxRows = 9999;
    }
    
    for (var n = 0; n < arr.length && n < maxRows; n++)
    {
        option = document.createElement('OPTION');
        option.text = option.value = arr[n];
        select.options[select.options.length] = option;
    }
    select.selectedIndex = selectedIndex;
    select.style.display='inline';
}

//Called by RichmondMapDotNetVEBase.WMGetAutoCompleteMatches(). This method assumes that a span  
//name selectID + 'StatusSpan' is available on the page for displaying feedback when 
//there are no results or for when the number of results exceeds the allowable limit.
function PopulateSelectFromWM(selectID, arr, maxRows)
{
    if (arr != null)
    {   
        var select = document.getElementById(selectID);

        //clear any existing feedback
        var queryStatusSpan = queryStatusSpan = document.getElementById(selectID + 'StatusSpan');
        if (queryStatusSpan)
        {
            queryStatusSpan.innerHTML = "";
        }
        
        if (select)
        {
            if (arr.length > 0)
            {
                //add matches to select element
                PopulateSelect(select, arr, maxRows, 0);
                
                //update feedback if length >= maxRows; these should be equal when max rows are
                //exceeded since the page method won't return more than this value anyway
                if (arr.length >= maxRows)
                {
                    if (queryStatusSpan)
                    {
                        queryStatusSpan.innerHTML = "max results exceeded";
                        queryStatusSpan.className = 'queryStatusWarning';
                    }
                }
            }
            else
            {
                //no matches; hide select element
                select.style.display='none';
                
                //update feedback 
                if (queryStatusSpan)
                {
                    queryStatusSpan.innerHTML = "no matches";
                    queryStatusSpan.className = 'queryStatusError';                    
                }
            }
        }
    }
}
