﻿/* ================================================================== */
/* SensorMap.js                                                       */
/*                                                                    */
/* This file contains all functions related to the core SensorMap UI  */
/* (that is the banner, the menubar, the map, the sensors placed on   */
/* the map, and the footer).  This file has functions related to      */
/* initailizing SensorMap, resizing, handling events that occur by    */
/* interacting with the map, and filter points.                       */
/*                                                                    */
/* ================================================================== */

/*
 * SensorMap globals.
 */

// Debug globals
var debugSubmitSensorsRequest = 0;
var debugHandleIconize = 0;
var debugPutSensorPushpins = 0;

// Window sizing globals
var bannerheight = 74;
var menubarheight = 20;
var footerheight = 20;
var defaultMinWidth = 1024;
var defaultMinHeight = 768;
var clientBodySize = null;

// Model globals
var smModel = null;

// View (UI) globals
var smMap = null;
var mapContainer = null;
var mapTableCell = null;
var movingPushpin = null;
var sensorsInViewport = [];
var sensorListByLatLon = {};

var polyFilterLayer = null;
var polygon = null;

var sensorLayer = null;

// View (UI) Context Menu globals
var sm_contextMenu;
var contextPin = null;
var contextPinSize = 7;

// Timer globals
var sensorStatusInterval = null;
var sensorStatusIntervalTime = 500;
var sensorStatusLabelVisible = false;
var lastQuery = null;

var swClientID = '';
var swPassCodeClientID = '';

var SM_Event = {
    VE_MAP_EVENT        : 0,
    SAVED_VIEW          : 1,
    FILTER_BY_TYPE      : 2, 
    FILTER_BY_SEARCH    : 3,
    FILTER_PIN_ADDED    : 4,
    FILTER_PIN_DELETED  : 5,
    POLYGON_DELETED     : 6,
    GO_TO_LOCATION      : 7
}

/* ============================================================ */
/* SensorMap Initialization                                     */
/* ============================================================ */

/*
 * OnApplicationLoad 
 *
 * Client-side 'main' function.  This function gets called from Default.aspx,
 * initializes global variables, registers window-related event handlers, and 
 * initializes SensorMap client-side functionality.
 */
function pageLoad()
{
    InitSensorTypes();
    InitSensorTypesDone = function() {
        //OnApplicationLoad('<%=swIDFieldName%>');
        OnApplicationLoad();
    }
} 

function OnApplicationLoad()
{    
    // initialize model
    SM_InitializeModel();
    
    // initialize view
    SM_InitializeView();
    
    // initialize handlers
    window.onresize = OnWindowResize;
    
    if (typeof swissMaps != 'undefined')
           swissMaps.init(smMap);
           
    graphAndTileHandler.init(smMap);
    graphTx.init(VizTypes, sensorTypeListByID, getNewViz, smModel.timeSetting, onColorMapChange);
    //graphTx.addOnColorMapChangeListener();
    
    ChartPanel.init(smModel.timeSetting);

    // register time change event
    TimePanel.init(smModel.timeSetting);
    TimePanel.addEventListener(onTimePanelchange);
    TimePanel.addEventListener(ChartPanel.onTimeChange);
    
    Chart.init('chart/');

    smMap.AttachEvent('onstartcontinuouspan', HideContextPinAndMenu);
    smMap.AttachEvent('onchangemapstyle', HandleSetStyle);
    smMap.AttachEvent('onstartzoom', HideContextPinAndMenu);
    smMap.AttachEvent('onendpan', HandleVEMapEvent);
    smMap.AttachEvent('onendzoom', HandleVEMapEvent);
    smMap.AttachEvent('onresize', HandleVEMapEvent);
    smMap.AttachEvent('onclick', SensorMapClick);
    smMap.AttachEvent("oninitmode", ModeChange);
    smMap.AttachEvent("onmouseover", HandleGetSensorDetail);
    smMap.AttachEvent("onmouseout", HandleMouseOut);
    //smMap.ClearInfoBoxStyles();

    //smMap.AddShape(new VEShape(VEShapeType.Pushpin, smMap.GetCenter()));
}

function HandleMouseOut(e) {
    if (e.elementID)
        if (lastShapeId == e.elementID)
            refresh = false;
}

