/*
  OpenLayers.js -- OpenLayers Map Viewer Library

  Copyright 2005-2007 MetaCarta, Inc., released under the BSD license.
  Please see http://svn.openlayers.org/trunk/openlayers/release-license.txt
  for the full text of the license.

  Includes compressed code under the following licenses:

  (For uncompressed versions of the code used please see the
  OpenLayers SVN repository: <http://openlayers.org/>)

*/

/* Contains portions of Prototype.js:
 *
 * Prototype JavaScript framework, version 1.4.0
 *  (c) 2005 Sam Stephenson <sam@conio.net>
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://prototype.conio.net/
 *
/*--------------------------------------------------------------------------*/

/**  
 *  
 *  Contains portions of Rico <http://openrico.org/>
 * 
 *  Copyright 2005 Sabre Airline Solutions  
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License"); you
 *  may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *  
 *         http://www.apache.org/licenses/LICENSE-2.0  
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 *  implied. See the License for the specific language governing
 *  permissions and limitations under the License. 
 *
**/

/*
 *---------------------------------------------------------------------------------
 * Copyright (C) 2007 - Regione Autonoma della Sardegna <www.regione.sardegna.it>
 * Tutti i diritti riservati.
 *
 * Autore - CORE Soluzioni Informatiche srl Bologna <www.corenet.it>
 *
 * Questo programma e' stato sviluppato nell'ambito del progetto RAS SIT2COM
 *----------------------------------------------------------------------------------
**/

/**
 * @requires OpenLayers/Ajax.js
 * 
 * Class: Roja.Address
 * Allows to search an address in the Sardinia Region. 
 * 
 * The address in shown on the map and, given the id of a div, on a div element inside the html page.
 * 
 * The object make use of an OGC standard Open Location Service to geocode the address.
 * 
 * The div that shows the address is created by an xsl transformation with the xml response of the service.
 *
 */
