/**
 * Class to carry out geocoding functions based on:
 * - city
 * - street name
 * - zip code
 *
 * @author: Akshay Bhurtun
 * $Id: geocodeutils.js,v 1.15 2009/07/20 07:24:19 abhurtun Exp $
 */



var addressElemNames = new Array("city", "cityDistrict", "street", "houseNumber", "zipcode", "state", "longitude", "latitude");
var cName = "[GeoCoder] ";
var geocodingTimer = null;

function GeoCoder(outputElemId_, form_, inputElement_)
{
   var outputElemId = outputElemId_;
   var form = form_;
   var inputElement = inputElement_;
   var addressValue;
   var geoDataSource = "Australia";
   var locateProperties = {maxResults:10};
   
   var self = this;
   this.geocode = geocode;
   
   function geocode() 
   {
      var fnName = cName + "geocode() ";
      var address = new com.ptvag.mnp.common.Address();
      
      var elements = form.getElementsByTagName("input");
      var selectElements = form.getElementsByTagName("SELECT");
      
      //address.zipcode = XMLUtils.getFormElementByName(elements, form.id + ".zipcode").value;//elements[form.id + ".zipcode"].value;
      address.city = XMLUtils.getFormElementByName(elements, form.id + ".cityDistrict").value;//elements[form.id + ".cityDistrict"].value;
      var streetAddress = XMLUtils.getFormElementByName(elements, form.id + ".street").value;//elements[form.id + ".street"].value;
      var elems = streetAddress.split(" ");
      address.street = "";
      address.houseNumberSpecified=false;
      for(var i=0; i<elems.length; i++)
      {
         if(i == 0 && !isNaN(elems[i][0]))
         {
            address.houseNumber = elems[i];
            address.houseNumberSpecified=true;
         }
         else
            address.street += elems[i] + " "; 
      }
      
      if(address.houseNumber == null)
         address.houseNumber = "1";
      
      address.street = address.street.replace(/^\s+|\s+$/g, '');
      
      address.country = XMLUtils.getFormElementByName(elements, form.id + ".country").value;//elements[form.id + ".country"].value;
      var state = XMLUtils.getFormElementByName(selectElements, form.id + ".state").value;
      if(state != null && state.length > 0)
         address.country += "-" + encodeState(state);
      //address.cityDistrict = XMLUtils.getFormElementByName(elements, form.id + ".cityDistrict").value;
      
      mLocation = new com.ptvag.mnp.location.Location();
      mLocation.locate(address, locateProperties, geoDataSource, 
                       function(geocodeAddressList,exc){Geocode_cb(geocodeAddressList,exc, address);});
   };
   
   this.reverseGeoCode=function reverseGeoCode(realCoordinate, callbackFn)
   {
      var fnName = cName + "reverseGeoCode() ";
      var mLocation = new com.ptvag.mnp.location.Location();
      //var smartCoord = ptvWrapper.getSmartCoordinate(realCoordinate);
      var coord = new com.ptvag.mnp.common.Coordinate();
      coord.CoordType = "GEODECIMAL";
      coord.x = Math.round(realCoordinate.longitude*conf_smartUnitToGeoFactor);
      coord.y = Math.round(realCoordinate.latitude*conf_smartUnitToGeoFactor);
      
      log.debug(fnName, "updated x [" + coord.x + "] [" + coord.y + "]");
      
      var reverseLocationProperties = new com.ptvag.mnp.location.ReverseLocateProperties();
      reverseLocationProperties.tolerance = 100;
      reverseLocationProperties.maxResults = 1;
      
      mLocation.reverseLocate(coord, reverseLocationProperties, geoDataSource, 
         function(geocodeAddressList,exc)
         {
            if (exc) 
            {
               alert("error [" + exc + "]");
               return false;
            }
            
            var reverseGeoCoded = "";
            log.debug(fnName, "number of results [" + geocodeAddressList.addresses.length + "]");
            if(geocodeAddressList != null && geocodeAddressList.addresses.length > 0)
            {
               for(var i=0; i<addressElemNames.length-2; i++)
               {
                  var value = null;
                  eval("value=geocodeAddressList.addresses[0]." + addressElemNames[i]);               
                  if(addressElemNames[i] == "state")
                  {
                     value = toState(geocodeAddressList.addresses[0].country);
                     geocodeAddressList.addresses[0].state=value;
                  }
                  
                  if(i>0)
                     reverseGeoCoded += '|';
                  reverseGeoCoded += value;
               }
               log.debug(fnName, "reverseGeocoded [" + reverseGeoCoded + "]");
               
               realCoordinate.address=geocodeAddressList.addresses[0];
            }
            if(callbackFn != null)
               callbackFn(realCoordinate);
         });
   };
   
   /**
    * This function converts a 2Letter PTV state to a 3 letter version
    *
    * 
    */
   function toState(country)
   {
      var ptvState = country.split("-")[1];
      var result = null;
      if ("VI" == ptvState)
         result = "VIC";
      else if ("SW" == ptvState)
         result = "NSW";
      else if ("QL" == ptvState)
         result = "QLD";
      else if ("TA" == ptvState)
         result = "TAS";
      else if ("AC" == ptvState)
         result = "ACT";
      else  // NT, SA, WA
         result = ptvState;
      return result;
   }
   
   function encodeState(state)
   {
      var result = null;
      if ("VIC" == state)
         result = "VI";
      else if ("NSW" == state)
         result = "SW";
      else if ("QLD" == state)
         result = "QL";
      else if ("TAS" == state)
         result = "TA";
      else if ("ACT" == state)
         result = "AC";
      else  // NT, SA, WA
         result = state;
      return result;
   }
   
   function Geocode_cb(geocodeAddressList,exc, inputAddress, autoRetry)
   {
      var fnName = cName + "Geocode_cb() ";
      
      if(autoRetry == null)
         autoRetry = true;
      log.debug(fnName, "input address [" + inputAddress.city + "]");
      if(geocodingTimer != null)
      {
         clearTimeout(geocodingTimer);
         geocodingTimer = null;
      }
      if (exc) 
      {
         MapUtils.processError(200);
         return false;
      } 
      log.debug(fnName, "list returned");
      var addressArr = geocodeAddressList.addresses;
      
      // hide wait symbol at station again:
      //Effect.Fade($('ajaxloaderstation'+GeocodeStation));
   
      // cache geocode proposal list at station property "geocodelist":
      //getStation(GeocodeStation).geocodelist = addressArr;
      var output = document.getElementById(form.id + ".selection");
      log.debug(fnName, "number of results [" + addressArr.length + "]");
      if(addressArr.length > 0) 
      {
         var selectoptions = "";
         var exactMatches = new Array();
         var nonExactMatches = new Array();
         var displayNonExactMatches = false;
         for(var i=0;i<(addressArr.length);i++)
         {
            log.debug(fnName, "city [" + addressArr[i].city + "] cityDistrict [" + addressArr[i].cityDistrict + "] input [" + inputAddress.city + "]");
            if( addressArr[i].cityDistrict.toLowerCase().indexOf(inputAddress.city.toLowerCase()) >= 0 &&
                (addressArr[i].zipcode != null && addressArr[i].zipcode.length > 0)
               )
               exactMatches[exactMatches.length] = addressArr[i];
            else if( (addressArr[i].cityDistrict == null || addressArr[i].cityDistrict.length == 0) &&   
                     addressArr[i].city.toLowerCase().indexOf(inputAddress.city.toLowerCase()) >= 0 &&
                     (addressArr[i].zipcode != null && addressArr[i].zipcode.length > 0)
                     )
               exactMatches[exactMatches.length] = addressArr[i];
            else
               nonExactMatches[nonExactMatches.length] = addressArr[i];
         }
         
         if(exactMatches.length > 0)
            addressArr = exactMatches;
         else if(autoRetry == false)
         {
            addressArr = nonExactMatches;
            displayNonExactMatches=true;
         }
         else
            addressArr = new Array();
         log.debug(fnName, "filtered matches [" + addressArr.length + "]");
         for (var i=0;i<(addressArr.length);i++)
         {    
            // check if there is a post code if we do not, get rid of them
            if (i%2 == 0) 
               optstyle = "even"
            else
               optstyle = "odd"
   
            /*if (i==0)
               selectedstr = " selected";
            else
               selectedstr = "";*/
            
            // Serialize address suggest entry:
            // city cityDistrict|street houseNumber|zipCode|longitude|latitude|geoCodeType
            if(addressArr[i].cityDistrict == null || addressArr[i].cityDistrict.length == 0)
               addressArr[i].cityDistrict =addressArr[i].city; 
               
            addressValue = ""+addressArr[i].city + "|" + addressArr[i].cityDistrict + "|" + 
                           addressArr[i].street + "|" + (inputAddress.houseNumberSpecified?addressArr[i].houseNumber:"")+ "|" + 
                           addressArr[i].zipcode + "|" + toState(addressArr[i].country) + "|" +
                           (addressArr[i].coordinate.x/conf_smartUnitToGeoFactor) + "|" + (addressArr[i].coordinate.y/conf_smartUnitToGeoFactor) + "|" + addressArr[i].coordinate.coordinateType;
            
            // visible option text:
            var suburb = addressArr[i].cityDistrict;
            /*if(inputAddress.city != null)
            {
               var cityDistrict = addressArr[i].cityDistrict;
               if(cityDistrict != null)
                  cityDistrict = cityDistrict.toLowerCase();
               else
                  cityDistrict = "";
               var city = addressArr[i].city;
               if(city != null)
                  city = city.toLowerCase();
               else
                  city = "";
               
               if(suburb != null && 
                  //inputAddress.city.toLowerCase() != cityDistrict && inputAddress.city.toLowerCase() != city &&
                  inputAddress.city.toLowerCase().indexOf(cityDistrict) >= 0 && 
                  city != cityDistrict
                  )
                  suburb = addressArr[i].cityDistrict;
               //else if(cityDistrict.indexOf(inputAddress.city.toLowerCase()) >= 0)
               //   suburb = addressArr[i].cityDistrict;
               //else if(city.indexOf(inputAddress.city.toLowerCase()) >= 0)
               //   suburb = addressArr[i].city;
               
            }*/
            
            /*if(suburb != null && addressArr[i].cityDistrict != null && suburb.toLowerCase() != addressArr[i].cityDistrict.toLowerCase())
               suburb += " " + addressArr[i].cityDistrict;*/
            locationstring = toState(addressArr[i].country) + " " + addressArr[i].zipcode + " " +  
                             suburb + " " +  
                             addressArr[i].street + " ";
            if(locationstring != null && selectoptions.indexOf(locationstring) > 0)
               continue;                             
            if(inputAddress.houseNumberSpecified)
               locationstring += addressArr[i].houseNumber;
               
            // add option tag:
            //selectoptions += '<option'+ selectedstr +' class='+ optstyle +' value="'+ addressValue + '">'+ locationstring +'</option>';
            selectoptions += "<div class=\"" + optstyle + "\" onclick=\"GeoCoder.selectAddress('" + addressValue + "', '" + form.id + "')\">" + locationstring + "</div>"
         }
         
         log.debug(fnName, "selectoptions [" + selectoptions.length + "] [" + autoRetry + "]");
         if(selectoptions.length == 0 && autoRetry)
         {
            //nothing was found!
            // lets pull the city out.
            log.debug(fnName, "autoretrying...");
            var mLocation = new com.ptvag.mnp.location.Location();
            var dupAddress = {};
            
            for(prop in inputAddress)
               eval("dupAddress." + prop + "=inputAddress." + prop);
            
            dupAddress.street="";
            dupAddress.zipcode="";
            mLocation.locate(dupAddress, locateProperties, geoDataSource, 
                             function(geoCodedCity, geoCodedErr)
                             {
                                if(geoCodedErr) 
                                {
                                   MapUtils.processError(200);
                                   log.debug(fnName, "failed to geocode city");
                                   return false;
                                } 
                                if(geoCodedCity.addresses.length > 0)
                                {
                                    // grab the 1st city
                                    for(prop in geoCodedCity.addresses[0])
   	      					  			{
   	      					  				var value = "";
   	      					  				eval("value = geoCodedCity.addresses[0]." + prop + ";");
   	      					  				log.debug(fnName, "&gt " + prop + ": [" + value + "]"); 
   	      					  			}
                                    var dupAddress2 = {};
                                    for(prop in inputAddress)
                                       eval("dupAddress2." + prop + "=inputAddress." + prop);
                                    dupAddress2.cityDistrict = inputAddress.city;
                                    dupAddress2.city = geoCodedCity.addresses[0].city;
                                    
                                    //inputAddress.cityDistrict = inputAddress.city;
                                    //inputAddress.city = geoCodedCity.addresses[0].city;
                                    
                                    mLocation.locate(dupAddress2, locateProperties, geoDataSource,
                                    function(geocodeAddressList3,exc3){Geocode_cb(geocodeAddressList3,exc3, inputAddress, false);}); 
                                }
                                else
                                {
                                    output.innerHTML = "Address Not found [" + inputElement.value + "]";
                                }
                                
                             });

            
            //selectoptions = "Address not found";
         }
         //log.debug(fnName, "<select>" + selectoptions + "</select>");
         //output.innerHTML = '<select style="height:auto" onclick="GeoCoder.selectAddress(this.value, ' + form.name + ')">' + selectoptions + "</select>";
         output.innerHTML = selectoptions;
      }
      else
      {
         output.innerHTML = "Address Not found [" + inputElement.value + "]";
      }
   };
   
} // end of class