var lastShapeId = null;
var refresh = true;
var _publishersNames=[];
function HandleGetSensorDetail(e) {
    if (e.elementID) {
        lastShapeId = e.elementID;
        refresh = true;
        var shape = smMap.GetShapeByID(lastShapeId);
        var points = shape.GetPoints();
        var unitLat = lastQuery ? 10 / lastQuery.latitudeRatio : 0.000001;
        var unitLon = lastQuery ? 10 / lastQuery.longitudeRatio : 0.000001;
        var index = parseInt(points[0].Latitude / unitLat) + ":" + parseInt(points[0].Longitude / unitLon);
        var sensorIndices = sensorListByLatLon[index];
        _publishersNames = null;
        if (sensorIndices && sensorIndices.length > 0) {
            var urls = [];
            var publisherNames = [];
            var sensorNames = [];
            _publishersNames = new Array();
            for (var ij = 0; ij < sensorIndices.length; ij ++) {
                var sensor = {};
                sensor = sensorsInViewport[sensorIndices[ij]];
                urls.push(sensor.webServiceUrl);
                publisherNames.push(sensor.publisher);
                _publishersNames.push(sensor.publisher);
                sensorNames.push(sensor.name);
            }  
            
            if (!smModel.timeSetting.inUse) {
                SenseWeb.AppManagerService.GetLatestScalarDataCSVInBatch(urls, publisherNames, sensorNames, HandleGetSensorDetailDone, HandleGetSensorDetailTimeout);
            } else {
                startTime = smModel.timeSetting.currentTime.toLocaleString();
                endTime = new Date(smModel.timeSetting.currentTime.getTime() + smModel.timeSetting.resolution * 1000).toLocaleString();
                SenseWeb.AppManagerService.GetAggregateScalarDataCSVInBatch(urls, publisherNames, sensorNames, startTime, endTime
                    , HandleGetSensorDetailDone, HandleGetSensorDetailTimeout);
            }
        }
    }
}

function HandleGetSensorDetailTimeout() {
    //alert("HandleGetSensorDetailTimeout");
}

function HandleGetSensorDetailDone(sensorDataCSV) {
//    alert("HandleGetSensorDetailDone:" + sensorDataCSV);
    var shape = smMap.GetShapeByID(lastShapeId);
    if (!shape) return;
    var points = shape.GetPoints();

    var unitLat = lastQuery ? 10 / lastQuery.latitudeRatio : 0.000001;
    var unitLon = lastQuery ? 10 / lastQuery.longitudeRatio : 0.000001;
    var index = parseInt(points[0].Latitude / unitLat) + ":" + parseInt(points[0].Longitude / unitLon);

    var sensorIndices = sensorListByLatLon[index];
    //var sensorDatumCSV = sensorDataCSV.replace(/\;(?=\;)/g, ' \;').replace(/([^#])\;/g, '$1$1\;').split(/[^#]\;/);
    var sensorDatumCSV = sensorDataCSV.replace(/\;/g, ' \;\;').split(/\;\;/);
    var sensorDetail = '';
    var sensor = {};
    if (sensorIndices && sensorIndices.length > 0) {
        for (var ij = 0; ij < sensorIndices.length; ij ++) {
            sensor = sensorsInViewport[sensorIndices[ij]];
            if (sensor.dataTypeName == 'scalar')
                sensor.updateState(sensorDatumCSV[ij]);
            if (sensor.name.indexOf('@') == -1) {
                sensorDetail += sensor.getDetailHTML();// + sensorDetail;
            } else {
                if (sensorDetail == '')
                    sensorDetail = sensor.getComponentMetaDetailHTML(); 
                sensorDetail += sensor.getComponentStateHTML();
            }
        }      
        shape.SetDescription(sensorDetail);
        //VEHideVEShapeERO(lastShapeId);
        if (refresh)
            VEShowVEShapeERO(lastShapeId, smMap.GUID);
    }
}

var panelDisplayState;
function ModeChange() {
    var el;
    if (smMap.GetMapMode() == VEMapMode.Mode3D) {
        // hide panels
        panelDisplayState = new Object();        
        el = document.getElementById('SM_ManageViewsPanel');
        if (el) {
            panelDisplayState['SM_ManageViewsPanel'] = el.style.display;
            el.style.display = 'none';
        }
        el = document.getElementById('timePanel');
        if (el) {
            panelDisplayState['timePanel'] = el.style.display;
            el.style.display = 'none';
        }
        el = document.getElementById('graphTx_panel');
        if (el) {
            panelDisplayState['graphTx_panel'] = el.style.display;
            el.style.display = 'none';
        }
        el = document.getElementById('SensorListDiv');
        if (el) {
            panelDisplayState['SensorListDiv'] = el.style.display;
            el.style.display = 'none';
        }
        el = document.getElementById('menu_bar_items');
        if (el) {
            panelDisplayState['menu_bar_items'] = el.style.display;
            el.style.display = 'none';
        }
    } else {
        // show panels
        el = document.getElementById('SM_ManageViewsPanel');
        if (el) {
            el.style.display = panelDisplayState['SM_ManageViewsPanel'];
        }
        el = document.getElementById('timePanel');
        if (el) {
            el.style.display = panelDisplayState['timePanel'];
        }
        el = document.getElementById('graphTx_panel');
        if (el) {
            el.style.display = panelDisplayState['graphTx_panel'];
        }
        el = document.getElementById('SensorListDiv');
        if (el) {
            el.style.display = panelDisplayState['SensorListDiv'];
        }
        el = document.getElementById('menu_bar_items');
        if (el) {
            el.style.display = panelDisplayState['menu_bar_items'];
        }
    }
}

function SM_InitializeModel()
{
    smModel = new SensorMapModel();

    if (location.search != '')
    {
        smModel.fromString(location.search.substring(1)); 
    }
    else
    {
        var savedViewCookieContent = ReadCookie(currentSavedViewCookieStr);
        
        if (savedViewCookieContent != null)
        {
            smModel.fromString(savedViewCookieContent);
        }
    }
}

