/*
* @author : Amit Kathotia@KCPL
* Javascript Library for the Insurance GMap Functionality. Includes the following - 
*  1. Classes 
		a. HashMap
		b. City
		c. Route
		d. Insurance Address
		e. Insurance Correspondent
		f. Insurance Correspondent Search
		g. In Limit Locator
*  2. Global Variables - map, geocoder, cityGeocodingStatusIconHtml, reasons, cityMap etc.
*  3. Global Functions - initGeoCoder, fnSetAndShowLocation etc.
*  4. Utility Functions - createGIcon, fnCollapseOrExpand, processEnter, trim, bind etc.
*/

/*******/
	/* HASHMAP CLASS STARTS */ 

	function HashMap() {
		this.keys = [],
		this.values = [],
		this.get = function (key){
			return (this[key])? this[key]:null;
		},
		this.getKeys = function (){
			return (this.keys)? this.keys:null ;
		},
		this.getValues = function (){
			return this.values ;
		},
		this.put = function (key,value) {
			if(!key || key == null)
				return ;
				
			if(this[key]) {
				for(var i = 0; i <this.keys.length ; i++) {
					if(this.keys[i] == key) {
						this.values[i] = value;
					}
				}
				this[key] = value;
			}else {
				this.keys[this.keys.length] = key;
				this.values[this.values.length] = value;
				this[key] = value;
			}
		},
		this.remove = function (key) {
			var foundKey = false;
			for(var i = 0; i <this.keys.length; i++) {
				if(this.keys[i] == key) {
					foundKey = true ;
				}
				if(foundKey) {
					if(i == this.keys.length - 1) {
						this.keys.pop(); 
						this.values.pop();
					}else {
						this.keys[i] = this.keys[i+1]
						this.values[i] = this.values[i+1]
					}
				}
			}
			delete this[key] ;
		},
		this.removeAll = function () {
			for(var i = 0; i <this.keys.length;) {
				delete this[this.keys[i]] ;
				this.keys.pop(); 
				this.values.pop();
			}
		}
		this.toStr = function () {
			var str = "";
			for(var i = 0; i <this.keys.length;i++) {
				str = str + "Keys :- " + this.keys[i] + ", Value :- " + this[this.keys[i]] + "<br>";
			}
			return str ;
		}
	}