/**
 * Static function that is invoked when the user enters data for the address
 *
 */
GeoCoder.tryGeoCode=function tryGeoCode(formName, currentElement, event)
{
   var fnName = cName + "tryGeoCode() ";
   
   //if(event != null && event.keyCode < 10)
   //   return;
   
   var cityDistrict = document.getElementsByName(formName + ".cityDistrict")[0].value.replace(/^\s+|\s+$/g, '');
   if((currentElement.value.length <= 3 && currentElement.name.indexOf("state") == -1) || 
       cityDistrict.length == 0)
      return;
   if(geocodingTimer != null)
   {
      clearTimeout(geocodingTimer);
      geocodingTimer = null;
   }
      
   //setTimeout("alert('hey hey')", 1000);
   //geocodingTimer = setTimeout("GeoCoder.performGeoCode('" + formName + "', '" + currentElement.name + "')", 1500);
   geocodingTimer = setTimeout("GeoCoder.initialiseGeoCode('" + formName + "', '" + currentElement.name + "')", 1500);
}

GeoCoder.initialiseGeoCode=function initialiaseGeoCode(formName, currentElementName)
{
   geocodingTimer = null;
   document.getElementById(formName + ".selection").innerHTML = "<img src='" + conf_geocodeLoadingImg +"'/>"; 
   GeoCoder.performGeoCode(formName, currentElementName);
};
GeoCoder.performReverseGeoCode=function performReverseGeoCode(realCoordinate, callbackFn)
{
   var geoCoder = new GeoCoder();
   geoCoder.reverseGeoCode(realCoordinate, callbackFn);
};

