﻿/// <reference name="MicrosoftAjax.debug.js" />
/// <reference name="MicrosoftAjaxTimer.debug.js" />
/// <reference name="MicrosoftAjaxWebForms.debug.js" />

Type.registerNamespace("SGis.MapToolkit");

SGis.MapToolkit.LayerObjectsBehavior = function(element) {
    SGis.MapToolkit.LayerObjectsBehavior.initializeBase(this, [element]);
    // Referencia a Controles y objetos
    this._mapControl = null;
    this._mapContainer = null;    // Referencia al contenedor de mapas
    this._parentControl = null;   // Referencia al control de mapas
    this._lyr = [];               // Elementos de layers imagenes 0 y 1
    // Propiedades
    this._ServicePath = null;     // Ruta del servicio
    this._ServiceMethod = null;   // Método a llamar del servicio de mapas
    this._MarginBuffer = 100;     // Buffer del grafico para las 4 direcciones
    this._ZOrder = 10;            // orden del indice z
    this._LayerId = "objMap";          // Identificador interno
    this._Params = "";            // Parámetros para obtener los objetos
    this._LoadDataDelay = 0;      // Retraso para cargar los datos (esto es para darle prioridad a que se ejecuten otros procesos antes)
    this._AutoRefresh = false;     // Indica que esta capa se autorefresca
    this._AutoRefreshPaused = false; // Indica si esta pausado el autorefresh
    this._AutoRefreshInterval = 5000; // Tiempo medio entre autorefresco (5 segundos)
    // Miembros Privados
    this._currentMap = 0;         // Una layer se usa de backup mientras la otra se muestra
    this._isLoaded = false;       // Bandera para indicar si el control ha sido cargado (inicializado)
    this._mapWidth = 0;           // Ancho del mapa incluyendo margenes buffer
    this._mapHeight = 0;          // Alto del mapa, incluyendo margenes buffer
    this._ctrWidth = 0;           // Ancho del control (parte visible del mapa)
    this._ctrHeight = 0;          // Alto del control (parte visible del mapa)
    this._serviceTimeOut = 20000; // Tiempo de espera máxio para time out
    this._requestNumber = 0;      // Bandera para determinar el numero de request al servidor para descartar respuestas asincronicas cuando hay una posterior
    this._isDirty = false;        // Bandera que indica si el mapa esta sucio
    this._autoRefreshTimer = 0;   // Temporizador utilizado para autorefresco
    this._lastZoom = 0;           // Ultimo zoom (utilizado para no ocultar la capa que esta siendo actualizada si es solo un paneo)
    this._loadDataTimer = 0;      // Temporizador usado para retrasar la carga de objetos
    // Animadores para Coordenadas y dimensiones 
    this._aniChangeDim = null;
    this._aniChangeX = null;
    this._aniChangeY = null;
    this._aniChangeW = null;
    this._aniChangeH = null;
    // Eventos
    this._mapControl$onPanningMove$delegate = Function.createDelegate(this, this._mapControl_onPanningMove);
    this._mapControl$onPanningEnd$delegate = Function.createDelegate(this, this._mapControl_onPanningEnd);
    this._mapControl$onMapExtentChanged$delegate = Function.createDelegate(this, this._mapControl_onMapExtentChanged);
    //
    this._obj$delegates = {
        mousemove: Function.createDelegate(this, this._obj_onMouseMoveHandler),
        mouseout: Function.createDelegate(this, this._obj_onMouseOutHandler),
        mousedown: Function.createDelegate(this, this._obj_onMouseDownHandler)
    }

}