function SM_InitializeView()
{
    // 0. set dimensions of map portion of ui
    clientBodySize = GetBodySize();
    mapContainer = document.getElementById('sensormap');    
    mapTableCell = document.getElementById('maptablecell');
    AdjustMapToClientBody();
    
    // 1. initialize virtual earth map control for ui
    smMap = new VEMap('sensormap');
    smMapCenter = new VELatLong(smModel.ctrLatitude, smModel.ctrLongitude);
    smMap.LoadMap(smMapCenter, smModel.zoom, smModel.style, false);
    
    // initilize the polygon selection layer
    polyFilterLayer = new VEShapeLayer();
    smMap.AddShapeLayer(polyFilterLayer);
    //DrawSensorMapPolyFilter(smModel.filterCoords);
    
    // 2. initialize sensors on map 
    sensorLayer = new VEShapeLayer();
    smMap.AddShapeLayer(sensorLayer);

    SubmitTypesRequest();
    SubmitSensorsRequest();
    
    // 3. initialize sensormap panels
    sm_contextMenu = new SensorMap_ContextMenu();
    MV_InitPanel();
    MV_PlacePanel();
    
}

function getNewViz(vizType, sensorTypes) {
    smModel.vizType = vizType;
    lastQuery.sensorTypes = sensorTypes;
    switch (smModel.vizType) {
        case VizTypes.contour:
            break;
        default:
            SubmitSensorsRequest(lastQuery);
            break;
    }
}

function onColorMapChange(colorMapSettings) {
    smModel.colorMapSetting.update(colorMapSettings.isContinuous, colorMapSettings.colorMapIndex
        , colorMapSettings.min, colorMapSettings.max, colorMapSettings.resolution, colorMapSettings.inUse);
}

function onTimePanelchange() {
    //alert('mapOntimechange ' + startTime + ' ' + endTime + ' ' + currentTime
    //    + ' ' + resolution);
    if (TimePanel.isTimerOn) {
        // hide the charting panel
        HidePanel('SensorListDiv');
    }
    switch (smModel.vizType) {
        case VizTypes.contour:
            graphTx.getContourMap(smModel.colorMapSetting, smModel.timeSetting);
            break;
        default:
            SubmitSensorsRequest(lastQuery);
            break;
    }
}

/* ============================================================ */
/* Map Event Handlers                                           */
/* ============================================================ */

function HandleVEMapEvent()
{
    if (smMap.GetMapMode() != VEMapMode.Mode3D)
        UpdateSensorMap(SM_Event.VE_MAP_EVENT);
}

/*
 * UpdateSensorMap 
 *
 * UpdateSensorMap should be called any time something in the UI has changed
 * (e.g., UI reloads, map location or sensor types to be viewed).  Updates 
 * the map by submitting new sensor request for new SensorMap view and saved 
 * current view information to a cookie.
 */
function UpdateSensorMap(action, args)
{
    if (action == SM_Event.VE_MAP_EVENT)
    {
        // update model
        smModel.ctrLatitude = smMap.GetCenter().Latitude;
        smModel.ctrLongitude = smMap.GetCenter().Longitude;
        smModel.zoom = smMap.GetZoomLevel();
        SubmitTypesRequest();
    }
    else if (action == SM_Event.SAVED_VIEW)
    {
        sensorsInViewport = [];
        sensorListByLatLon = new Object();
        MV_SetFilterTypes();
        MV_SetSearchFilterTerms();
        
        smMap.DeleteAllShapes();
        
        DrawSensorMapPolyFilter();
        
        smMap.SetCenterAndZoom(
            new VELatLong(smModel.ctrLatitude, smModel.ctrLongitude), smModel.zoom);
        smMap.SetMapStyle(smModel.style);
        SubmitTypesRequest();
    }
    else if (action == SM_Event.FILTER_BY_SEARCH)
    {
        smModel.filterTerms = MV_GetSearchFilterTerms();
    }
    else if (action == SM_Event.FILTER_PIN_ADDED)
    {
        var newPinLocation = contextPin.GetPoints()[0];
        HideContextPinAndMenu();
        
        // add filter point to model
        smModel.filterCoords.push(newPinLocation);
    
        // add filter point to view
        AddFilterPointToView(newPinLocation);
    }
    else if (action == SM_Event.FILTER_PIN_DELETED)
    {
        // delete filter point from model and view
        DeleteAllFilterPinsAndPoly(smModel.filterCoords.length);
        smModel.deleteFilterPoint(args);
        
        // redraw polygon filter
        DrawSensorMapPolyFilter();
    }
    else if (action == SM_Event.POLYGON_DELETED)
    {
        HideContextPinAndMenu();
        
        // delete filter point from model and view
        DeleteAllFilterPinsAndPoly(smModel.filterCoords.length);
        smModel.filterCoords = new Array();
    }
    else if (action == SM_Event.GO_TO_LOCATION)
    {
        // delete filter point from model and view
        DeleteAllFilterPinsAndPoly(smModel.filterCoords.length);
        smModel.filterCoords = new Array();
        
        smMap.Find('', args);
        SubmitTypesRequest();
    }

    SubmitSensorsRequest();
    
    // save state to current cookie
    WriteCookie(currentSavedViewCookieStr, smModel.toString());
}