/**
 * Function that is invoked by tryGeoCode function after a delay
 * 
 */
GeoCoder.performGeoCode=function performGeoCode(formName, currentElementName)
{
   var fnName = cName + "performGeoCode() ";
   var currentForm = document.getElementById(formName);
   
   var inputs = currentForm.getElementsByTagName("INPUT");
   var currentElement = XMLUtils.getFormElementByName(inputs, currentElementName);
   
   if(currentElement == null)
   {
      var selects = currentForm.getElementsByTagName("SELECT");
      currentElement = XMLUtils.getFormElementByName(selects, currentElementName);
   }
   log.debug(fnName, "geocoding [" + currentElement.value + "]");
   
   var geoCoder = new GeoCoder("output", currentForm, currentElement);
   geoCoder.geocode();
}

GeoCoder.selectAddress=function selectAddress(data, formDiv)
{
   var fnName = cName + "selectAddress() ";
   log.debug(fnName, "geocoder [" + data + "]");
   
   var addressElems = data.split("|");
   
   log.debug(fnName, "Number of elems [" + addressElems.length + "]");
   /*for(var i=0; i<addressElems.length; i++)
   {
      log.debug(fnName, "[" + i + "] - [" + addressElems[i] + "]");
   }*/
   var formInputElements = document.getElementById(formDiv).getElementsByTagName("input");
   var formSelectElements = document.getElementById(formDiv).getElementsByTagName("select");
   
   for(var i=0; i<addressElemNames.length; i++)
   {
      //var formElement = formInputElements[formDiv + "." + addressElemNames[i]];
      var formElement = XMLUtils.getFormElementByName(formInputElements, formDiv + "." + addressElemNames[i]); 
      
      if(formElement == null)
         formElement = XMLUtils.getFormElementByName(formSelectElements, formDiv + "." + addressElemNames[i]);
         //formElement = formSelectElements[formDiv + "." + addressElemNames[i]];
      
      if(formElement != null)
      {
         formElement.value = "";
         if(i == 2) // street name
            formElement.value += addressElems[3] + " ";
         
         formElement.value += addressElems[i];
      }
      
   }
}