/* HASHMAP CLASS END */ 


	/* CITY CLASS STARTS */ 
	function city() {
		this.id = null,
		this.type = null,
		this.sourceElem = null,
	   
		this.latElement	= null,
		this.lngElement	= null,
		this.normalizedCountryElement = null,
		this.normalizedCountryCodeElement = null,
		this.normalizedAddressElement = null,
	   
		this.marker = null ,
		this.markerIcon = null ,
		this.markerDragEndListener = null, // TODO - Not required as of Now
		this.placeMark = null ,
		this.markerMovedElement	= null,
	   
		this.geoCodingStatus = STATUS_NONE ,
		this.onGeocodingComplete = [],
		
		this.statusIconDiv = null,
		this.userMessageDiv = null,
		this.zoomerControl = null,
		this.icControl = null,
		this.zoomLevel = cityNormalZoomLevel,
	   
		/** Function to init JS variables / events corrosponding to city **/
		this.init = function(id, type) {
			this.id = id;
			this.type = type;
			
			this.sourceElem = document.getElementById(this.id);
			this.statusIconDiv = document.getElementById(this.id + "StatusIcon");
			this.userMessageDiv = document.getElementById(this.id + "UserMessageDiv");
			this.zoomerControl = document.getElementById(this.id + "ZoomerControl");
			this.icControl = document.getElementById(this.id + "ICControl");
			this.latElement	= document.getElementById(this.id + "Lat");
			this.lngElement	= document.getElementById(this.id + "Lng");
			this.normalizedCountryElement = document.getElementById(this.id + "NormalizedCountry");
			this.normalizedCountryCodeElement = document.getElementById(this.id + "NormalizedCountryCode");
			this.normalizedAddressElement = document.getElementById(this.id + "NormalizedAddress");
			this.markerMovedElement	= document.getElementById(this.id + "MarkerMoved");
			this.updateGeoCodingStatusAndMsg(STATUS_NONE);
			this.onGeocodingComplete = [];
			
			/* Google Bind is not working hence do some good work oneself */			
			this.sourceElem.onchange = this.fnOnCityChange.bind(this); // Need this to bind the scope
			this.zoomerControl.onclick = this.fnCenterAndZoomIn.bind(this); // Need this to bind the scope
			this.icControl.onclick = this.fnICControlClick.bind(this); // Need this to bind the scope
			this.statusIconDiv.onclick = this.fnStatusIconClick.bind(this); // Need this to bind the scope, @1.2 - Remove Event Handler from HTML 
			
			return this ;
		},
			
		/** Function to handle onChange of City **/
		this.fnOnCityChange = function(){
			this.resetGeoCoding();
			if(trim(this.getValue()) != '') {
				this.fnGeoCodeAddress(this.getValue(),GEOCODING_TYPE_NORMAL);
			}
			else {
				this.fnOnGeocodingComplete();
			}
		},
	
		/** Function to call city geocoding service of google **/
		this.fnGeoCodeAddress = function (addressToGeoCode,geoCodingType){
			this.updateGeoCodingStatusAndMsg(STATUS_IN_PROCESS);
			hGCR = this.handleGeoCodingResponse.bind(this);
			geocoder.getLocations(addressToGeoCode, function(result) {hGCR(result,geoCodingType)});
		},

		/** Function to handle city geocoding response from google and either sets location or message or show error **/
		this.handleGeoCodingResponse = function (result,geoCodingType){
			if (result.Status.code == G_GEO_SUCCESS) {
				this.setPlaceMark(result.Placemark);
				if (result.Placemark.length > 1 && geoCodingType == GEOCODING_TYPE_NORMAL) {
					userMsg = "Did you mean:";
					for (var i=0; i<result.Placemark.length; i++) {
						userMsg += "<br>"+
						(i+1)+": <a href='#' onclick=\"fnSetAndShowLocation('" + this.id + "','" + i + "'," + geoCodingType + ");\">"+ result.Placemark[i].address+"</a>";
					}
					this.updateGeoCodingStatusAndMsg(STATUS_INPUT_INCOMPLETE,userMsg);
				}
				else {
					this.setAndShowLocation(0,geoCodingType);
				}
			} else { // In case of error, display error message and put a marker at the center if its an error during normal geocoding and not reverse geocoding.
				var reason="Unknown Error, Error Code : "+result.Status.code;
				if (reasons[result.Status.code]) {
					reason = reasons[result.Status.code]
				}
				if(geoCodingType == GEOCODING_TYPE_NORMAL) {
					this.updateGeoCodingStatusAndMsg(STATUS_INPUT_ERRORED, 'Could not find "'+result.name+ '" <BR>' + reason+ '" <BR>Adding marker to the center. Please drag to the appropriate location');
					this.placeMarker(map.getCenter());
				}
				else
					this.updateGeoCodingStatusAndMsg(STATUS_INPUT_ERRORED, 'Could not find "'+result.name+ '" <BR>' + reason+ '"');

				this.fnOnGeocodingComplete();
			}
		},

		/** Function to set the location details and show the locations. Includes -
		placemark decoding, marker placement, adjusting map, updating status and firing geocoding complete event **/
		this.setAndShowLocation = function (placemarkNumber,geoCodingType) {
			placemark = this.getPlaceMark()[placemarkNumber];
			
			var p = placemark.Point.coordinates;
			lat = p[1];
			lng = p[0];
			address = placemark.address;
			cityName = stateName = countryName = countryCode = "NA" ;
				
			if(placemark.AddressDetails.Country) {
				countryName = placemark.AddressDetails.Country.CountryName;//CountryName 
				countryCode = placemark.AddressDetails.Country.CountryNameCode;//CountryNameCode 
				if(placemark.AddressDetails.Country.AdministrativeArea) {
					stateName = placemark.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName ;
					if(placemark.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea) {
						stateName = placemark.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.SubAdministrativeAreaName + " | " + stateName ;
						if(placemark.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality) {
							cityName = placemark.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName ;
						}
					}
					else if(placemark.AddressDetails.Country.AdministrativeArea.Locality) {
						cityName = placemark.AddressDetails.Country.AdministrativeArea.Locality.LocalityName ;
					}
				}
				else if(placemark.AddressDetails.Country.Locality) {
					cityName = placemark.AddressDetails.Country.Locality.LocalityName ;
				}
			}
				
			currentLocation = new GLatLng(lat,lng);
			
			if(geoCodingType == GEOCODING_TYPE_NORMAL) {
				this.placeMarker(currentLocation);
				this.setHasMarkerMoved("false");
				this.setLat(lat);
				this.setLng(lng);
			}
			else {
				currentLocation = this.marker.getLatLng();
				this.setLat(currentLocation.lat());
				this.setLng(currentLocation.lng());
 			}
			
			this.setGNormalizedCountry(countryName);
			this.setGNormalizedCountryCode(countryCode);
			this.setGNormalizedAddress(address);
			this.zoomLevel = cityNormalZoomLevel;
			cityLocations.put(this.id,currentLocation);
			
			fnAdjustMapAndResetZoom(currentLocation) ; // Animate the zoom and map - this.adjustMapTimer = setTimeout("fnAdjustMapAndResetZoom(currentLocation)",10); //
			
			this.updateGeoCodingStatusAndMsg(STATUS_INPUT_OK);
			this.fnOnGeocodingComplete();
		},
		
		/** Function to create Marker and Marker Icon and add Event Handler to the Marker Move Event **/
		this.placeMarker = function(currentLocation){
			
			if(this.markerIcon == null)
				this.markerIcon = createGIcon(this.getType(),false,"CITY");
			
			if(this.marker != null) { // Marker already present just need to move
				this.marker.setLatLng(currentLocation);
				this.marker.title = this.id + " : " + this.getValue(); // TODO - To update the title on move - Undocumented method
			}
			else { // Need to create a new marker
				this.marker = new GMarker(currentLocation,{
						icon:this.markerIcon,
						draggable:true,
						title:this.id + " : " + this.getValue(),
						bouncy:true,
						dragCrossMove:true
					})
				map.addOverlay(this.marker);
				GEvent.bind(this.marker, "dragend", this, this.onMarkerMove); // this.markerDragEndListener = 
			}
		},
		
		/** Function to setMarkerIcon **/
		this.setMarkerIcon = function(iconUrl){
			
			this.markerIcon = new GIcon(iconUrl) ;
			
			if(this.isCityGeocoded()) {
				if(this.marker != null) { // Marker already present remove it.
					map.removeOverlay(this.marker);
					this.marker = null ;
				}
				this.marker = new GMarker(this.getLatLng(),{
						icon:this.markerIcon,
						draggable:true,
						title:this.id + " : " + this.getValue(),
						bouncy:true,
						dragCrossMove:true
					});
				map.addOverlay(this.marker);
				GEvent.bind(this.marker, "dragend", this, this.onMarkerMove); // this.markerDragEndListener = 
			}
		},
		
		/** Function to Center and Zoom-in based to the current city **/
		this.fnCenterAndZoomIn = function(){
			if(this.isCityGeocoded()) {
				this.zoomLevel = this.zoomLevel + 1 ;
				this.zoomLevel  = (this.zoomLevel < 20)? this.zoomLevel  : 20 ;
				map.setCenter(this.getLatLng(), this.zoomLevel);
			}
		},
		
		/** Function to Handle click of IC Control **/
		this.fnICControlClick = function(){
			fnSearchInsuranceCorrespondent("CITY",this.getLatLng(),this.getGNormalizedCountryCode());
		},
		 
		/** Function to Handle click of Status Icon **/
		this.fnStatusIconClick = function(){
			fnCollapseOrExpand(document.getElementById(this.id + 'UserMessageWrapper')); // @1.2 - Remove Event Handler from HTML
		},
		 
		/** Function to reset geocoding **/
		this.resetGeoCoding = function (){
			
			if(this.isCityGeocoded()) {
				this.setLat("");
				this.setLng("");
				this.setGNormalizedCountry("");
				this.setGNormalizedCountryCode("");
				this.setGNormalizedAddress("");
				this.setHasMarkerMoved("false");
				if(this.marker != null) { // Marker already present remove it.
					map.removeOverlay(this.marker);
					this.marker = null ;
				}
				this.markerIcon = null;
				this.zoomLevel = cityNormalZoomLevel;
				cityLocations.remove(this.id);
			}
			this.placeMark = null; // There may be a case when placemark is set and city is not geocoded -> when status is STATUS_INPUT_INCOMPLETE
			this.updateGeoCodingStatusAndMsg(STATUS_NONE);
		},

		/** Function to handler on Marker Move event **/
		this.onMarkerMove = function(gLatLng) {
			this.setHasMarkerMoved("true");
			this.fnGeoCodeAddress(gLatLng,GEOCODING_TYPE_REVERSE);
		},
		
		/** Function to fire Geocoding Complete event by invoking all the subscribers **/
		this.fnOnGeocodingComplete = function() {
			for(i=0 ; i < this.onGeocodingComplete.length ; i++)
				this.onGeocodingComplete[i]() ;
		},
		
		/** Function to set geocoding status and message **/
		this.updateGeoCodingStatusAndMsg = function (status,msg){ 
			this.geoCodingStatus = status ;
			this.statusIconDiv.innerHTML = cityGeocodingStatusIconHtml[status] ;
			this.zoomerControl.style.display = "none" ;
			this.icControl.style.display = "none" ;

			switch(status) {
				case STATUS_NONE :
					this.setGeoCodingUserMessage(""); 
					fnCollapseOrExpand(this.id + "UserMessageWrapper",'collapsed');
					break;
				case STATUS_INPUT_OK :
					this.setGeoCodingUserMessage(this.getLocationDetails()); // Show the Location details
					this.zoomerControl.style.display = "inline" ;
					this.icControl.style.display = "inline" ;
					break;
				case STATUS_INPUT_INCOMPLETE :
					this.setGeoCodingUserMessage(msg);  // Show the did you mean
					break;
				case STATUS_INPUT_ERRORED :
					this.setGeoCodingErrorMessage(msg);  // Show the error msg
					break;
			}
		},
		
		/** Function to show message in City UserMessage div **/
		this.setGeoCodingUserMessage = function (msg) {
			this.userMessageDiv.className = '';
			this.userMessageDiv.innerHTML = msg ;
		},

		/** Function to show error in User Message div **/
		this.setGeoCodingErrorMessage = function (errorMsg) {
			this.userMessageDiv.className = "errored";
			this.userMessageDiv.innerHTML = errorMsg;
			fnCollapseOrExpand(this.id + "UserMessageWrapper",'expanded');
		},
	
		/** Function to return the Location Details in html div format **/
		this.getLocationDetails = function() {
			return "<div id='locationDetails'>Data returned from Google:<br>" + 
				//"<div class='locationDetailsLabel'>City : </div>" + cityName + "<br>" +
				"<div class='locationDetailsLabel'>Has Marker Moved : </div>" + this.getHasMarkerMoved() + "<br>" +
				"<div class='locationDetailsLabel'>Country : </div>" + this.getGNormalizedCountry() + "<br>" +
				"<div class='locationDetailsLabel'>Country Code : </div>" + this.getGNormalizedCountryCode() + "<br>" +
				"<div class='locationDetailsLabel'>Address : </div>" + this.getGNormalizedAddress() + "<br>" +
				"<div class='locationDetailsLabel'>Latitude : </div>" + this.getLat() + "<br>" +
				"<div class='locationDetailsLabel'>Longitude : </div>" + this.getLng() + "<br>" +
				"</div>";
		},
		
		this.setValue = function(value) { this.sourceElem.value = value ;}, // TODO - Not required as this value is never to change from JS
		this.getValue = function(){	return this.sourceElem.value ;}, 
		this.setType = function (type){	this.type = type ;},
		this.getType = function (){	return this.type ;},
		this.setLat = function(lat) { this.latElement.value = lat ;},
		this.getLat = function(){	return this.latElement.value ;}, 
		this.setLng = function(lng) { this.lngElement.value = lng ;},
		this.getLng = function(){	return this.lngElement.value  ;}, 
		this.getLatLng = function(){return new GLatLng(this.latElement.value,this.lngElement.value);}, 
		this.setGNormalizedAddress = function(gNormalizedAddress) { this.normalizedAddressElement.value = gNormalizedAddress ;},
		this.getGNormalizedAddress = function(){return this.normalizedAddressElement.value ;}, 
		this.setGNormalizedCountry = function(gNormalizedCountry) { this.normalizedCountryElement.value = gNormalizedCountry ;},
		this.getGNormalizedCountry = function(){return this.normalizedCountryElement.value ;}, 
		this.setGNormalizedCountryCode = function(gNormalizedCountryCode) { this.normalizedCountryCodeElement.value = gNormalizedCountryCode ;},
		this.getGNormalizedCountryCode = function(){return this.normalizedCountryCodeElement.value ;}, 
		this.setHasMarkerMoved = function(hasMarkerMoved) {this.markerMovedElement.value = hasMarkerMoved ;},
		this.getHasMarkerMoved = function(){return this.markerMovedElement.value ;}, 
		this.setPlaceMark = function (placeMark){this.placeMark = placeMark ;},
		this.getPlaceMark = function (){return this.placeMark ;},
		this.isCityGeocoded = function (){return (this.geoCodingStatus == STATUS_INPUT_OK)?true:false;},
		this.addGeocodingCompleteHandler = function (handler){this.onGeocodingComplete[this.onGeocodingComplete.length] = handler;}
	 }
	/* CITY CLASS ENDS */ 

	 
	/* ROUTE CLASS STARTS */ 
	
	function route() {
		this.id = null,
		this.routeType = null, // Travel Type - Sea / Land / Air
		this.insuranceType = null,  // @@ NOT USED - Kind of Insurance - None, Partial etc.. // Should set the GPolyStyleOptions
		this.startLocation = null, // The city object
		this.endLocation = null, // The city object
		
		this.routePoints = [],
		this.polyLine = null,
		this.routeStyleOption = { // Default Route style
			color : "#FF0000",
			weight : 2 ,
			opacity : .7 ,
			geodesic : false
		},
		this.routingStatus = STATUS_NONE ,
		this.statusIconDiv = null,
		this.userMessageDiv = null,
		
		/* Function to init JS variables / events corrosponding to route */
		this.init = function(id,startLocation,endLocation) {
			this.id = id;
			this.startLocation	= startLocation;
			this.endLocation = endLocation;
			
			this.statusIconDiv = document.getElementById(this.id + "_StatusIcon");
			this.userMessageDiv = document.getElementById(this.id + "_UserMessageDiv");
			this.routeType	= document.getElementById(this.id + "_type"); // Travel Type - Sea / Land / Air
			this.insuranceType = document.getElementById(this.id + "_insuranceType");// Kind of Insurance - None, Partial etc..
			
			this.statusIconDiv.onclick = this.fnStatusIconClick.bind(this); // Need this to bind the scope, @1.2 - Remove Event Handler from HTML 
						
			this.updateRoutingStatusAndMsg(STATUS_NONE);
			
			/* Google Bind is not working hence do some good work oneself */			
			this.routeType.onchange = this.fnOnRouteTypeChange.bind(this); // For change of Route Type
			this.startLocation.addGeocodingCompleteHandler(this.fnOnCityChange.bind(this)); // For change of Start Location
			this.endLocation.addGeocodingCompleteHandler(this.fnOnCityChange.bind(this)); // For change of End Location
			document.getElementById(this.id + "_insured").onclick = this.fnOnRouteInsuranceChange.bind(this); // Change of Insurance Flag - TODO - whether onclick or onchange ? IE fires onchange on lost of focus ?
			//document.getElementById(this.id + "_insured").onclick = this.fnOnRouteInsuranceChange.bind(this); // Change of Insurance Flag - TODO - whether onclick or onchange ? IE fires onchange on lost of focus ?
			
			this.updateRouteStyle();
			
			return this ;
		},
		
		/* Function to handle onChange of Route Type */
		this.fnOnRouteTypeChange = function(){
			// @optmization - Do not need to geocode again, just need to update the style
			this.updateRouteStyle();
		},
	
		/* Function to handle onChange of Route Insurance */
		this.fnOnRouteInsuranceChange = function(){
			// @optmization - Do not need to geocode again, just need to update the style
			this.updateRouteStyle();
		},
	
		/** Function to Handle click of Status Icon **/
		this.fnStatusIconClick = function(){
			fnCollapseOrExpand(document.getElementById(this.id + '_UserMessageWrapper')); // @1.2 - Remove Event Handler from HTML
		},
	
		/* Function to handle onChange of City */
		this.fnOnCityChange = function(){
			this.updateRoute(); // - Geocode and plot the route
		},
	
		/* Function to set route insurance */
		this.setRouteInsurance =  function(insure){
			if(insure) 
				document.getElementById(this.id + "_insured").checked=true;
			else
				document.getElementById(this.id + "_insured").checked=false;
			this.fnOnRouteInsuranceChange(); 
		},
		
		/* Function to update route */
		this.updateRoute = function(){
			this.resetRoute();
			if(this.startLocation.isCityGeocoded() && this.endLocation.isCityGeocoded())
				this.plotRoute();
		},
	
		/* Function to update route style */
		this.updateRouteStyle = function (){
			
			//var oldGeoDesic = this.routeStyleOption.geodesic ;
			
			if(document.getElementById(this.id + "_insured").checked) {
				this.routeStyleOption.weight = 4 ;
				this.routeStyleOption.opacity = 1 ;
				this.routeStyleOption.geodesic = false ;
				
				if(this.getRouteType() == "LAND") {
					this.routeStyleOption.color = "#000000" ;
				}else if(this.getRouteType() == "SEA") {
					this.routeStyleOption.color = "#4444FF" ;
				}else if(this.getRouteType() == "AIR") {
					this.routeStyleOption.color = "#88BBFF" ;
					this.routeStyleOption.geodesic = true;
				}
			}
			else {
				this.routeStyleOption.weight = 2 ;
				this.routeStyleOption.opacity = .7 ;
				this.routeStyleOption.geodesic = false ;
					
				if(this.getRouteType() == "LAND") {
					this.routeStyleOption.color = "#AAAAAA" ;
				}else if(this.getRouteType() == "SEA") {
					this.routeStyleOption.color = "#2222AA" ;
				}else if(this.getRouteType() == "AIR") {
					this.routeStyleOption.color = "#6688AA" ;
					this.routeStyleOption.geodesic = true;
				}
				
			}/*
			if(this.polyLine != null) { // && this.routeStyleOption.geodesic == oldGeoDesic
				this.polyLine.setStrokeStyle(this.routeStyleOption); // this.drawRoute();
				this.updateRoutingStatusAndMsg(STATUS_INPUT_OK);
			}
			else */
				this.updateRoute();
		},

		/* Function to locate an address using Google and show details in the city message box */
		this.plotRoute = function (){
//			alert('plotting route');
			this.updateRoutingStatusAndMsg(STATUS_IN_PROCESS);
			
			if(this.getRouteType() == "SEA") { // Need to incorporate the Sea Point Logic - Simple logic to draw a Geodesic line.
				this.seaRouting();
			}
			else if(this.getRouteType() == "AIR") { // Simple logic to draw a line.
				this.airRouting();
			}
			else if(this.getRouteType() == "LAND") { // Get the route a.k.a. driving directions from Google and draw. - Get Simple logic to draw a line.
				this.landRouting();
			}
			else {
				this.updateRoutingStatusAndMsg(STATUS_INPUT_ERRORED,'Route Type not selected');
			}
		
		},

		this.seaRouting = function(){ // For getting sea route points and plotting route
			this.routePoints = [this.getStartLocation().getLatLng(),this.getEndLocation().getLatLng()]
			this.drawRoute();
		},
		
		this.airRouting = function(){ // For getting air route points and plotting route
			this.routePoints = [this.getStartLocation().getLatLng(),this.getEndLocation().getLatLng()]
			this.drawRoute();
		},
		
		this.landRouting = function(){ // For getting air route points and plotting route
			this.routePoints = [this.getStartLocation().getLatLng(),this.getEndLocation().getLatLng()]
			this.drawRoute();
		},
		
		this.drawRoute = function (){
//			alert('Draw Route - ' + this.routePoints + " : " + this.routeStyleOption.color + " : " + this.routeStyleOption.weight + " : " + this.routeStyleOption.opacity + " : " + this.routeStyleOption.geodesic);
			this.polyLine = new GPolyline(this.routePoints,
				this.routeStyleOption.color , this.routeStyleOption.weight, 
				this.routeStyleOption.opacity, { "geodesic" : this.routeStyleOption.geodesic});
			map.addOverlay(this.polyLine);
			this.updateRoutingStatusAndMsg(STATUS_INPUT_OK);
		},

		/* Function to reset geocoding */
		this.resetRoute = function (){
			if(this.isRoutePlotted()) {
				this.routePoints = [];
				if(this.polyLine != null) {
					map.removeOverlay(this.polyLine);
					this.polyLine = null ;
				}
				this.updateRoutingStatusAndMsg(STATUS_NONE);
			}
		},

		/* Function to set geocoding status icon */
		this.updateRoutingStatusAndMsg = function (status,msg){
			this.routingStatus = status ;
			this.statusIconDiv.innerHTML = cityGeocodingStatusIconHtml[status] ;

			switch(status) {
				case STATUS_NONE :
					this.setRoutingUserMessage(""); 
					fnCollapseOrExpand(this.id + "_UserMessageWrapper",'collapsed');
					break;
				case STATUS_INPUT_OK :
					this.setRoutingUserMessage(this.getRouteDetails()); // Show the Location details
					break;
				case STATUS_INPUT_INCOMPLETE :
					this.setRoutingUserMessage(msg);  // Show the did you mean
					break;
				case STATUS_INPUT_ERRORED :
					this.setRoutingErrorMessage(msg);  // Show the error msg
					break;
			}
		},
		this.getRouteDetails = function() {
			return "<div id='locationDetails'>Route Plotted from Google:<br>" + 
				"<div class='locationDetailsLabel'>Route Type : </div>" + this.getRouteType() + "<br>" +
				"<div class='locationDetailsLabel'>Color : </div>" + this.routeStyleOption.color + "<br>" +
				"<div class='locationDetailsLabel'>weight : </div>" + this.routeStyleOption.weight + "<br>" +
				"<div class='locationDetailsLabel'>opacity : </div>" + this.routeStyleOption.opacity + "<br>" +
				"<div class='locationDetailsLabel'>geodesic : </div>" + this.routeStyleOption.geodesic + "<br>" +
				"<div class='locationDetailsLabel'>Start Location : </div>" + this.getStartLocation().getValue() + " [ " + this.getStartLocation().getLat() + " | " + this.getStartLocation().getLng() + " ]" + "<br>" +
				"<div class='locationDetailsLabel'>End Location : </div>" + this.getEndLocation().getValue() + " [ " + this.getEndLocation().getLat() + " | " + this.getEndLocation().getLng() + " ]" + "<br>" +
				"<div class='locationDetailsLabel'># Route Points : </div>" + this.getRoutePoints().length + "<br>" +
				"</div>";
		},
		
		/* Function to shown message in City UserMessage div */
		this.setRoutingUserMessage = function (msg) {
			this.userMessageDiv.className = '';
			this.userMessageDiv.innerHTML = msg ;
		},

		/* Function to show error in User Message div */
		this.setRoutingErrorMessage = function (errorMsg) {
			this.userMessageDiv.className = "errored";
			this.userMessageDiv.innerHTML = errorMsg;
			fnCollapseOrExpand(this.id + "_UserMessageWrapper",'expanded');
		},

	   
		this.setRouteType = function (routeType){this.routeType.value = routeType ;},
		this.getRouteType = function (){return this.routeType.value ;},
		this.setInsuranceType = function(insuranceType) { this.insuranceType.value = insuranceType ;},
		this.getInsuranceType = function(){	return this.insuranceType.value ;}, 
		this.setStartLocation = function(startLocation) { this.startLocation = startLocation ;},
		this.getStartLocation = function(){	return this.startLocation ;}, 
		this.setEndLocation = function(endLocation) { this.endLocation = endLocation ;},
		this.getEndLocation = function(){return this.endLocation ;}, 
		this.setRoutePoints = function(routePoints) { this.routePoints = routePoints ;},
		this.getRoutePoints = function(){return this.routePoints ;},
		this.isRoutePlotted = function(){return (this.routingStatus == STATUS_INPUT_OK)?true:false;}
	 }
	/* ROUTE CLASS ENDS */ 
	
	/* INSURANCE ADDRESS CLASS STARTS */ 
	function InsuranceAddress() {
		this.addressId = null,
		this.addressType = null,
		this.address = null,
		this.street = null,
		this.city = null,
		this.region = null,
		this.state = null,
		this.country = null,
		this.pin = null,
		this.isoCntryCode = null,
		this.lat = null,
		this.lng = null,
		this.accuracy = null,
		this.activeFlag = null,
	   
		/** Function to init JS variables / events corrosponding to city **/
		this.init = function(addressId,addressType){
			this.addressId = addressId;
			this.addressType = addressType;
			return this ;
		},
		
		this.toHTML = function(){	
			/*var addrHtml = '<b> Address :</b> ' + this.address + ' | ' 
						+ '<b> City :</b> ' + this.city + ' | '
						+ '<b> Country :</b> ' + this.country + ' | ' 
						+ '<b> IsoCntryCode :</b> ' + this.isoCntryCode + ' | '
						+ '<b> Lat :</b> ' + this.lat + ' | '
						+ '<b> Lng :</b> ' + this.lng + ' | ' ;
			
			var addrHtml = this.address + ', ' + this.city + ', ' + this.country + ', ' 
						+ '<b> IsoCntryCode :</b> ' + this.isoCntryCode + ' | '
						+ '<b> Lat :</b> ' + this.lat + ' | '
						+ '<b> Lng :</b> ' + this.lng + ' | ' ;
			*/
			var addrHtml = '' ;
			 
			if(this.address) addrHtml = addrHtml  + this.address + ", " ;
			if(this.city) addrHtml = addrHtml  + this.city + ", " ;
			if(this.country) addrHtml = addrHtml  + this.country + ", " ; 
			if(this.isoCntryCode) addrHtml = addrHtml  + "<br/><div class='correspondentDetailsLabel'>IsoCntryCode :</div>" + this.isoCntryCode + "<br/>" ;
			if(this.lat) addrHtml = addrHtml  + "<div class='correspondentDetailsLabel'>Lat :</div>" + this.lat + "<br/>" ;
			if(this.lat) addrHtml = addrHtml  + "<div class='correspondentDetailsLabel'>Lng :</div>" + this.lng ;	
			
			return addrHtml	;	
		},
		
		this.setAddress = function(address) { this.address = address ;},
		this.getAddress = function(){return this.address ;},
		this.setStreet = function(street) { this.street = street ;},
		this.getStreet = function(){return this.street ;},
		this.setCity = function(city) { this.city = city ;},
		this.getCity = function(){return this.city ;},
		this.setRegion = function(region) { this.region = region ;},
		this.getRegion = function(){return this.region ;},
		this.setState = function(state) { this.state = state ;},
		this.getState = function(){return this.state ;},
		this.setCountry = function(country) { this.country = country ;},
		this.getCountry = function(){return this.country ;},
		this.setPin = function(pin) { this.pin = pin ;},
		this.getPin = function(){return this.pin ;},
		this.setIsoCntryCode = function(isoCntryCode) { this.isoCntryCode = isoCntryCode ;},
		this.getIsoCntryCode = function(){return this.isoCntryCode ;},
		this.setLat = function(lat) { this.lat = lat ;},
		this.getLat = function(){	return this.lat ;}, 
		this.setLng = function(lng) { this.lng = lng ;},
		this.getLng = function(){	return this.lng ;}, 
		this.getLatLng = function(){return new GLatLng(this.lat,this.lng);}, 
		this.setAccuracy = function(accuracy) { this.accuracy = accuracy ;},
		this.getAccuracy = function(){	return this.accuracy ;}, 
		this.setActiveFlag = function(activeFlag) { this.activeFlag = activeFlag ;},
		this.getActiveFlag = function(){	return this.activeFlag ;}
	 }
	 
	/* INSURANCE ADDRESS CLASS ENDS */
	
	/* INSURANCE CORRESPONDENT CLASS STARTS */ 
	function InsuranceCorrespondent() {
	
		this.insuranceCorrespondentId = null,
		this.name = null,
		this.type = null, // RESIDENT or COMPETENT
		this.residentAddress = null,
		this.competentAddress = null,
		this.phone = null,
		this.fax = null,
		this.email = null,
		this.telex = null,
		this.activeFlag = null,
		this.distance = null,
		this.gMarker = null,
		this.peripheryCircle = null,
	   
		/** Function to init JS variables  **/
		this.init = function(insuranceCorrespondentId){
			this.insuranceCorrespondentId = insuranceCorrespondentId;
			return this ;
		},

		this.show = function(sourcePoint,searchType){
						
			this.gMarker = this.createMarker();
			map.addOverlay(this.gMarker);
			
			if(sourcePoint && sourcePoint != null) {
				this.peripheryCircle = this.createPeripheryCircle(sourcePoint,this.getCircleOptions(searchType));
				map.addOverlay(this.peripheryCircle);
			}
		},

		this.createMarker = function() {
			var starred = (this.distance <=500) ? true : false ; 
			
			var infoHtml = "<div id='correspondentDetails'>" 
						+ "<div class='correspondentDetailsLabel'> Insurance Correspondent Id :</div>" + this.insuranceCorrespondentId + "<br/>" 
						+ "<div class='correspondentDetailsLabel'> Name :</div>" + this.name + "<br/>"
						+ "<div class='correspondentDetailsLabel'> Type :</div>" + this.getType() + "<br/>"
						+ "<div class='correspondentDetailsLabel'> Resident Address :</div>" ;
						
				infoHtml += (this.residentAddress == null) ? "NA" : this.residentAddress.toHTML() ; 
				infoHtml = infoHtml + "<br/> <div class='correspondentDetailsLabel'> Competent Address :</div> " ;
				infoHtml += (this.competentAddress == null) ? "NA" : this.competentAddress.toHTML() ; 
				infoHtml = infoHtml + "<br/> <div class='correspondentDetailsLabel'> Phone :</div> " ; 
				infoHtml += (this.phone == null) ? "NA" : this.phone ;
				infoHtml = infoHtml + "<br/> <div class='correspondentDetailsLabel'> Fax :</div> " ; 
				infoHtml += (this.fax == null) ? "NA" : this.fax ;
				infoHtml = infoHtml + "<br/> <div class='correspondentDetailsLabel'> Email :</div> " ; 
				infoHtml += (this.email == null) ? "NA" : this.email ;
				infoHtml = infoHtml + "<br/> <div class='correspondentDetailsLabel'> Telex :</div> " ; 
				infoHtml += (this.telex == null) ? "NA" : this.telex ;
				infoHtml = infoHtml + "<br/> <div class='correspondentDetailsLabel'> Distance :</div> " + this.distance + "</div>" ;
			
			var gMarker = new GMarker(this.getLatLng(), createGIcon(this.insuranceCorrespondentId,starred,"INSURANCE_CORRESPONDENT_" + this.getType()));
			
			GEvent.addListener(gMarker, "click", function() { this.openInfoWindowHtml(infoHtml);});
			
			return gMarker;
		},
		
		this.createPeripheryCircle = function(centerPoint,circleOptions){
		    var zoom = map.getZoom();
		    var normalProj = G_NORMAL_MAP.getProjection();
		  	var center = normalProj.fromLatLngToPixel(centerPoint, zoom);
		  	var circlePoints = [] ;
		  	var radius = 0 ;
		  	
		    with (Math) {
			  	var tmpRadiusPt = normalProj.fromLatLngToPixel(this.getLatLng(), zoom);
	    		radius = floor(sqrt(pow((center.x-tmpRadiusPt.x),2) + pow((center.y-tmpRadiusPt.y),2)));
		        for (var a = 0 ; a < 361 ; a+=5 ) {
		        	var aRad = a*(PI/180);
		        	y = center.y + radius * sin(aRad)
		        	x = center.x + radius * cos(aRad)
		        	var p = new GPoint(x,y);
	        	    circlePoints.push(normalProj.fromPixelToLatLng(p, zoom));
			    }
		    }
		    return new GPolyline(circlePoints, circleOptions.color,circleOptions.weight,circleOptions.opacity);
			 
		},

		this.reset = function (){
			if(this.gMarker != null) { // Marker present remove it.
				map.removeOverlay(this.gMarker);
				this.gMarker = null ;
			}
			if(this.peripheryCircle != null) { // Periphery Circle present remove it.
				map.removeOverlay(this.peripheryCircle);
				this.peripheryCircle = null ;
			}
		},

		
		this.setName = function (name){	this.name = name ;},
		this.getName = function (){	return this.name ;},
		this.setActiveFlag = function(activeFlag) { this.activeFlag = activeFlag ;},
		this.getActiveFlag = function(){	return this.activeFlag ;}, 
		this.setDistance = function(distance) { this.distance = distance ;},
		this.getDistance = function(){return this.distance ;},
		this.setType = function(type) { this.type = type ;},
		this.getType = function(){return this.type ;},
		this.setResidentAddress = function(residentAddress) { this.residentAddress = residentAddress ;},
		this.getResidentAddress = function(){return this.residentAddress ;}, 
		this.setCompetentAddress = function(competentAddress) { this.competentAddress = competentAddress ;},
		this.getCompetentAddress = function(){return this.competentAddress ;},
		this.setPhone = function (phone){	this.phone = phone ;},
		this.getPhone = function (){	return this.phone ;},
		this.setFax = function (fax){	this.fax = fax ;},
		this.getFax = function (){	return this.fax ;},
		this.setEmail = function (email){	this.email = email ;},
		this.getEmail = function (){	return this.email ;},
		this.setTelex = function (telex){	this.telex = telex ;},
		this.getTelex = function (){	return this.telex ;},
		
		this.getLatLng = function(){ 
			if(ic.getType() == "COMPETENT")
				return this.competentAddress.getLatLng() ;
			else
				return this.residentAddress.getLatLng() ;
		},
		
		this.getCircleOptions = function(searchType) { 
		
			var circleOption = {
				color : "#000000", 
				weight : 1,
				opacity : .75
			} ;
		
			if("AUTOMATIC" == searchType) {
				circleOption.color = "#FF00FF" ;
				circleOption.weight = 2 ;
			}
			else if ("CITY" == searchType){
				circleOption.color = "#0000FF" ;
				circleOption.opacity = 1 ;
			}
			else
				circleOption.color = "#00FF00" ;
				
			if(this.getType() == "RESIDENT")
				circleOption.weight = circleOption.weight + 1 ;
			
			return circleOption ;	  
		}
			
	 }
	/* INSURANCE CORRESPONDENT CLASS ENDS */
	 
	/* INSURANCE CORRESPONDENT SEARCH CLASS STARTS */ 
	function InsuranceCorrespondentSearch() {
		
		/* The search parameters */
		this.searchType = null, // AUTOMATIC, CITY, USER_INPUT
		this.companyCode = null,
		this.isoCntryCode = null,
		this.addressToSearch = null,
		this.searchProximity = null,
		this.sourceLat = null,
		this.sourceLng = null,
		this.status = null,
	   
		/** Function to init JS variables  **/
		this.init = function(companyCode,searchProximity,searchType){
			this.companyCode = companyCode;
			this.searchProximity = searchProximity;
			this.searchType = searchType ;
			return this ;
		},

		this.search = function(address,isoCntryCode){
			this.resetSearch();
			if(typeof(address) === "string"){ // User Entered Address - Geocode and then do search
				fnOnGeoCodeCallBack = function (result){
					if (result.Status.code == G_GEO_SUCCESS) {
						var placemark = result.Placemark[0] ;
						var p = placemark.Point.coordinates ;
						var isoCntryCode = "" ;
						if(placemark.AddressDetails.Country) {
							isoCntryCode = placemark.AddressDetails.Country.CountryNameCode;
						} 
						this.doSearch(p[1],p[0],isoCntryCode);
					} else { // In case of error, display error message
						var reason="Unknown Error, Error Code : "+result.Status.code;
						if (reasons[result.Status.code]) {
							reason = reasons[result.Status.code]
						}
						alert('Could not find Address - ' + reason); // "'+result.name+ '"
					}
				};
				this.setAddressToSearch(address); 
				var method = fnOnGeoCodeCallBack ;
				var obj = this ;
				var hOGCB = method.bind(obj);
				geocoder.getLocations(address, function(result) {hOGCB(result)});
				//geocoder.getLatLng(address, function(data) {hOGCB(data)});
			}
			else if(typeof(address) === "object"){
				this.doSearch(address.lat(),address.lng(),isoCntryCode);
			}
		},

		this.resetSearch = function(){
			for (; IC_Search_Result.length > 0; ) {
				IC_Search_Result.shift().reset() ;
			}
			IC_Search_Result = [] ;
		},

		this.doSearch = function(sourceLat,sourceLng,isoCntryCode){
			this.sourceLat = sourceLat;
			this.sourceLng = sourceLng;
			this.isoCntryCode = isoCntryCode ;
			var queryString = "ACTION=SEARCH_INSURANCE_CORRESPONDENT"
							+ "&" + "searchType=" + this.searchType
							+ "&" + "companyCode=" + this.companyCode
							+ "&" + "isoCntryCode=" + this.isoCntryCode
							+ "&" + "lat=" + this.sourceLat
							+ "&" + "lng=" + this.sourceLng
							+ "&" + "dl=" + this.searchProximity ;

			var method = this.onSearchComplete ;
			var obj = this ;
			var hOSC = method.bind(obj);
			invokeAjaxRequest("InsuranceCorrespondentSearch.php","POST",queryString,function(data) {hOSC(data)});
		},

		this.onSearchComplete = function(data){

			var xml = GXml.parse(data);
			var insuranceCorrespondents = xml.documentElement.getElementsByTagName("insurancecorrespondent");
			var totalrecords = xml.documentElement.getElementsByTagName("totalrecords")[0].firstChild.nodeValue;
			if (insuranceCorrespondents.length == 0 || totalrecords == 0)
				alert('No Insurance Correspondents found.');
			else														
				for (var i = 0; i < insuranceCorrespondents.length; i++) {
					ic = this.createNewInsuranceCorrespondent(insuranceCorrespondents[i]);
					ic.show(this.getSourceLatLng(),this.searchType);
					IC_Search_Result[IC_Search_Result.length] = ic ;
				}
		},

		this.createNewInsuranceCorrespondent = function(insuranceCorrespondentData) {
			var ic = new InsuranceCorrespondent ;
			
			var getNode = function(xmlData,nodeName) {
				if(xmlData.getElementsByTagName(nodeName) != null)
					return xmlData.getElementsByTagName(nodeName)[0].firstChild ;
				return null ;
			} 
			
			var getNodeValue = function(xmlData,nodeName) {
				node = getNode(xmlData,nodeName);
				if(node != null) {
					//alert(nodeName + " - " + node.nodeValue);
					return node.nodeValue ;
					}
				return null;
			} 
			
			ic.init(getNodeValue(insuranceCorrespondentData,"insuranceCorrespondentId"));
			ic.setName(getNodeValue(insuranceCorrespondentData,"name"));
			ic.setType(getNodeValue(insuranceCorrespondentData,"correspondentType"));
			ic.setActiveFlag(getNodeValue(insuranceCorrespondentData,"activeFlag"));
			ic.setPhone(getNodeValue(insuranceCorrespondentData,"phone"));
			ic.setFax(getNodeValue(insuranceCorrespondentData,"fax"));
			ic.setEmail(getNodeValue(insuranceCorrespondentData,"email"));
			ic.setTelex(getNodeValue(insuranceCorrespondentData,"telex"));
			ic.setDistance(getNodeValue(insuranceCorrespondentData,"distance"));
			
			var setInsuranceAddress = function(insAddr,addrElem) {
				
				//ia.setId(getNodeValue(addressElem,"insuranceAddressId"));
				ia.setAddress(getNodeValue(addressElem,"address"));
				ia.setStreet(getNodeValue(addressElem,"street"));
				ia.setCity(getNodeValue(addressElem,"city"));
				ia.setRegion(getNodeValue(addressElem,"region"));
				ia.setState(getNodeValue(addressElem,"state"));
				ia.setCountry(getNodeValue(addressElem,"country"));
				ia.setPin(getNodeValue(addressElem,"pin"));
				ia.setIsoCntryCode(getNodeValue(addressElem,"isoCntryCode"));
				ia.setLat(getNodeValue(addressElem,"latitude"));
				ia.setLng(getNodeValue(addressElem,"longitude"));
				ia.setAccuracy(getNodeValue(addressElem,"accuracy"));
				ia.setActiveFlag(getNodeValue(addressElem,"activeFlag"));
				
				return ia ; 
			}
			
			addressElem = getNode(insuranceCorrespondentData,"residenceaddress") ; //insuranceCorrespondentData.getElementsByTagName("residenceaddress") ;
			if(addressElem != null) {
				ia = new InsuranceAddress ;
				ia.init(getNodeValue(addressElem,"insuranceAddressId"),ic.getType());
				ia = setInsuranceAddress(ia,addressElem);
				ic.setResidentAddress(ia) ;
			}
			if(ic.getType() == "COMPETENT") {
				addressElem = getNode(insuranceCorrespondentData,"competenceAddresses") ; //insuranceCorrespondentData.getElementsByTagName("competenceAddresses") ;
				if(addressElem != null) {
					ia = new InsuranceAddress ;
					ia.init(getNodeValue(addressElem,"insuranceAddressId"),ic.getType());
					ia = setInsuranceAddress(ia,addressElem);
					ic.setCompetentAddress(ia) ;
				}
			}
			
			return ic ;
		},
		
		this.setCompanyCode = function(companyCode) { this.companyCode = companyCode ;}, 
		this.getCompanyCode = function(){	return this.companyCode ;}, 
		this.setIsoCntryCode = function(isoCntryCode) { this.isoCntryCode = isoCntryCode ;}, 
		this.getIsoCntryCode = function(){	return this.isoCntryCode ;},
		this.setAddressToSearch = function(addressToSearch) { this.addressToSearch = addressToSearch ;},
		this.getAddressToSearch = function(){return this.addressToSearch ;}, 
		this.setSourceLat = function(sourceLat) { this.sourceLat = sourceLat ;},
		this.getSourceLat = function(){	return this.sourceLat ;}, 
		this.setSourceLng = function(sourceLng) { this.sourceLng = sourceLng ;},
		this.getSourceLng = function(){	return this.sourceLng ;}, 
		this.getSourceLatLng = function(){return new GLatLng(this.sourceLat,this.sourceLng);}, 
		this.setSearchProximity = function(searchProximity) { this.searchProximity = searchProximity ;},
		this.getSearchProximity = function(){return this.searchProximity ;}
	 }
	/* INSURANCE CORRESPONDENT SEARCH CLASS ENDS */
	 


	/* IN LIMIT LOCATOR  CLASS STARTS */ 
	function InLimitLocator() {
		
		/* The assesment parameters */
		this.limitLocations = null,
		this.locationToBeAssesed = null,
		this.inLimitElem = null,
		this.limit = null,
		this.shouldDraw = null,
		this.inLimitPoly = null,
		this.gMarker = null,
		this.drawOption = {
			color : "#000000", 
			weight : 1,
			opacity : .75
		},
		
	   
		/** Function to init JS variables  **/
		this.init = function(limitLocations,locationToBeAssesed,inLimitElem,limit,shouldDraw){
			this.limitLocations = limitLocations;
			this.locationToBeAssesed = locationToBeAssesed;
			if(typeof(inLimitElem) === "string")
				this.inLimitElem = document.getElementById(inLimitElem);
			else
				this.inLimitElem = inLimitElem ;
			this.limit = limit;
			this.shouldDraw = shouldDraw ;
			return this ;
		},

		this.process = function(){
			this.reset();
			if(!this.shouldDraw)
				return ;
				
			var markerIcon ; //= new GIcon(G_DEFAULT_ICON) ;
			var distance ;
			
			if(typeof(this.limitLocations) === "object" && this.limitLocations instanceof Array){ // User Entered An Array of limitLocations
			// Check if it is for Range Check or Bound Check by looping For each element 
				var points = [] ;
				var symmetricPoints = [] ;
				for(i = 0 ; i < this.limitLocations.length ; i++)
				{
					var limitLocationPoint ;
					if(this.limitLocations[i]  instanceof GLatLng) {
						limitLocationPoint = this.limitLocations[i]  ;
					}
					else if(this.limitLocations[i].type == "latitude"){ // @TODO - Check how would we make a bound  
						limitLocationPoint = new GLatLng(this.limitLocations[i].value,180);
						symmetricPoints[symmetricPoints.length] = new GLatLng(this.limitLocations[i].value,-180);// To make a polygon
					}
					else if(this.limitLocations[i].type == "longitude"){  // @TODO - Check how would we make a bound
						limitLocationPoint = new GLatLng(90,this.limitLocations[i].value);
						symmetricPoints[symmetricPoints.length] = new GLatLng(-90,this.limitLocations[i].value); // To make a polygon
					}
					points[points.length] = limitLocationPoint;
				}
				for(i = symmetricPoints.length -1 ; i >= 0 ; i--)
					points.push(symmetricPoints[i]); // Adding symmetric points of the polygon
				points[points.length] = points[0]; // Completing the polygon by adding start point at last - atleast one element should be there
					
			    this.inLimitPoly = new GPolygon(points, this.drawOption.color,this.drawOption.weight,this.drawOption.opacity,this.drawOption.color, this.drawOption.opacity / 2);
			    
			    if(this.inLimitPoly.getBounds().containsLatLng(this.locationToBeAssesed)) {
			    	this.inLimitElem.value = "true" ;
			    	markerIcon = createGIcon("Yes",true,"IL");
			    }
			    else {
			    	this.inLimitElem.value = "false" ;
			    	markerIcon = createGIcon("No",false,"IL");
			    } 
			}
			else { // Distance Check 
				this.inLimitPoly = this.createInLimitPolyCircle(this.limitLocations,this.limit,this.drawOption);
				distance = this.limitLocations.distanceFrom(this.locationToBeAssesed) ;
				if(distance <= this.limit * 1000) {
					this.inLimitElem.value = "true" ;
					// Draw an Icon with Tick
					//markerIcon.image = "http://localhost:8000/oDesk_2601744_Free_Insurance/styles/icon_tick.gif"
					markerIcon = createGIcon("Yes",true,"IL");
				}
				else {
					this.inLimitElem.value = "false" ;
					// Draw an Icon with Cross
					//markerIcon.image = "http://localhost:8000/oDesk_2601744_Free_Insurance/styles/icon_cross.gif"
					markerIcon = createGIcon("No",false,"IL");
				}
			}
			var markerText = this.inLimitElem.value + " - " + distance ;
			this.gMarker = new GMarker(this.locationToBeAssesed,markerIcon);
			GEvent.addListener(this.gMarker, "click", function() { this.openInfoWindowHtml(markerText);});
			map.addOverlay(this.inLimitPoly);
			map.addOverlay(this.gMarker);
			return this.inLimitElem.value ;
		},

		this.reset = function(){
			this.inLimitElem.value = "false" ;
			if(this.inLimitPoly != null) {
				map.removeOverlay(this.inLimitPoly);
				this.inLimitPoly = null ;
			}
			if(this.gMarker != null) {
				map.removeOverlay(this.gMarker);
				this.gMarker = null ;
			}
		},

		/*
		this.createInLimitPolyCircle = function(center,radius,circleOptions){
		
		    var zoom = map.getZoom();
		    var normalProj = G_NORMAL_MAP.getProjection();
		  	var centerPt = normalProj.fromLatLngToPixel(center, zoom);
		  	var points = [] ;
		    with (Math) {
		        for (var a = 0 ; a < 361 ; a+=5 ) {
		        	var aRad = a*(PI/180);
		        	y = centerPt.y + radius * sin(aRad)
		        	x = centerPt.x + radius * cos(aRad)
		        	var p = new GPoint(x,y);
	        	    points.push(normalProj.fromPixelToLatLng(p, zoom));
			    }
		    }
		    alert(center.distanceFrom(points[0]));
		    return new GPolyline(points, circleOptions.color,circleOptions.weight,circleOptions.opacity);
			 
		},

		this.createInLimitPolyCircle1 = function(center,radius,circleOptions){
		
		    /*var zoom = map.getZoom();
		    var normalProj = G_NORMAL_MAP.getProjection();
		  	var centerPt = normalProj.fromLatLngToPixel(center, zoom);* /
		  	var points = [] ;
		    with (Math) {
		    	alert('center.lng() - ' + center.lng() + ' center.lat() - ' + center.lat());
			  	b1 = center.lng() *(PI/180) * 6371 / (2 * PI)
			  	p1 = tan(center.lat() *(PI/180)) * b1;
			  	alert('b1 - ' + b1 + ' p1 - ' + p1); 
		        for (var a = 0 ; a < 361 ; a+=90 ) {
		        	var aRad = a*(PI/180);
		        	b2 = (b1 + radius * cos(aRad));
		        	p2 = (p1 + radius * sin(aRad)) ;
		        	lng = b2 * 360 / 6371 
		        	lat = atan(p2/b2) / (PI/180)
		        	//var p = new GPoint(x,y);
		        	alert('b2 - ' + b2 + ' p2 - ' + p2); 
				  	alert('lat - ' + lat + ' lng - ' + lng); 
	        	    points.push(new GLatLng(lat,lng));
			    }
		    }
		    alert(center.distanceFrom(points[0]));
		    return new GPolyline(points, circleOptions.color,circleOptions.weight,circleOptions.opacity);
			 
		},
		
		this.createInLimitPolyCircle2 = function(center,radius,circleOptions){
		
		    /*var zoom = map.getZoom();
		    var normalProj = G_NORMAL_MAP.getProjection();
		  	var centerPt = normalProj.fromLatLngToPixel(center, zoom);* /
		  	var points = [] ;
		    with (Math) {
		    	alert('center.lng() - ' + center.lng() + ' center.lat() - ' + center.lat());
			  	b1 = center.lng() * 6371 / (360)
			  	p1 = tan(center.lat() *(PI/180)) * b1;
			  	alert('b1 - ' + b1 + ' p1 - ' + p1); 
		        for (var a = 0 ; a < 361 ; a+=90 ) {
		        	var aRad = a*(PI/180);
		        	bx = (radius * cos(aRad));
		        	px = (radius * sin(aRad)) ;
		        	b2 = b1 + bx ;
		        	p2 = p1 + px ;
		        	lng = b2 * 360 / 6371 
		        	lat = atan(p2/b2) / (PI/180)
		        	//var p = new GPoint(x,y);
		        	alert('b2 - ' + b2 + ' p2 - ' + p2); 
				  	alert('lat - ' + lat + ' lng - ' + lng); 
	        	    points.push(new GLatLng(lat,lng));
			    }
		    }
		    alert(center.distanceFrom(points[0]));
		    return new GPolyline(points, circleOptions.color,circleOptions.weight,circleOptions.opacity);
			 
		},
		
		*/
		this.createInLimitPolyCircle = function(center,radius,circleOptions){
		
		    /*var zoom = map.getZoom();
		    var normalProj = G_NORMAL_MAP.getProjection();
		  	var centerPt = normalProj.fromLatLngToPixel(center, zoom);*/
		  	var points = [] ;
			with (Math) {
				var radiusRadian = radius/6378.8;	// radians
				var lat1 = center.lat() * (PI/180); // radians
				var lng1 = center.lng() * (PI/180); // radians
		
				for (var a = 0 ; a < 361 ; a++ ) {
					var aRad = a*(PI/180);
					var latRadian = asin(sin(lat1)*cos(radiusRadian)+cos(lat1)*sin(radiusRadian)*cos(aRad));
					var dlng = atan2(sin(aRad)*sin(radiusRadian)*cos(lat1),cos(radiusRadian)-sin(lat1)*sin(latRadian));
					var lngRadian = ((lng1-dlng+PI) % (2*PI)) - PI ; // MOD function
					var point = new GLatLng(parseFloat(latRadian*(180/PI)),parseFloat(lngRadian*(180/PI)));
					points.push(point);
				}
		
			}
//		    return new GPolyline(points, circleOptions.color,circleOptions.weight,circleOptions.opacity);
		    return new GPolygon(points, circleOptions.color,circleOptions.weight,circleOptions.opacity,circleOptions.color, circleOptions.opacity / 2);
			 
		},
		
		this.setLimitLocations = function(limitLocations) { this.limitLocations = limitLocations ;}, 
		this.getLimitLocations = function(){	return this.limitLocations ;}, 
		this.setLocationToBeAssesed = function(locationToBeAssesed) { this.locationToBeAssesed = locationToBeAssesed ;}, 
		this.getLocationToBeAssesed = function(){	return this.locationToBeAssesed ;},
		this.setInLimitElem = function(inLimitElem) { this.inLimitElem = inLimitElem ;},
		this.getInLimitElem = function(){return this.inLimitElem ;}, 
		this.setLimit = function(limit) { this.limit = limit ;},
		this.getLimit = function(){	return this.limit ;}, 
		this.setShouldDraw = function(shouldDraw) { this.shouldDraw = shouldDraw ;},
		this.getShouldDraw = function(){	return this.shouldDraw ;} 
	 }
	/* IN LIMIT LOCATOR CLASS ENDS */






	
	/* GLOBAL OBJECTS AND FUNCTIONS */ 
	 
	var map = null;
	var geocoder = null;
	var reasons=[];			
	var cityGeocodingStatusIconHtml=[];
	var cityMap = new HashMap();
	var routeMap = new HashMap();
	var cityLocations = new HashMap();
	var iLLMap = new HashMap();
	
	var cityNormalZoomLevel = 7 ;
	var cityFocusedZoomLevel = 11
	var animateZoomAndPanTimer = null ;
	
	//var overlayCounter = 0;
	var IC_Search_Result = [];
	
	GEOCODING_TYPE_NORMAL = 0 ;
	GEOCODING_TYPE_REVERSE = 1 ;
	
	STATUS_NONE = 0 ;
	STATUS_IN_PROCESS = 1 ;
	STATUS_INPUT_OK = 2 ;
	STATUS_INPUT_INCOMPLETE = 3 ;
	STATUS_INPUT_ERRORED = 4 ;
	
				
	cityGeocodingStatusIconHtml[STATUS_NONE] = "" ;
	cityGeocodingStatusIconHtml[STATUS_IN_PROCESS] = '<img src="styles/waiting.gif" />' ;
	cityGeocodingStatusIconHtml[STATUS_INPUT_OK] =  '<img src="styles/icon_tick.gif" />' ;
	cityGeocodingStatusIconHtml[STATUS_INPUT_INCOMPLETE] =  '<img src="styles/down_arrow_yellow.gif" />' ;
	cityGeocodingStatusIconHtml[STATUS_INPUT_ERRORED] = '<img src="styles/icon_cross.gif" />'
	
	/* Function to initialize Geocoder*/
	function initGeoCoder() {
		sourceCity = new city().init("sourceCity","SC");
		embankmentPort = new city().init("embankmentPort","EP");
		unloadingPort = new city().init("unloadingPort","UP");
		endDestination = new city().init("endDestination","ED");
		
		cityMap.put("sourceCity",sourceCity);
		cityMap.put("embankmentPort",embankmentPort);
		cityMap.put("unloadingPort",unloadingPort);
		cityMap.put("endDestination",endDestination);

		sc_ep_route = new route().init("route_SC_EP", sourceCity,embankmentPort);
		ep_up_route = new route().init("route_EP_UP", embankmentPort,unloadingPort);
		up_ed_route = new route().init("route_UP_ED", unloadingPort,endDestination);
		
		routeMap.put("route_SC_EP",sc_ep_route);
		routeMap.put("route_EP_UP",ep_up_route);
		routeMap.put("route_UP_ED",up_ed_route);
		
		unloadingPort.addGeocodingCompleteHandler(fnOnunloadingPortGeocodingComplete.bind(unloadingPort));
		fnInLimitLocatorAutoTest(unloadingPort,endDestination,"route_UP_ED_inLimit",200,true);
		
		document.getElementById('resetZoom').onclick = fnAdjustMapAndResetZoom ;
		document.getElementById('nearbyIC').onclick = fnOnNearbyICClick ;
		
		document.getElementById('iLLTest').onclick = fnInLimitLocatorTest ;
		document.getElementById('iLLReset').onclick = fnInLimitLocatorReset ;
		
		if (GBrowserIsCompatible()) {
		   	map = new GMap2(document.getElementById("map"));
		    map.setCenter(new GLatLng( 45.54, 10.225), 4);
		    map.addControl(new GSmallMapControl());
		    map.addControl(new GMenuMapTypeControl());
//map.addMapType(G_SATELLITE_3D_MAP);
		    
			geocoder = new GClientGeocoder();
		    
			// ====== Array for decoding the failure codes ======
			reasons[G_GEO_SUCCESS]            = "Success";
			reasons[G_GEO_MISSING_ADDRESS]    = "Missing Address: The address was either missing or had no value.";
			reasons[G_GEO_UNKNOWN_ADDRESS]    = "Unknown Address:  No corresponding geographic location could be found for the specified address.";
			reasons[G_GEO_UNAVAILABLE_ADDRESS]= "Unavailable Address:  The geocode for the given address cannot be returned due to legal or contractual reasons.";
			reasons[G_GEO_BAD_KEY]            = "Bad Key: The API key is either invalid or does not match the domain for which it was given";
			reasons[G_GEO_TOO_MANY_QUERIES]   = "Too Many Queries: The daily geocoding quota for this site has been exceeded.";
			reasons[G_GEO_SERVER_ERROR]       = "Server error: The geocoding request could not be successfully processed.";
			reasons[G_GEO_MISSING_QUERY]	  = "Missing Query: The query parameter was either missing or had no value.";
			reasons[G_GEO_BAD_REQUEST]		  = "Bad Request: The directions request could not be successfully parsed." ;
			
			GEvent.addListener(map, "zoomend", fnRefreshRoutes);
			//GEvent.addListener(map, "addoverlay", function() {overlayCounter++; alert("overlay couter :" + overlayCounter);});
			//GEvent.addListener(map, "removeoverlay", function() {overlayCounter--; alert("overlay couter :" + overlayCounter);});
			
		}
	}
	
	/* Function for showing location given the placemark retuned by Google */
	function fnSetAndShowLocation(cityId,placemarkNumber,geoCodingType) {
		cityMap.get(cityId).setAndShowLocation(placemarkNumber,geoCodingType);
	}
	
	/* Function for changing city icon */
	function fnChangeCityIcon(cityId,iconUrl) {
		cityMap.get(cityId).setMarkerIcon(iconUrl);
	}
	
	/* Function for fnResetZoom button */
	function fnAdjustMapAndResetZoom(currentLocation) {
	/** Logic for Centering and Zooming - 
			Clear any previous timers
			Check all the locations which are present.
			Start with 1st or the current one and zoom in and include that in map
			For rest of all, keep including them by increasing the map bound.
			Get the final bound and zoom level
			Animate to zoom out and panTo once animation is over
	**/
		
		if(animateZoomAndPanTimer != null) {
			clearTimeout(animateZoomAndPanTimer);
			animateZoomAndPanTimer = null; 
		}
			
		var locations = cityLocations.getValues();
		if(!locations.length || locations.length <= 0)
			return ;

		
		var mapBounds ;
		var i = 0;
		var zoomOutAnimationDelay = 1000 ;
		 
		if(currentLocation && currentLocation instanceof GLatLng) {// Focusing on some city
			map.setCenter(currentLocation);
			map.setZoom(cityFocusedZoomLevel);
		}
		else {// General Adjusting Map and ZoomLevel
			map.setCenter(locations[i],cityNormalZoomLevel); 
			i = 1 ;
		}
			
		mapBounds = map.getBounds() ;
		
		for(; i < locations.length ; i++) {
			mapBounds.extend(locations[i]);
		}
		finalZoom = map.getBoundsZoomLevel(mapBounds) ;
		if(finalZoom > cityNormalZoomLevel)
			finalZoom = cityNormalZoomLevel ;
			
		//alert(finalZoom + " | " + map.getZoom());

		//zoomDiff = map.getZoom() - finalZoom ;
		
		if(currentLocation && currentLocation instanceof GLatLng) { // Focusing on some city - Use Animation
			fnAnimateZoomAndPan = function() {
					
					if(map.getZoom() > finalZoom) {
						map.zoomOut() ;
						//setTimeout(fnAnimateZoomAndPan,zoomOutAnimationDelay * 4 / zoomDiff);
						animateZoomAndPanTimer = setTimeout(fnAnimateZoomAndPan,zoomOutAnimationDelay);
					}
					else {
						map.panTo(mapBounds.getCenter());
					}
				}
			
			animateZoomAndPanTimer = setTimeout(fnAnimateZoomAndPan,zoomOutAnimationDelay);
		}
		else { // General Adjusting Map and ZoomLevel
			map.setZoom(finalZoom);
			map.setCenter(mapBounds.getCenter());
		}
		
	}
	
	/* Function for refreshin routes*/
	function fnRefreshRoutes() {
		routeMap.get("route_SC_EP").updateRoute();
		routeMap.get("route_EP_UP").updateRoute();
		routeMap.get("route_UP_ED").updateRoute();
	}
	
	/* Function for setting route insurance */
	function fnRouteInsurance(routeId,insure) {
		routeMap.get(routeId).setRouteInsurance(insure);
	}
	
	/* Function for changing city icon */
	function fnSearchInsuranceCorrespondent(searchType, address,isoCntryCode) {
		searchProximity = 50000 ; // TODO - Change the searchProximity
		var ics = new InsuranceCorrespondentSearch().init("",searchProximity,searchType); 
		ics.search(address,isoCntryCode);
	}
	
	/* Function for OnClick of NearbyIC Button*/
	function fnOnNearbyICClick() {
		address = document.getElementById('nearbyICAddress').value;
		if(trim(address) != '')
			fnSearchInsuranceCorrespondent("USER_INPUT",address)
		else
			alert('Please enter the address');
	}
	
	/* Function for Automatic Correspondent Search */
	function fnOnunloadingPortGeocodingComplete() {
		if(this.isCityGeocoded())
			fnSearchInsuranceCorrespondent("AUTOMATIC",this.getLatLng(),this.getGNormalizedCountryCode());
	}
	
	function fnInLimitLocatorAutoTest(limitLocations,locationToBeAssesed,inLimitElem,limit,shouldDraw) {
		var iLL = new InLimitLocator().init(null,null,inLimitElem,limit,shouldDraw);
		limitLocations.addGeocodingCompleteHandler(function(){
			if(limitLocations.isCityGeocoded()) {
				iLL.setLimitLocations(limitLocations.getLatLng());
				iLL.process();
			}
			else
				iLL.reset();
			});
		locationToBeAssesed.addGeocodingCompleteHandler(function(){
			if(locationToBeAssesed.isCityGeocoded()) {
				iLL.setLocationToBeAssesed(locationToBeAssesed.getLatLng());
				iLL.process();
			}
			else
				iLL.reset();
			});
	}
		
	function fnInLimitLocatorTest() {
	
		var limitLocationPoints = []
		
		limitLocationPoints[0] = {type:"longitude",value:-10 } ;
		limitLocationPoints[1] = {type:"longitude",value:20 } ;
		
		var iLL = iLLMap.get("iLL");
		
		if(iLL != null)
			fnInLimitLocatorReset();
	
//		var iLL = new InLimitLocator().init(cityMap.get("unloadingPort").getLatLng(),cityMap.get("endDestination").getLatLng(),"route_UP_ED_inLimit",200,true);
		iLL = new InLimitLocator().init(limitLocationPoints,cityMap.get("endDestination").getLatLng(),"route_UP_ED_inLimit",200,true);
		if(iLL.process() === "true")
			alert("location within limit");
		else
			alert("location outside limit");
		iLLMap.put("iLL",iLL);
	}
	
	function fnInLimitLocatorReset() {
		iLL = iLLMap.get("iLL");
		if(iLL != null)
			iLL.reset();
		iLLMap.remove("iLL");
	}
	
	
	/* GLOBAL OBJECTS AND FUNCTIONS ENDS */ 

	/* GLOBAL UITLITIY FUNCTIONS STARTS */ 
		
	/* Function to create Google Map Icon using Google mapiconmaker */	
	function createGIcon(strLabel,starred,type)
	{
		if(type == "CITY")
			return MapIconMaker.createFlatIcon({addStar: starred, label: strLabel, primaryColor: "#ff0000", labelColor: "#000000"});
		else if(type == "INSURANCE_CORRESPONDENT_RESIDENT")
			return MapIconMaker.createLabeledMarkerIcon({addStar: starred, label: strLabel, primaryColor: "#ff7777", labelColor: "#000000"});
		else if(type == "INSURANCE_CORRESPONDENT_COMPETENT")
			return MapIconMaker.createLabeledMarkerIcon({addStar: starred, label: strLabel, primaryColor: "#ffff77", labelColor: "#000000"});
		else if(type == "IL")
			return MapIconMaker.createLabeledMarkerIcon({addStar: starred, label: strLabel, primaryColor: "#ffff00", labelColor: "#000000"});
	}
	
	// @1.2 Change
	/* Function - An interface to GMap resize to check whether Map needs Resize or Not and resizes accordingly */	
	function checkMapResize() {
		if(map != null)
			map.checkResize() ;
	}
	
	/* Function to expanding / collapsing a div. If collapseOrExpand is specified, it will set accordingly else will toggle the div */	
	function fnCollapseOrExpand(divToCollapse,collapseOrExpand) {
		
		if(typeof(divToCollapse) === "object")
			theDiv = divToCollapse ;
		else
			theDiv = document.getElementById(divToCollapse);
			
		if(collapseOrExpand)
			theDiv.className = collapseOrExpand;
		else if(theDiv.className == 'collapsed') { // Expand the Div
			theDiv.className  = 'expanded';
		}
		else { // Collapse the Div
			theDiv.className  = 'collapsed';
		}
	}
	
	/* Function to avoid submit in some browser when enter is pressed in YUI autocomplete */
	function processEnter(e)
	{
		var keycode;
		if (window.event) keycode = window.event.keyCode;
		else if (e) keycode = e.which;
		else return true;
		
		if (keycode == 13)
			return false;
		else
			return true;
	}

	/* Function to Invoke an Ajax Request to Server */
	function invokeAjaxRequest(url,method,queryString,callback) {
		var xmlHttpRequest = GetXmlHttpObject();
	    if (xmlHttpRequest == null) {
	        alert("Browser does not support HTTP Request");
	        return;
	    }
	    
		xmlHttpRequest.onreadystatechange = fnOnReadyStateChange ;
		xmlHttpRequest.open(method, url, true);
	    xmlHttpRequest.setRequestHeader('Content-Type','application/x-www-form-urlencoded') ;
		xmlHttpRequest.send(queryString);
		
		function fnOnReadyStateChange(){
			if (xmlHttpRequest.readyState == 4) {
				if (xmlHttpRequest.status == 200)
					callback(xmlHttpRequest.responseText);		      	 
				else {
					alert("Error In Processing Ajax Request");
				}
			}
		}
		
		function GetXmlHttpObject() {
		    var objXMLHttp = null;
		    if (window.XMLHttpRequest) {
		        objXMLHttp = new XMLHttpRequest();
		    } else {
		        if (window.ActiveXObject) {
		            objXMLHttp = new ActiveXObject("Microsoft.XMLHTTP");
		        }
		    }
		    return objXMLHttp;
		}
		
	}
	
	/* Function to trim a string */
	function trim(str) {
		return str.replace(/^\s+|\s+$/g,"");
	} 

	/* Function to Bind a method with correct scope - Refer http://www.digital-web.com/articles/scope_in_javascript/ */
	Function.prototype.bind = function(obj) {
		var method = this,
		temp = function() {
			return method.apply(obj, arguments);
		};
		return temp;
	} 
	/* GLOBAL UITLITIY FUNCTIONS ENDS*/