/*
 * SensorMapContextMenu 
 *
 * On right click SensorMap shows a SensorMap context menu (see 
 * SensorMapPanel.js for SensorMap_ContextMenu object).
 */
function SensorMapContextMenu(e)
{
    // calculate coords that correspond to where user clicked    
    var parameters = new Object();
    parameters.mapXPixel = event.clientX;
    parameters.mapYPixel = event.clientY;
    var mapXPixel = event.clientX + contextPinSize;
    var mapYPixel = event.clientY - bannerheight - menubarheight + contextPinSize;
    var contextPointLocation = smMap.PixelToLatLong(new VEPixel(mapXPixel, mapYPixel));
    
    // put the context pin on the map
    if (contextPin != null)
    {
        smMap.DeleteShape(contextPin);
    }
    
    contextPin = new VEShape(VEShapeType.Pushpin, contextPointLocation);
    //contextPin.SetCustomIcon('image/red_circ7px.gif');
    contextPin.HideIcon();
    smMap.AddShape(contextPin);
 
    // if user is pressing alt while right clicking add filter point directly
    if (e.altKey)
    {
        UpdateSensorMap(SM_Event.FILTER_PIN_ADDED);
    }
    // otherwise show the context menu
    else
    {
        sm_contextMenu.show(ContextDef.MAP, parameters);
    }
}

/*
 * SensorMapClick 
 *
 * On left click SensorMap hides context menu.
 */
function SensorMapClick(e)
{
    if (e.leftMouseButton) {
        HideSensorManagement();
        sm_contextMenu.hide();
        HideContextPin();
        
        if (movingPushpin != null)
        {
            mapContainer.childNodes[0].style.cursor = '';
            
            var mapXPixel = event.clientX;
            var mapYPixel = event.clientY - bannerheight - menubarheight;
            var newLocation = smMap.PixelToLatLong(new VEPixel(mapXPixel, mapYPixel));
            SensorManagementMoveSave(newLocation.Latitude, newLocation.Longitude);
        }
    } else if (e.rightMouseButton) {
        SensorMapContextMenu(e);
    }
    
}

function HandleSetStyle(e)
{
    smModel.style = e.mapStyle;
}

function HideContextPin()
{
    if (contextPin != null)
    {
        smMap.DeleteShape(contextPin);
        contextPin = null;
    }
}

function HideContextPinAndMenu()
{
   HideContextPin();
    
   sm_contextMenu.hide();
}

/* ============================================================ */
/* Filter Point Handlers                                        */
/* ============================================================ */

function AddFilterPointToView(filterPointLocation)
{
    AddFilterPointPushpin(filterPointLocation, smModel.filterCoords.length - 1);
    
    DrawFilterPoly(); 
}

function DeleteAllFilterPinsAndPoly(numPinsOnMap)
{
    // delete all filter points and polygon from view
    /*
    for(ii = 0; ii < numPinsOnMap; ii++)
    {
        smMap.DeletePushpin('filterPoint' + ii);
    }

    smMap.DeleteAllPolygons();
    */
    polyFilterLayer.DeleteAllShapes();
    
}

function DrawSensorMapPolyFilter()
{
    // add poly points
    for(ii = 0; ii < smModel.filterCoords.length; ii++)
    {        
        AddFilterPointPushpin(smModel.filterCoords[ii], ii);
    }

    DrawFilterPoly();  
}

function AddFilterPointPushpin(location, index)
{
    var filterPointPinId = 'filterPoint' + index;
    var filterPointTitle = 'Polygon Vertex';
    var filterPointDetail = '<a href=javascript:UpdateSensorMap(' 
        + SM_Event.FILTER_PIN_DELETED + ',' + index 
        + ')>Delete</a>';
    var pin = new VEShape(VEShapeType.Pushpin, location);
    pin.SetTitle(filterPointTitle);
    pin.SetCustomIcon('image/flag.gif');
    pin.SetDescription(filterPointDetail);
    polyFilterLayer.AddShape(pin);
}

function DrawFilterPoly()
{
    if (polygon)
        polyFilterLayer.DeleteShape(polygon);
    if (smModel.filterCoords.length > 1)
    {   
        if (smModel.filterCoords.length == 2)
            polygon = new VEShape(VEShapeType.Polyline, smModel.filterCoords);
        else
            polygon = new VEShape(VEShapeType.Polygon, smModel.filterCoords);        
        polygon.SetLineWidth(2);
        polygon.SetLineColor(new VEColor(0, 150, 100, 0.2));
        polygon.SetFillColor(new VEColor(0, 150, 100, 0.2));
        polygon.HideIcon();
        polyFilterLayer.AddShape(polygon);
    }
}

/* ============================================================ */
/* Map and Window Resizing functions                            */
/* ============================================================ */