/**
 * Util function to clear the input elements in an address field form
 * @param formId - the form with ID formId to be cleared
 * @param excludeElems - map of elements in the form to be excluded from being cleared
 *                       format {foo:true} will cause element with name "foo" to be
 *                       cleared in the form  
 */
GeoCoder.clearForm=function clearForm(formId, excludeElems)
{
   var formElements = document.getElementById(formId).getElementsByTagName("input");
   for(var i=0; i<formElements.length; i++)
   {
      if(formElements[i].type == "text" || formElements[i].type == "hidden")
      {
         var doExclude = false;
         if(excludeElems != null)
         {
            var name = formElements[i].name;
            eval("doExclude = excludeElems" + name.substring(name.lastIndexOf("."), name.length));
         }
         if(!doExclude)
            formElements[i].value = "";
      }
   }
   
   var formSelectElements = document.getElementById(formId).getElementsByTagName("select");
   
   for(var i=0; i<formSelectElements.length; i++)
   {
      formSelectElements[i].selectedIndex=0;
   }
}

GeoCoder.currentMarker = null;

/**
 * Util function to display an address onto a map
 */

GeoCoder.displayMap=function displayMap(formId, currentMap)
{
   var fnName = cName + "displayMap() ";
   
   var currentForm = document.getElementById(formId);
   var formElements = currentForm.getElementsByTagName("input");
   var longitude = parseFloat(formElements[formId + ".longitude"].value);
   var latitude = parseFloat(formElements[formId + ".latitude"].value);
   
   var smartCoordinate = ptvWrapper.getSmartCoordinate({longitude:longitude,latitude:latitude});
   
   if(currentMap == null)
      currentMap = map;
   currentMap.setCenter(smartCoordinate);
   
   if(GeoCoder.currentMarker != null)
      currentMap.getLayer("vector").removeElement(GeoCoder.currentMarker);
   
   GeoCoder.currentMarker = currentMap.getLayer("vector").addElement(new com.ptvag.webcomponent.map.vector.ImageMarker(smartCoordinate.x, smartCoordinate.y, conf_defaultMarkerImg, 65));
   currentMap.setViewToPoints([smartCoordinate], false, false);
   log.debug(fnName, "Longitude [" + longitude + "] latitude [" + latitude + "]");
}