NONE = 0;                //Initial status
ADDRESS_LOADED = 1;      //Address have been loaded
UNKNOWN_ADDRESS = 11;    //Address cannot be found
MULTI_ADDRESS = 12;      //There is a multi addressess result
SERVER_ERROR = 13;       //Server error

 
Roja.Address = OpenLayers.Class({

    /**
    * @private 
    * Property: id
    * {String}
    */
    id: null,

    /**
    * Property: map
    * {OpenLayers.Map} an instance of an OpenLayers.Map object
    */
    map: null,

    /**
    * @private 
    * Property: addresspanel
    * {Object} addresspanel component
    */
    addresspanel: null,

    /**
    * @private 
    * Property: div
    * {DOMElement}
    */
    div: null,
    
    /**
    * Property: displayClass
    * {String} This property is used to assign a CSS to the Control.
    */
    displayClass: "",

    /**
    * Property: geocodeUrl
    * {String} Uri of the Open Location Service that geocodes the address
    */
    geocodeUrl: null,

    /**
    *
    * Property: geocodePars
    * {String} Http params of the Open Location Service geocode service
    */
    geocodePars: 'serviceName=locationservice&exactStreetName=true&xmlRequest=',

    /**
    * Property: xslPath
    * {String} path of the xsl file tha
    */
    xslPath: '../resources/xsl/buildAddressingResponsePanel.xsl',

    /**
    * @private
    * Property: xsl
    * {DOMElement} XmlDocument containing the xsl
    */
    xsl: null,

    /**
    * @private
    * Property: errorGeocodeMessage
    * {String} String that contains geocode error message
    */
    errorGeocodeMessage: 'error on geocode service',

    /**
    * @private
    * Property: errorXSLLoadingMessage
    * {String} String that contains xsl loading error message
    */
    errorXSLLoadingMessage: 'file xsl not loaded',

    
    /**
    * @private 
    * Property: geocodedAddr
    * {Array(OpenLayers.Geocode.GeocodedAddress)}
    */
    geocodedAddr: null,

    

    /**
    * @private 
    * Property: gmlns
    * {String}
    */
    gmlns: "http://www.opengis.net/gml",

    /**
    * @private 
    * Property: xls
    * {String}
    */
    xls: "http://www.opengis.net/xls",

    
    /**
    * @private 
    * Property: markersLayer
    * {} Geocoded Addresses Layer
    */
    markersLayer: null,

    /**
    * @private 
    * Property: events
    * {OpenLayers.Events}
    */
    events: null,

    /**
    * @private 
    * Property: loading
    * {OpenLayers.Control.LoadingMessage}
    */
    loading: null,

    /**
    * @private 
    * Property: EVENT_TYPES
    * {Array}
    */
    EVENT_TYPES: ["loadComplete","loadFailure","drawComplete","drawFailure"],

    /**
     * @private
    * Property: fallThrough
    * {boolean}
    * Should Address allow events on the map to fall through to other
    * elements on the page, or should it swallow them? (#457)
    * Default is to swallow them.
    */
    fallThrough: false,

    /**
    * @private 
    * Property: status_code
    * {String}
    */
    status_code: null,
    
    /**
     *@public
     * Property: xmlResponse
     * {String}
     * XML response geocode service
     */
    xmlResponseService: null,

    /**
    * Property: iconUrl
    * {String} The relative path of icon image. This path is added to constant OpenLayers.ImgPath
    */
    iconUrl:  "signal_shadow.png",

    /**
    * Property: iconSize
    * {OpenLayers.Size} size icon associated to markers
    */
    iconSize: new OpenLayers.Size(68, 48),

    /**
    * Constructor: Roja.Address
    * Constructor for a new <Roja.Address> instance.
    *
    * Parameters:
    * options - {Object} layer that the tile will go in.
    */
    initialize: function (options) {
        // We do this before the extend so that instances can override
        // className in options.
        this.displayClass = this.CLASS_NAME.replace("Roja.", "Roja").replace(".",""); 
        // OpenLayers class: Copy all properties of a source object to a destination object.  Modifies the passed in destination object.
        OpenLayers.Util.extend(this, options);                                               
        //use of Util class:A unique id string, built on the passed in prefix
        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");                    
        
        
        //define marker layer
        this.markersLayer = new Roja.Layer.Markers( "GeocodedAddresses" );        
        
        //define events class
        this.events = new OpenLayers.Events(this, this, this.EVENT_TYPES, this.fallThrough); 
        //regiter event, functions will be run on relative trigger 
        this.events.register("loadComplete", this, this.stopLoading);                     
        this.events.register("loadFailure", this, this.stopLoading);                
        
        // to wiev loading state
        this.loading = new Roja.Control.LoadingMessage();
        if ((this.map != null) && (this.loading != null))
        {
            this.map.addControl(this.loading);        
        }

        var successXslFunc = this.successXsl.bind(this);
        var faliureXslFunc = this.failureXsl.bind(this);

        // loading xsl file
        new OpenLayers.Ajax.Request(this.xslPath,  
                   {   method: 'get',
                       parameters: null,
                       onComplete: successXslFunc,
                       onFailure: faliureXslFunc
                    }
                   );
    },

    /**
    * @private 
    * Method: stopLoading
    * stop loanding bar layer
    * Parameters:
    * 
    */

    stopLoading: function () {
        if (this.map != null && this.loading != null)
        {
            this.loading.stopLoad();
        }
    },

    /**
    * @private 
    * Method: destroy
    * Eliminate circular references
    */
    destroy: function () {

        if (this.map != null) {
            
            if (this.markersLayer != null)
            {
                this.map.removeLayer(this.markersLayer, true);
                this.markersLayer = null;
            }
        }
        this.map = null;
        this.div = null;
        this.xsl = null;
        
        
        if (this.events) {
            this.events.destroy();
        }

        this.events = null;
                       
    },

    /**
    * Method: load
    * This method launch a geocode search and popolate object itself with results
    * 
    * Parameters:
    * args - {} address
    * address to geocode
    * 
    */
    load: function (address) {
    	this.reset();
    	this.geocodedAddr=null;
        if(address != null && address.length == 0){
            alert('Attenzione, inserire un indirizzo per la ricerca.');
            return;
        }
        if (this.map != null)
        {
            this.loading.startLoad();
            if (this.map.getLayer(this.markersLayer.id)==null)
            {
                this.map.addLayer(this.markersLayer);
            }
        }
            

        var successAddressFunc = this.successAddress.bind(this);
        var failureAddressFunc = this.failureAddress.bind(this);

        // loading xml request
        var xmlRequest = this.createFreeFormAddressRequest(address);
        var par = this.geocodePars+xmlRequest;
        this.status_code = NONE;
        
        var uri = this.geocodeUrl;
        if (Roja.Proxy && this.geocodeUrl.startsWith("http")) {
             uri = Roja.Proxy + Roja.Util.escapeUri(this.geocodeUrl);
        }
        
        new OpenLayers.Ajax.Request(uri+par,
                         {   method: 'get',
                             parameters: null,
                             onComplete: successAddressFunc,
                             onFailure: failureAddressFunc
                          }
                         );

    },
    
    /**
    * @private 
    * Method: successXsl
    *
    * Parameters:
    * request - {}
    */
    successXsl: function (request) {
        this.xsl = request.responseXML;
    },
    
   

    /**
    * @private 
    * Method: failureXsl
    *
    * Parameters:
    * request - {}
    */
    failureXsl: function (request) {
        alert(this.errorXSLLoadingMessage);
    },

    /**
    * Method: setAddressPanel
    * Set the geocode panel element
    *
    * Parameters:
    * addressPanel - {DOMElement}
    */
    setAddressPanel: function (addressPanel) {
        this.addresspanel = addressPanel;
    },

    /**
    * @private 
    * Method: successAddress
    *
    * Parameters:
    * request - {}
    */
    successAddress: function (request) { 
        if (request.responseXML == null){
            return;
        }
        var xmlResponse = this.xmlResponseService;
        if (!xmlResponse || request.fileType!="XML") { //for IE compatibility
            xmlResponse = OpenLayers.parseXMLString(request.responseText);
        }
        this.xmlResponseService = xmlResponse;
        if (this.map!=null)
        {
            this.drawOnMap(xmlResponse);
        }
        var xml = request.responseText;
        var html = Roja.transformXml(xml,this.xsl);
        html=html.replace(/&lt;/g , "<");                 
        html=html.replace(/&gt;/g , ">");
        if (this.addresspanel != null)
        {
            document.getElementById(this.addresspanel).innerHTML = html;
        }

        this.loadData(xmlResponse);//this insert marker
    },

    /**
    * @private 
    * Method: loadData
    *
    * Parameters:
    * xmlResponse - {DOMElement} response xml
    */
    loadData: function (xmlResponse) {
        //Added now and on load because failure ajax request existed
        this.status_code = NONE; 
        // parses xml response and stores geocoded addresses
        this.loadGeocodes(xmlResponse);//this insert marker e save geocod addresses
        
        
        //OpenLayers.Console.debug(this.status_code);
        if (this.status_code > 10)
        {
            this.events.triggerEvent("loadFailure");
        }
        else
        {
            this.events.triggerEvent("loadComplete");
        }
    },

    /**
    * Method: getStatusCode
    * Get status code of routing response. 
    * Possible values are:
    * NONE = 0;                 Initial status
    * ADDRESS_LOADED = 1;        Address have been loaded
    * UNKNOWN_ADDRESS = 11;        Address cannot be found
    * MULTI_ADDRESS = 12;        There is a multi addressess result
    * SERVER_ERROR = 13;        Server error
    * RESPONSE_ERROR = 14;        Response is not correctly extracted    
    * 
    * Returns:
    * {int} status code
    */
    getStatusCode: function () {
        return this.status_code;
    },

    /**
    * @private 
    * Method: failureAddress
    *
    * Parameters:
    * request - {}
    */
    failureAddress: function (request    ) {
        alert(this.errorGeocodeMessage);
        this.status_code = SERVER_ERROR;
        return;
    },

    /**
    * Method: geocodedMarkersClear
    * Remove geocoded markers
    */
    geocodedMarkersClear: function () {
        if (this.markersLayer.markers != null) {
            while(this.markersLayer.markers.length > 0) {
                var marker = this.markersLayer.markers[0];
                this.markersLayer.removeMarker(marker);
                marker.destroy();
            }
        }
    },

    /**
    * @private 
    * Method: loadGeocodes
    *
    * Parameters:
    * xmlResponse - {}
    */
    loadGeocodes: function(xmlResponse) {
        var geocodeAddressElement = xmlResponse.getElementsByTagName('GeocodedAddress');
        var geocodedAddress = xmlResponse.getElementsByTagName('GeocodedAddress')[0];
        var freeFormAddressElement = geocodedAddress.getElementsByTagName('freeFormAddress');
    
        if ((freeFormAddressElement.length > 0) || (geocodeAddressElement.length > 1))
        {    
            //addres in not univoc or errore response from service
            this.events.triggerEvent("drawFailure"); 
            if(freeFormAddressElement.length > 0){
                this.status_code = UNKNOWN_ADDRESS;
            }
            else{
                this.status_code = MULTI_ADDRESS;
            }
            return;
        }
        if (xmlResponse.getElementsByTagName('StreetAddress').length>0) {
            this.status_code = ADDRESS_LOADED;
            var geocodedAddress = xmlResponse.getElementsByTagName('GeocodedAddress')[0];
            var gmlpos = OpenLayers.Ajax.getElementsByTagNameNS(geocodedAddress, this.gmlns, "gml", "pos");
            var pos = gmlpos[0].firstChild.nodeValue;
            var x = pos.substring(0,pos.indexOf(' '));
            var y = pos.substring(pos.indexOf(' '));
            this.geocodedMarkersClear();
            // add marker's icon
            var options = { lonlat: new OpenLayers.LonLat(x,y),
                    iconUrl: OpenLayers.ImgPath + this.iconUrl,
                    iconSize: this.iconSize,
                    title: 'Indirizzo'
                  };
            this.geocodedAddr = new Roja.Marker.GeocodedAddress(geocodedAddress,options);
            var marker = new Roja.Marker(options);
            if (this.map != null)
            {
                this.markersLayer.addMarker(marker);
                var text = '<font style="font-family:Verdana;font-size:9px;color:black;">Indirizzo</font><br/><font' +
                        ' style="font-family:Verdana;font-size:9px;color:black;font-weight:bold;">' + this.geocodedAddr.description +
               '</font>';
                marker.createPopup(text,true);
            }
        }
        else
        {
            /*var geocodeResponse = xmlResponse.getElementsByTagName('GeocodeResponse')[0];
            
            //Set status code error
            var freeformaddress = OpenLayers.Ajax.getElementsByTagNameNS(geocodeResponse, this.xls, "xls", "freeFormAddress");
            //If there are free form address, the address doesnt found
            if (freeformaddress.length > 0)
            {
                this.status_code = UNKNOWN_ADDRESS;
            }
            else
            {
                var geocodeResponseList = OpenLayers.Ajax.getElementsByTagNameNS(geocodeResponse, this.xls, "xls", "GeocodeResponseList");
                var i;
                for (i=0; i<geocodeResponseList.length; i++)
                {
                    var geocodeResponse = geocodeResponseList[i];
                    var numberOfGeocodedAddresses = geocodeResponse.getAttribute('numberOfGeocodedAddresses');
                    if (numberOfGeocodedAddresses > 1)
                    {
                        this.status_code = MULTI_ADDRESS;
                        //Used to step out if error has been found
                        i=geocodeResponseList.length;
                    }
                }
            }*/
        }
    },



    

    /**
    * Method: drawOnMap
    * Draw geometry on map
    *
    * Parameters:
    * xmlResponse - {DOMElement} 
    */
    drawOnMap: function(xmlResponse) {

        var geocodeAddressElement = xmlResponse.getElementsByTagName('GeocodedAddress');
        var geocodedAddress = xmlResponse.getElementsByTagName('GeocodedAddress')[0];
        var freeFormAddressElement = geocodedAddress.getElementsByTagName('freeFormAddress');
        
        
        if ((freeFormAddressElement.length > 0) || (geocodeAddressElement.length > 1))
        {    
            //addres in not univoc
            this.events.triggerEvent("drawFailure"); 
            return;
        }
        var geocodedAddress = xmlResponse.getElementsByTagName('GeocodedAddress')[0];
        var gmlpos = OpenLayers.Ajax.getElementsByTagNameNS(geocodedAddress, this.gmlns, "gml", "pos");
        var pos = gmlpos[0].firstChild.nodeValue;
        var x = pos.substring(0,pos.indexOf(' '));
        var y = pos.substring(pos.indexOf(' '));
        
        //if envelope is return...zoom to extend bounds
        var gmlEnvelope = OpenLayers.Ajax.getElementsByTagNameNS(geocodedAddress, this.gmlns, "gml", "Envelope"); 
        if (gmlEnvelope.length > 0){
            var pos1Element = OpenLayers.Ajax.getElementsByTagNameNS(gmlEnvelope[0], this.gmlns, "gml", "pos")[0];
            var pos2Element = OpenLayers.Ajax.getElementsByTagNameNS(gmlEnvelope[0], this.gmlns, "gml", "pos")[1];
            var minx = pos1Element.firstChild.nodeValue.substring(0,pos.indexOf(' '));
            var miny = pos1Element.firstChild.nodeValue.substring(pos.indexOf(' '));
            
            var maxx = pos2Element.firstChild.nodeValue.substring(0,pos.indexOf(' '));
            var maxy = pos2Element.firstChild.nodeValue.substring(pos.indexOf(' '));
            
            var bounds = new OpenLayers.Bounds(minx,miny,maxx,maxy);
            //Control level zoom to set correct center if zoom level is 0
            map.zoomToExtent(bounds);
            if (map.getZoom() == 0 ){
                map.setCenter(map.getInitialCenter(),0);
            }
        }
        //else...max zoom to point returned
        else{
            var lonlat = new OpenLayers.LonLat(x,y);
        //Temporanly zoom to max zoom levels -3 if an address area is obteined
        if (xmlResponse.getElementsByTagName('Street')[0].firstChild == null)
        {
            this.map.setCenter(lonlat,this.map.getNumZoomLevels()-4);
        }
        else
        {
            this.map.setCenter(lonlat,this.map.getNumZoomLevels()-6);
        }
            this.events.triggerEvent("drawComplete");
        }
    },

    /**
    * @private 
    * Method: createFreeFormAddressRequest
    *
    * Parameters:
    * address - 
    * address to search
    *
    *
    * Returns:
    * request - {DOMElement} request for Geocode service
    * address ca be a text input address to search or a address XML element. This function manage both case
    */
    createFreeFormAddressRequest: function (address) {
        var request = '<?xml version="1.0" encoding="iso-8859-1"?>';
        //controllo se address contiene un indirizzo o un xml
        var addressReplaced = Roja.Util.transformXMLJStoXMLString(address);
        var startXML = new OpenLayers.parseXMLString(addressReplaced);
        if ((startXML.firstChild != null) && (startXML.firstChild.tagName != 'parsererror') && (startXML.getElementsByTagName('parsererror').length==0))
        {
            //contiene un xml
            request += '<GeocodeRequest xmlns="http://www.opengis.net/xls">';
            request += addressReplaced;
            request += '</GeocodeRequest>';
        }
        else{
            request += 
                '<GeocodeRequest xmlns="http://www.opengis.net/xls">'+
                    '<Address countryCode="IT">' +
                        '<freeFormAddress>'+
                                address +
                        '</freeFormAddress>'+
                    '</Address>'+
                '</GeocodeRequest>';
        }
        return request;

    },

    /**
    * Method: reset
    * Reset object to initial status
    */
    reset: function() {
        this.markersLayer.clearMarkers();
        this.status_code = NONE;
    },
    
        
    /**
    * Method: getStatusCode
    * Get status code of addressing response. 
    * Possible values are:
    *    NONE = 0;               //Initial status
    *    ADDRESS_LOADED = 1;     //Address have been loaded
    *    UNKNOWN_ADDRESS = 11;   //Address cannot be found
    *    MULTI_ADDRESS = 12;     //There is a multi addressess result
    *    SERVER_ERROR = 13;      //Server error 
    * 
    * Returns:
    * {int} status code
    */
    getStatusCode: function () {
        return this.status_code;
    },
    
    
    /**
    * Method: getGeocodes
    * Get the geocoded addressess
    * 
    * Returns:
    * {Array(Roja.Marker.GeocodedAddress)} Array of Roja.Marker.GeocodedAddress
    */
    getGeocodes: function (){
        return this.geocodedAddr;
    },
    
      

    /** @final @type String */
    CLASS_NAME: "Roja.Address"
});