/*
 * OnWindowResize 
 *
 * Handles Javascript window resize event.
 */
function OnWindowResize()
{
    var newSize = GetBodySize();
    
    if ((newSize.width != clientBodySize.width 
         || newSize.height != clientBodySize.height) && mapContainer != null)
    {
        clientBodySize = GetBodySize();
        AdjustMapToClientBody();
    }
}

/*
 * GetBodySize 
 *
 * Returns the size of the body of the HTML page.
 */
function GetBodySize()
{
    var size = new Object();

    if (parseInt(navigator.appVersion) > 3)
    {
        // Netscape/Mozilla family
        if (navigator.appName == 'Netscape')
        {
            size.width = window.innerWidth;
            size.height = window.innerHeight;
        }
        if (navigator.appName.indexOf('Microsoft') != -1)
        {
            // Internet Explorer 6
            if (document.compatMode && document.compatMode != 'BackCompat')
            {
                size.width = document.documentElement.clientWidth;
                size.height = document.documentElement.clientHeight;
            }
            else  // Internet Explorer 5 and back
            {
                size.width = document.body.clientWidth;
                size.height = document.body.clientHeight;
            }
        }
    }
    
    return size;
}

/*
 * AdjustMapToClientBody 
 *
 * Sets the map control's div and surrounding table cell to fill the entire 
 * client browser window's width and the height between the SensorMap header 
 * and footer.
 */
function AdjustMapToClientBody()
{   
    if (mapContainer != null)
    {
        // get map width and height
        var mapdims = GetMapDimensions();
        
        // set the dimensions of the sensormap div
        mapContainer.style.width = mapdims.width + 'px';
        mapContainer.style.height = mapdims.height + 'px';
        
        // set the dimensions of the table cell surrounding the map to match 
        // of the map itself
        mapTableCell.style.width = mapdims.width + 'px';
        mapTableCell.style.height = mapdims.height + 'px';
    }
}

/*
 * GetMapDimensions 
 *
 * Gets what the dimensions of the map portion of the page should be.  The
 * minimum dimensions are 1024 x 768 so if user's browser window is smaller
 * than this, the dimension get set to 1024 x 768.
 */
function GetMapDimensions()
{
    var mapdims = new Object();

    clientBodySize = GetBodySize();
    
    if (clientBodySize.width < defaultMinWidth)
    {
        mapdims.width = defaultMinWidth;
    }
    else
    {
        mapdims.width = clientBodySize.width;
    }
    
    if (clientBodySize.height < defaultMinHeight)
    {
        mapdims.height = defaultMinHeight - bannerheight - menubarheight 
                        - footerheight; 
    }
    else
    {
        mapdims.height = clientBodySize.height - bannerheight - menubarheight 
                        - footerheight; 
    }

    return mapdims;
}

/* ============================================================ */
/* Sensor Type Fetching and Handling                            */
/* ============================================================ */
var currentTypes = null;

var typeCacheTimerId = 0;
function SubmitTypesRequest() {
    if (typeCacheTimerId) {
        clearTimeout(typeCacheTimerId);
    }
    typeCacheTimerId = setTimeout('TypeTimerFires()', 500);
}

function TypeTimerFires() {
    // get the topleft lat/lon and bottom right lat/lon
    var leftTopViewport = smMap.PixelToLatLong(new VEPixel(1, 1));
    var bodySize = GetBodySize();
    var rightDownViewport = smMap.PixelToLatLong(new VEPixel(bodySize.width - 1, bodySize.height - 1));

    var viewPort = leftTopViewport.Latitude + ',' + leftTopViewport.Longitude + ',' +
                     leftTopViewport.Latitude + ',' + rightDownViewport.Longitude + ',' +
                     rightDownViewport.Latitude + ',' + rightDownViewport.Longitude + ',' +
                     rightDownViewport.Latitude + ',' +  leftTopViewport.Longitude;
    SenseWeb.IconD.GetSensorTypeURIsInView(viewPort
        , HandelTypes, HandelTypesTimeout);
}

function HandelTypes(result) {
    if (result != currentTypes) {
        MV_UpdateFilterTypes;
        //SubmitSensorsRequest();

        currentTypes = result;
        
        // init sensorTypes in view
        for (var i in sensorTypeListByUri) {
            if(sensorTypeListByUri[i] != null)
                sensorTypeListByUri[i].inView = false;
        }      
        //update types in view
        for (var i in result){
            if((result[i] != null || result[i] != '') && (sensorTypeListByUri[result[i]] != null || typeof(sensorTypeListByUri[result[i]]) != 'undefined' ))
                sensorTypeListByUri[result[i]].inView = true;
        }

        graphTx.updateSensorTypes(sensorTypeListByUri);
        MV_InitPanel();
    }
}

function HandelTypesTimeout() {
}


/* ============================================================ */
/* Sensor Fetching and Placement Functions                      */
/* ============================================================ */

/*
 * HandleIconize 
 *
 * Places sensor icons on the map by iterating through the sensor csv result, 
 * making sensor objects, adding them to the global collection of sensors 
 * seen in the viewport, putting sensors on map as VE pushpins, and turning off
 * the status interval.
 */