SGis.MapToolkit.LayerObjectsBehavior.prototype = {
    initialize: function() {
        //
        SGis.MapToolkit.LayerObjectsBehavior.callBaseMethod(this, 'initialize');
        //
        this.add_propertyChanged(Function.createDelegate(this, this._onPropertyChanged));
        //
        this._mapContainer = this.get_element().control._mapContainer;
        this._mapControl = this.get_element();
        this._parentControl = this.get_element().control;
        // Crea Controles
        this.buildAllControls();
        // Attach event handlers

        this._parentControl.add_panningMove(this._mapControl$onPanningMove$delegate);
        this._parentControl.add_panningEnd(this._mapControl$onPanningEnd$delegate);
        this._parentControl.add_mapExtentChanged(this._mapControl$onMapExtentChanged$delegate);
        // Animations
        this._aniChangeX = new AjaxControlToolkit.Animation.LengthAnimation(null, null, null, "style", null, 0, 0, "px");
        this._aniChangeY = new AjaxControlToolkit.Animation.LengthAnimation(null, null, null, "style", null, 0, 0, "px");
        this._aniChangeW = new AjaxControlToolkit.Animation.LengthAnimation(null, null, null, "style", null, 0, 0, "px");
        this._aniChangeH = new AjaxControlToolkit.Animation.LengthAnimation(null, null, null, "style", null, 0, 0, "px");
        this._aniChangeDim = new AjaxControlToolkit.Animation.ParallelAnimation(null, .10, null, [this._aniChangeX, this._aniChangeY, this._aniChangeW, this._aniChangeH]);

    },
    dispose: function() {
        this._parentControl.remove_panningMove(this._mapControl$onPanningMove$delegate);
        this._parentControl.remove_panningEnd(this._mapControl$onPanningEnd$delegate);
        this._parentControl.remove_mapExtentChanged(this._mapControl$onMapExtentChanged$delegate);
        //
        SGis.MapToolkit.LayerObjectsBehavior.callBaseMethod(this, 'dispose');
    },
    // Construye los controles internos
    buildAllControls: function() {
        for (i = 0; i < 2; i++) {
            this._lyr[i] = $common.createElementFromTemplate({
                nodeName: "div",
                properties: {
                    id: "lyrObj" + i,
                    style: {
                        position: "absolute",
                        visibility: "hidden",
                        zIndex: this._ZOrder - i
                    }
                }
            }, this._mapContainer);
        }

    },
    get_PlugInId: function() {
        /// <value type="string" mayBeNull="false">
        /// Gets de plugIn unique id
        /// </value>
        return 'sgis.objectsmap';
    },
    load: function(container) {
        if (this._isLoaded) return;
        this._isLoaded = true;
        this.resetLayers();
    },
    unload: function() {
        Sys.Debug.trace('Unloading objectsMap');
    },
    // Resetea posición y tamaño de layers
    resetLayers: function() {
        var size = WebForm_GetElementPosition(this._mapContainer);
        this._mapWidth = size.width + this._MarginBuffer * 2;
        this._mapHeight = size.height + this._MarginBuffer * 2;
        this._ctrWidth = size.width;
        this._ctrHeight = size.height;
        var l0 = this._lyr[0];
        var l1 = this._lyr[1];
        // Hace capas invisibles para evitar distorsión en resize
        l0.style.visibility = "hidden";
        l1.style.visibility = "hidden";
        l0.startPos = null;
        l1.startPos = null;
        // Cambia tamaños 
        WebForm_SetElementHeight(l0, this._mapHeight);
        WebForm_SetElementHeight(l1, this._mapHeight);
        WebForm_SetElementWidth(l0, this._mapWidth);
        WebForm_SetElementWidth(l1, this._mapWidth);
        // Cambia posición
        WebForm_SetElementX(l0, this._MarginBuffer * (-1));
        WebForm_SetElementX(l1, this._MarginBuffer * (-1));
        WebForm_SetElementY(l0, this._MarginBuffer * (-1));
        WebForm_SetElementY(l1, this._MarginBuffer * (-1));
        // Carga contenido
        this.loadData();
    },
    loadData: function() {
        $common.setVisible(this.visibleMap(), false);
        this.programLoadData();
    },
    // programa la carga del reporte, retrasandola decimas de segundo
    programLoadData: function() {
        if (this._LoadDataDelay > 0) {
            window.clearTimeout(this._loadDataTimer);
            this._loadDataTimer = window.setTimeout(Function.createDelegate(this, this.invokeGetMapWebService), this._LoadDataDelay);
        } else {
            this.invokeGetMapWebService();
        }
    },
    autoLoadData: function() {
        // Invoca solicitud sin ocultar
        this.invokeGetMapWebService();
        // Cancela autorefresco hasta obtener los datos o un timeout
        this.cancelAutoRefresh();
    },
    // Invoca al servicio web para obtener mapa en forma asincrónica
    invokeGetMapWebService: function() {
        this._requestNumber++;
        // Oculta capa visible para que no se vea sucia mientras se carga la nueva si es que cambió el zoom
        if (this._lastZoom != this._parentControl._Zoom) {
            this._lastZoom = this._parentControl._Zoom;
            $common.setVisible(this.visibleMap(), false);
        }
        Sys.Net.WebServiceProxy.invoke(this._ServicePath, "GetObjects", false,
                { centerX: this._mapControl.control._CenterX, centerY: this._mapControl.control._CenterY, zoom: this._mapControl.control._Zoom, imgWidth: this._mapWidth, imgHeight: this._mapHeight, objectParams: this._Params },
                Function.createDelegate(this, this.onGetMapMethodComplete),
                Function.createDelegate(this, this.onGetMapMethodError), { requestNumber: this._requestNumber }, this._serviceTimeOut);
        this.showStatusProcess('Solicitando mapa...');
    },
    // Muestra mensaje cargando y throbber
    showStatusProcess: function(message) {
        if (this._parentControl._stsBar) {
            this._parentControl._stsBar.addMessage(this._LayerId, { Text: message, Icon: SGis.MapToolkit.StatusBarIcon.Throbber });
        }
    },
    // Muestra mensaje cargando y throbber
    showStatusDownload: function(message) {
        if (this._parentControl._stsBar) {
            this._parentControl._stsBar.addMessage(this._LayerId, { Text: message, Icon: SGis.MapToolkit.StatusBarIcon.Download });
        }
    },
    showStatusError: function(message) {
        if (this._parentControl._stsBar) {
            this._parentControl._stsBar.addMessage(this._LayerId, { Text: message, Icon: SGis.MapToolkit.StatusBarIcon.Error });
        }
    },
    removeStatus: function(message) {
        if (this._parentControl._stsBar) {
            this._parentControl._stsBar.removeMessage(this._LayerId);
        }
    },
    // Se completo invocación al web service - GetMap
    onGetMapMethodComplete: function(result, userContext, methodName) {
        // Verifica que sea el ultimo request, ignora resultado si no
        if (this._requestNumber != userContext.requestNumber) return;
        // Si el resultado es invalido debe mostrar el error y no procesar mas
        if (!result.IsValid) return;
        this._isDirty = false;
        // Aplica cambios al mapa invisible para mostrarlo una vez cargado
        var map = this.invisibleMap();
        map.resultGetMap = result; // Se guarda el resultado del mapa dentro de la layer para porder dibujarla en base a valores actuales de centro y zoom
        // 
        this.showStatusDownload('Cargando mapa...');
        this.buildLayerObjects(map);
        this.swapImgMaps();
        //
        map.startPos = this.getRelativeMapPosition(map);
        WebForm_SetElementHeight(map, map.startPos.height);
        WebForm_SetElementWidth(map, map.startPos.width);
        WebForm_SetElementX(map, map.startPos.x);
        WebForm_SetElementY(map, map.startPos.y);

        // Programa proximo auto refresh
        this.programAutoRefresh();
    },
    // Recontruye la capa pasada como parámetro ,
    buildLayerObjects: function(lyr) {
        lyr.innerHTML = "";
        for (var i in lyr.resultGetMap.Objects) {
            var obj = lyr.resultGetMap.Objects[i];
            var ele = $common.createElementFromTemplate({
                nodeName: "div",
                properties: {
                    id: "o_" + obj.Id,
                    style: {
                        position: "absolute",
                        left: obj.Left + "px",
                        top: obj.Top + "px",
                        width: obj.Width + "px",
                        height: obj.Height + "px"
                    },
                    innerHTML: obj.ObjectHtml
                }
            }, lyr);

            // Div invisible para manejar eventos
            if (obj.HasToolTip) {
                var tip = $common.createElementFromTemplate({
                    nodeName: "div",
                    properties: {
                        id: "tip_" + obj.Id,
                        style: {
                            position: "absolute",
                            left: (obj.Left - obj.Width / 2) + "px",
                            top: (obj.Top - obj.Height / 2) + "px",
                            width: obj.Width + "px",
                            height: obj.Height + "px",
                            zIndex: 100
                        }
                    },
                    events: this._obj$delegates
                }, lyr);
                tip._object = obj;
            }
        }


    },
    // Obtiene la posición, tamaño y zoom relativos entre un mapa y el control principal
    getRelativeMapPosition: function(element) {
        var c = this._mapControl.control;
        // Crea objeto para retornar
        var res = element.resultGetMap;
        var dx = res.CenterX / c._Zoom - c._CenterX / c._Zoom;
        var dy = res.CenterY / c._Zoom - c._CenterY / c._Zoom;
        //debugger;
        var result = new Object();
        // Relación entre el zoom actual y el zoom del mapa
        result.r = Math.round(element.resultGetMap.Zoom / c._Zoom * 10000) / 10000;
        // Ancho del mapa
        result.width = element.resultGetMap.Width * result.r;
        // Alto
        result.height = element.resultGetMap.Height * result.r;
        // Posición X
        result.x = Math.round((res.Width - this._ctrWidth * result.r) / -2 + dx);
        // Posición Y
        result.y = Math.round((res.Height - this._ctrHeight * result.r) / -2 - dy);
        return result;
    },
    // Error al invocar web service
    onGetMapMethodError: function(webServiceError, userContext, methodName) {
        // Verifica que sea el ultimo request, ignora resultado si no
        if (this._requestNumber != userContext.requestNumber) return;
        // Call failed
        //this._removeStatusLabel();
        var strError;
        if (webServiceError.get_timedOut()) {
            strError = "Tiempo de espera excedido.";
        } else {
            strError = "Error al llamar Servicio Web: " + webServiceError.get_statusCode() + " " + webServiceError.get_message();
        }
        this.showStatusError(strError);
        // Programa proximo auto refresh
        this.programAutoRefresh();
    },
    // Obtiene el mapa visible
    visibleMap: function() {
        return this._lyr[this._currentMap];
    },
    // Obtiene el mapa invisible
    invisibleMap: function() {
        return this._lyr[this._currentMap == 0 ? 1 : 0];
    },
    // Determina si es necesario redibujar el mapa, cuando se encuentra fuera de la ventana donde se ve
    reloadIfNeeded: function() {
        var map = this.visibleMap();
        var reload = true;
        if (map.startPos) {
            var pos = map.startPos;
            if (this._isDirty || pos.x > 0 || pos.y > 0 || pos.r != 1 || pos.width + pos.x < this._ctrWidth || pos.height + pos.y < this._ctrHeight) {
                reload = true;
            } else {
                reload = false;
            }
        }
        if (reload) this.loadData();
    },
    // Intercambia imagenes de mapas
    swapImgMaps: function() {
        this.removeStatus(); // Remueve el mensaje en la barra de estados
        this._currentMap = this._currentMap == 0 ? 1 : 0;
        $common.setVisible(this.visibleMap(), true);
        $common.setVisible(this.invisibleMap(), false);
    },
    // Realiza el paneo y zoom animado de el mapa principal y el overview
    animateZoomPan: function() {
        var map = this.visibleMap();
        if (map.resultGetMap) {
            // cambio porcentual del zoom (para minimizar errores de redondeo)
            var z = Math.abs((map.resultGetMap.Zoom - this._parentControl._Zoom) / map.resultGetMap.Zoom);
            //
            if (map.startPos && z < 0.001) {

                var endPos = this.getRelativeMapPosition(map);

                this._aniChangeX.set_propertyKey("left");
                this._aniChangeX.set_target(map);
                this._aniChangeX.set_startValue(map.startPos.x);
                this._aniChangeX.set_endValue(endPos.x);
                this._aniChangeY.set_propertyKey("top");
                this._aniChangeY.set_target(map);
                this._aniChangeY.set_startValue(map.startPos.y);
                this._aniChangeY.set_endValue(endPos.y);
                this._aniChangeW.set_propertyKey("width");
                this._aniChangeW.set_target(map);
                this._aniChangeW.set_startValue(map.startPos.width);
                this._aniChangeW.set_endValue(endPos.width);
                this._aniChangeH.set_propertyKey("height");
                this._aniChangeH.set_target(map);
                this._aniChangeH.set_startValue(map.startPos.height);
                this._aniChangeH.set_endValue(endPos.height);
                var endHandler = Function.createDelegate(this, function() {
                    this._aniChangeDim.remove_ended(endHandler);
                    this.reloadIfNeeded();
                });
                map.startPos = endPos;  // La posición final pasa a ser la pos. inicial
                this._aniChangeDim.add_ended(endHandler);
                this._aniChangeDim.play();
            } else {
                this.loadData();
            }
        }
    },
    // Programa el cerrado de la ventana de información
    programAutoRefresh: function() {
        window.clearTimeout(this._autoRefreshTimer);
        if (this._AutoRefresh && !this._AutoRefreshPaused) {
            this._autoRefreshTimer = window.setTimeout(Function.createDelegate(this, this.autoLoadData), this._AutoRefreshInterval);
        }
    },
    // Anula autorefresco, por ejemplo cuando se esta haciendo panning
    cancelAutoRefresh: function() {
        window.clearTimeout(this._autoRefreshTimer);
    },
    // pausa el autorefresco
    pauseAutoRefresh: function() {
        this._AutoRefreshPaused = true;
        this.cancelAutoRefresh();
    },
    // Anula autorefresco, por ejemplo cuando se esta haciendo panning
    resumeAutoRefresh: function() {
        if (this._AutoRefreshPaused && this._AutoRefresh) {
            this._AuroRefreshpaused = false;
            this.programAutoRefresh();
        }
    },
    //--------------------------------------------------------------------------
    // Acceso a Propiedades
    //--------------------------------------------------------------------------
    get_Params: function() {
        return this._Params;
    },
    set_Params: function(value) {
        if (this._Params != value) {
            this._Params = value;
            this.raisePropertyChanged('Params');
        }
    },

    get_ServicePath: function() {
        return this._ServicePath;
    },
    set_ServicePath: function(value) {
        if (this._ServicePath != value) {
            this._ServicePath = value;
            this.raisePropertyChanged('ServicePath');
        }
    },
    get_ServiceMethod: function() {
        return this._ServiceMethod;
    },
    set_ServiceMethod: function(value) {
        if (this._ServiceMethod != value) {
            this._ServiceMethod = value;
            this.raisePropertyChanged('ServiceMethod');
        }
    },
    get_MarginBuffer: function() {
        return this._MarginBuffer;
    },
    set_MarginBuffer: function(value) {
        if (this._MarginBuffer != value) {
            this._MarginBuffer = value;
            this.raisePropertyChanged('MarginBuffer');
        }
    },
    get_ZOrder: function() {
        return this._ZOrder;
    },
    set_ZOrder: function(value) {
        if (this._ZOrder != value) {
            this._ZOrder = value;
            this.raisePropertyChanged('ZOrder');
        }
    },
    get_LayerId: function() {
        return this._LayerId;
    },
    set_LayerId: function(value) {
        if (this._LayerId != value) {
            this._LayerId = value;
            this.raisePropertyChanged('LayerId');
        }
    },
    get_AutoRefresh: function() {
        return this._AutoRefresh;
    },
    set_AutoRefresh: function(value) {
        if (this._AutoRefresh != value) {
            this._AutoRefresh = value;
            this.raisePropertyChanged('AutoRefresh');
        }
    },
    get_AutoRefreshPaused: function() {
        return this._AutoRefreshPaused;
    },
    set_AutoRefreshPaused: function(value) {
        if (this._AutoRefreshPaused != value) {
            this._AutoRefreshPaused = value;
            this.raisePropertyChanged('AutoRefreshPaused');
        }
    },
    get_AutoRefreshInterval: function() {
        return this._AutoRefreshInterval;
    },
    set_AutoRefreshInterval: function(value) {
        if (this._AutoRefreshInterval != value) {
            this._AutoRefreshInterval = value;
            this.raisePropertyChanged('AutoRefreshInterval');
        }
    },
    get_LoadDataDelay: function() {
        return this._LoadDataDelay;
    },
    set_LoadDataDelay: function(value) {
        if (this._LoadDataDelay != value) {
            this._LoadDataDelay = value;
            this.raisePropertyChanged('LoadDataDelay');
        }
    },
    //--------------------------------------------------------------------------
    // Manejadores de Eventos
    //--------------------------------------------------------------------------
    // Mueve el mose sobre el Mapa

    // Al realizar paneo en el control mapa
    _mapControl_onPanningMove: function(sender, args) {
        var map = this.visibleMap();
        if (map.startPos) {
            WebForm_SetElementX(map, map.startPos.x + args.dx);
            WebForm_SetElementY(map, map.startPos.y + args.dy);
        }
        // TODO: En lugar de cancelar el autorefresco, controlar esto en el medodo del refresco,
        //       para que en el caso de que se necesite, se marque como dirty y refrescar
        //       inmediatamente termine el panning.
        this.cancelAutoRefresh();
        if (this._parentControl._infoWindow)
            this._parentControl._infoWindow.hideInfoWindow(true);
    },
    // Al finalizar paneo de mapa
    _mapControl_onPanningEnd: function(sender, args) {
        var map = this.visibleMap();
        if (map.startPos) {
            // Aplica los delta en forma definitiva
            map.startPos.x = map.startPos.x + args.dx;
            map.startPos.y = map.startPos.y + args.dy;
            WebForm_SetElementX(map, map.startPos.x + args.dx);
            WebForm_SetElementY(map, map.startPos.y + args.dy);

        }
        this.programAutoRefresh();
        //this.reloadIfNeeded();
    },
    // Al cambiar la extensión del mapa
    _mapControl_onMapExtentChanged: function(sender, args) {

        var map = this.visibleMap();
        this.animateZoomPan();
    },

    // Termina carga de imagen para mapa 2
    _obj_onMouseMoveHandler: function(evt) {
        try {
            if (this._parentControl._infoWindow && evt.target._object) {
                if (this._parentControl._infoWindow._objectId === evt.target._object.Id)
                    return;
                var v = $common.getLocation(this.visibleMap());
                var p = $common.getLocation(this.visibleMap().parentNode);
                var o = evt.target._object;
                var w = $common.getClientBounds();
                var s = $common.getSize(this._mapControl);
                var top = '';
                var left = '';
                var bottom = '';
                var right = '';
                // Posición X
                if (evt.clientX > w.width / 2) {
                    right = s.width - (o.Left + (v.x - p.x)) + o.Width / 2 + 2;
                } else {
                    left = o.Left + o.Width + (v.x - p.x);
                }
                // Posición Y
                if (evt.clientY > w.height / 2) {
                    bottom = s.height - (o.Top + o.Height + (v.y - p.y));
                } else {
                    top = o.Top - o.Height / 2 + (v.y - p.y);
                }
                var s = evt.target._object.ServiceInfo;
                var o = evt.target._object.Id;
                this._parentControl._infoWindow.showInfoWindow(top, right, bottom, left, s, o);
            }
        } catch (e) {
            debugger;
        };
    },
    // Termina carga de imagen para mapa 2
    _obj_onMouseOutHandler: function(evt) {
        try {
            if (this._parentControl._infoWindow) {
                this._parentControl._infoWindow.programHideInfoWindow();
            }

        } catch (e) {
            debugger;
        };
    },
    // Al cambiar propiedades 
    _onPropertyChanged: function(sender, args) {
        var propname = args.get_propertyName();
    },
    // Al presionar el botón del mouse
    _obj_onMouseDownHandler: function(evt) {
        // revisar porque no funciona
        //debugger;
        try {
            if (evt.target._object && evt.target._object.OnClientClick && evt.target._object.OnClientClick != "") {
                eval(evt.target._object.OnClientClick);
            }

        } catch (e) {
            debugger;
        };
    }
}

SGis.MapToolkit.LayerObjectsBehavior.registerClass('SGis.MapToolkit.LayerObjectsBehavior', AjaxControlToolkit.BehaviorBase, SGis.MapToolkit.IPlugIn);
if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();