function HandleIconize(result)
{
    if (debugHandleIconize == 1)
        alert('result = ' + result);
    RemoveSensorPushpins();
    sensorsInViewport = [];
    sensorListByLatLon = {};
    var count = 0;

    if (result != null && result.substring(0, 7) == '#error:')
    {
        alert(result.substring(7));
    }
    else if ( (result != null) && (result.length > 0) )
    {
        var sensorsCsvs = result.split(';');
        
        if (debugHandleIconize == 1)
            alert('sensorsCsvs = ' + sensorsCsvs);
            
        if ( (sensorsCsvs != null) && (sensorsCsvs.length > 0) )
        {
            for (var ii = 0; ii < sensorsCsvs.length; ii++)
            {
                if (sensorsCsvs[ii] != null && sensorsCsvs[ii].length > 0) 
                {
                    var newSensor = Sensor.fromString(sensorsCsvs[ii]);
                    count += newSensor.numSensors ? newSensor.numSensors : 1;
                    var unitLat = lastQuery ? 10 / lastQuery.latitudeRatio : 0.000001;
                    var unitLon = lastQuery ? 10 / lastQuery.longitudeRatio : 0.000001;
                    var index = parseInt(newSensor.latitude / unitLat) + ":" + parseInt(newSensor.longitude / unitLon);
                    if (!sensorListByLatLon[index]) {
                        sensorListByLatLon[index] = [];
                    }
                    sensorListByLatLon[index].push(sensorsInViewport.length);
                    sensorsInViewport.push(newSensor);
 
                }
            }
            PutSensorPushpins2();
        }
    }
    if (count == 500)
        setStatusLabel('More than 500 sensors in view. Only 500 sensors are displayed.');
    else
        hideStatusLabel();
    if (TimePanel.isTimerOn)
        TimePanel.nextDone();
}

/*
 * HandleIconizeTimeout 
 *
 * Handles the case that the sensor query times out.
 */
function HandleIconizeTimeout(result)
{
    hideStatusLabel();
    if (TimePanel.isTimerOn)
        TimePanel.nextDone();
}

function PolygonQuery(pointList, viewPort
        , latitudeRatio, longitudeRatio, zoomLevel
        , sensorTypes, searchTerm
        , startTime, endTime) {
    this.pointList = pointList;
    this.viewPort = viewPort;
    this.latitudeRatio = latitudeRatio;
    this.longitudeRatio = longitudeRatio;
    this.zoomLevel = zoomLevel;
    this.sensorTypes = sensorTypes;
    this.searchTerm = searchTerm;
    this.startTime = startTime;
    this.endTime = endTime;
}

PolygonQuery.create = function() {
    // get the topleft lat/lon and bottom right lat/lon
    var leftTopViewport = smMap.PixelToLatLong(new VEPixel(1, 1));
    var bodySize = GetBodySize();
    var rightDownViewport = smMap.PixelToLatLong(new VEPixel(bodySize.width - 1, bodySize.height - 1));
    
    // calculate the latitude and longitude ratio that IconD will use for 
    // grouping icons
    var minLat = leftTopViewport.Latitude;
    var minLong = leftTopViewport.Longitude;
    var maxLat = rightDownViewport.Latitude;
    var maxLong = rightDownViewport.Longitude;
    
    var polyPointsStr = '';
    
    if (smModel.filterCoords.length > 2)
    {
        for( var ii = 0; ii < smModel.filterCoords.length; ii++)
        {
            if (smModel.filterCoords[ii].Latitude < minLat)
            {
                minLat = smModel.filterCoords[ii].Latitude;
            }
            else if (smModel.filterCoords[ii].Latitude > maxLat)
            {
                maxLat = smModel.filterCoords[ii].Latitude;
            }
            
            if (smModel.filterCoords[ii].Longitude < minLong)
            {
                minLong = smModel.filterCoords[ii].Longitude;
            }
            else if (smModel.filterCoords[ii].Longitude > maxLong)
            {   
                // liqian bug fixed
                //maxLong = smModel.filterCoords[ii].maxLong;
                maxLong = smModel.filterCoords[ii].Longitude;
            }
            
            polyPointsStr += smModel.filterCoords[ii].Latitude + ',' +
                smModel.filterCoords[ii].Longitude;
            polyPointsStr += (ii < (smModel.filterCoords.length - 1)) ? ',' : ''; 
        }
    }
    
    var pixelMin = smMap.LatLongToPixel(new VELatLong(minLat, minLong));
    var pixelMax = smMap.LatLongToPixel(new VELatLong(maxLat, maxLong));

    var latitudeRatioMin = (minLat - leftTopViewport.Latitude == 0) ?
                               0 : (pixelMin.y / (minLat - leftTopViewport.Latitude));
    var latitudeRatioMax = (maxLat - leftTopViewport.Latitude == 0) ?
                               latitudeRatioMin : (pixelMax.y / (maxLat - leftTopViewport.Latitude));
    latitudeRatioMin = (latitudeRatioMin == 0) ? latitudeRatioMax : latitudeRatioMin;
    
    var longitudeRatioMin = (minLong - leftTopViewport.Longitude == 0) ?
                               0 : (pixelMin.x / (minLong - leftTopViewport.Longitude));
    var longitudeRatioMax = (maxLong - leftTopViewport.Longitude == 0) ?
                               longitudeRatioMin : (pixelMax.x / (maxLong - leftTopViewport.Longitude));
    longitudeRatioMin = (longitudeRatioMin == 0) ? longitudeRatioMax : longitudeRatioMin;
    
    var latitudeRatio  = (latitudeRatioMin + latitudeRatioMax) / 2;
    var longitudeRatio = (longitudeRatioMin + longitudeRatioMax) / 2;
    
    // build polygon query consisting of:
    // 1. current viewport (the topleft lat/lon, the bottom left lat/lon, 
    //    the topright lat/lon, the bottom right lat/lon)
    // 2. the list of points in which we want to search for sensors
    // 3. the latitude ratio
    // 4. the longitude ratio
    // 5. the zoom level 
    // 6. the types of sensors to view
    var viewPort = leftTopViewport.Latitude + ',' + leftTopViewport.Longitude + ',' +
                     leftTopViewport.Latitude + ',' + rightDownViewport.Longitude + ',' +
                     rightDownViewport.Latitude + ',' + rightDownViewport.Longitude + ',' +
                     rightDownViewport.Latitude + ',' +  leftTopViewport.Longitude;
    var pointList = (smModel.filterCoords.length > 2) ? polyPointsStr : viewPort;
    var zoomLevel = smModel.zoom;
    var sensorTypes = '';
    for (var ii in sensorTypeListByUri)
    {
        if (smModel.filterTypes[ii] == 1)
        {
            // old icond
            //query.SensorTypes += Sensor.type[ii].typeURI + ';';
            // new icond
            sensorTypes += sensorTypeListByUri[ii].id + ';';
        }
    }
    if (sensorTypes.length > 1)
        sensorTypes = sensorTypes.substr(0, sensorTypes.length - 1);
    var searchTerm = smModel.filterTerms;

    var startTime = null;
    var endTime = null;
    if (smModel.timeSetting.inUse) {
        startTime = smModel.timeSetting.currentTime.toLocaleString();
        endTime = new Date(smModel.timeSetting.currentTime.getTime() + smModel.timeSetting.resolution * 1000).toLocaleString();
    }
    return new PolygonQuery(pointList, viewPort
        , latitudeRatio, longitudeRatio, zoomLevel
        , sensorTypes, searchTerm
        , startTime, endTime);
}

/*
 * SubmitSensorsRequest 
 *
 * Builds a sensor query and sends it to IconD web service to get all sensors
 * that belong on the map at this time.
 */
var cacheTimerId = 0;
function SubmitSensorsRequest(query)
{
    var time = new Date();
    if (cacheTimerId) {
        clearTimeout(cacheTimerId);
    }
    lastQuery = query;
    cacheTimerId = setTimeout('TimerFires()', 500);
}

function TimerFires() {
    var time = new Date();
    var query = lastQuery;
    if (!query) {
        query = PolygonQuery.create();
    }
    var passCode = GetPassCode();
    var userName = GetUserName();
    if (smModel.timeSetting.inUse) {
        query.startTime = smModel.timeSetting.currentTime.toLocaleString();
        query.endTime = new Date(smModel.timeSetting.currentTime.getTime() + smModel.timeSetting.resolution * 1000).toLocaleString();
        if (passCode.length > 0)
        SenseWeb.IconD.SensorGroupsInsidePolygonByTimeCSV2WithAuthentication(
            userName
            , passCode
            , query.pointList, query.viewPort
            , query.latitudeRatio, query.longitudeRatio, query.zoomLevel
            , query.sensorTypes, query.searchTerm
            , query.startTime
            , query.endTime
            , HandleIconize, HandleIconizeTimeout);
        else
        SenseWeb.IconD.SensorGroupsInsidePolygonByTimeCSV2(
            query.pointList, query.viewPort
            , query.latitudeRatio, query.longitudeRatio, query.zoomLevel
            , query.sensorTypes, query.searchTerm
            , query.startTime
            , query.endTime
            , HandleIconize, HandleIconizeTimeout);
    } else {
        if (passCode.length > 0){
            SenseWeb.IconD.SensorGroupsInsidePolygonCSV2WithAuthentication(
                userName
                , passCode
                , query.pointList, query.viewPort
                , query.latitudeRatio, query.longitudeRatio, query.zoomLevel
                , query.sensorTypes, query.searchTerm
                , HandleIconize, HandleIconizeTimeout);}
        else{
            SenseWeb.IconD.SensorGroupsInsidePolygonCSV2(
                query.pointList, query.viewPort
                , query.latitudeRatio, query.longitudeRatio, query.zoomLevel
                , query.sensorTypes, query.searchTerm
                , HandleIconize, HandleIconizeTimeout);}
    }
//    showDebugMessage(query.pointList);
//    appendDebugMessage(query.viewPort);
//    appendDebugMessage(query.latitudeRatio);
//    appendDebugMessage(query.longitudeRatio);
//    appendDebugMessage(query.zoomLevel);
//    appendDebugMessage(query.sensorTypes);
//    appendDebugMessage(query.searchTerm);
//    appendDebugMessage(query.startTime);
//    appendDebugMessage(query.endTime);
    // Indicate fetching sensors status
    lastQuery = query;
    setStatusLabel('Fetching sensors ...');
}

function showDebugMessage(message) {
    $('debugMsg').innerHTML = message + '<br />';
}
function appendDebugMessage(message) {
    $('debugMsg').innerHTML += message + '<br />';
}

function hideStatusLabel(msg) {
    document.getElementById('sensorStatusMessage').style.visibility = 'hidden';
}

function setStatusLabel(msg) {
    document.getElementById('sensorStatusMessage').style.visibility = 'visible';
    document.getElementById('sensorStatusMessage').innerHTML = msg;
}

/*
 * PutSensorPushpins 
 *
 * Puts sensors on map by iterating through the global array sensorsInViewport, 
 * getting each sensor's icon, and placing icon on the map as a VE pushpin.
 */
function PutSensorPushpins2()
{
    for (var i in sensorListByLatLon)
    {
        PutSensorPushpin2(i);
    }
}

function sortSensors(index1, index2) {
    var name1 = sensorsInViewport[index1].name;
    var name11 = name1.substring(0, indexOf('@'));
    var name12 = parseInt(name1.substring(indexOf('@')+1, name1.length));
    var name2 = sensorsInViewport[index2].name;
    var name21 = name2.substring(0, indexOf('@'));
    var name22 = parseInt(name2.substring(indexOf('@')+1, name1.length));
    if (name11 > name21)
        return 1;
    else if (name11 < name21)
        return -1;
    else {
        if (name12 > name22)
            return 1;
        else if (name12 < name22)
            return -1;
        else
            return 0;
   }
}

function PutSensorPushpin2(index)
{
    var sensorIndices = sensorListByLatLon[index];
    if (!sensorIndices || sensorIndices.length == 0) return;
    
    var sensor = sensorsInViewport[sensorIndices[0]];
    var sensorLocation = new VELatLong(sensor.latitude, sensor.longitude);
    var sensorIconURL = null;
    var sensorTitle = null;
    var sensorDetail = '';
    // if user is admin or published the sensor, give the user the ability
    // to edit and delete the sensor.
    //  alert(swClientID);
    var swID = GetUserLogin();
    var pin = new VEShape(VEShapeType.Pushpin, sensorLocation);

    if (sensorIndices.length > 1) {
        for (var i in sensorIndices) {
            sensor = sensorsInViewport[sensorIndices[i]];
            if (sensor.name.indexOf('@') == -1) {
                if (!sensorIconURL) {
                    sensorIconURL = sensor.getIcon();
                    sensorTitle = sensor.getTitle() + ' Detail';
                } else {
                    sensorIconURL = 'image/sensor/generic/generic_sensor_32.gif';
                    sensorTitle = 'Misc Sensor Detail';
                }
                sensorDetail += sensor.getDetailHTML();// + sensorDetail;
            } else {
                if (sensorDetail == '')
                    sensorDetail = sensor.getComponentMetaDetailHTML(); 
                sensorDetail += sensor.getComponentStateHTML();
            }
            sensorsInViewport[sensorIndices[i]].vePushpin = pin;
        }
        if (!sensorIconURL) {
            sensorIconURL = 'image/sensor/generic/generic_sensor_32.gif';
            sensorTitle = 'Misc Sensor Detail';
        }
    } else {
        var sensorIconURL;
        switch (smModel.vizType) {
            case VizTypes.rectangle:
                //var sensorIconURL = sensor.getValueIcon(min, max, resolution, colorMapIndex, isContinuous);
                sensorIconURL = sensor.getValueIcon(smModel.colorMapSetting.min
                    , smModel.colorMapSetting.max
                    , smModel.colorMapSetting.resolution
                    , smModel.colorMapSetting.colorMapIndex
                    , smModel.colorMapSetting.isContinuous);
                break;
            default: // icon
                sensorIconURL = sensor.getIcon();
                break;
        }
        sensorTitle = sensor.getTitle() + ' Detail';
        sensorDetail = sensor.getDetailHTML();
        sensorsInViewport[sensorIndices[0]].vePushpin = pin;
    }
    //pin.SetTitle(sensorTitle);
    pin.SetCustomIcon(sensorIconURL);
    pin.SetDescription(sensorDetail);
    sensorLayer.AddShape(pin);
    //smMap.AddShape(pin);
}

/*
 * RemoveSensorPushpins 
 *
 * Removes all sensor pushpins from the smMap.
 */
function RemoveSensorPushpins()
{
    /*
    for (var ss = 0; ss < sensorsInViewport.length; ss++)
    {
        var sensorPinID = 's' + ss;
        smMap.DeletePushpin(sensorPinID);
    }
    */
    sensorLayer.DeleteAllShapes();
}
