/**
 * @author Alex
 */
var TRAIL_DOWNLOAD = 1;
var TRAIL_DOWNLOADING = 2;
var TRAIL_CLEAR = 3;

var STR_UNREGISTERED_USER = "Unregistered User";
var STR_TRAIL_LOADING = "Trail loading";
var STR_HIDE_TRAIL = "Hide Trail";
var STR_SHOW_TRAIL = "Show Trail";
var STR_LAST_UPDATE = "Last update:";
var STR_LAST_ALTITUDE = "Altitude:";
var STR_METERS_SYMBOL = "m"; // e.g. "Alititude: 341m";
if (!window.WMPG) 
    WMPG = {};

WMPG.model = {
    downloadPublicUsers: function(){
        if ($('show_users_radio_button').checked) {
            return true;
        }
        else {
            return false;
        }
    },
    clearSearch: function(){
        $('search_text').value = "";
    },
    infoWindowNode: function(){
        return $('info_window_node');
    }
};

WMPG.controller = {
    addListeners: function(){
        Event.observe($('hide_trail'), 'click', WMPG.trailController.clearTrail);
    }
};

WMPG.trailView = {
    showTrailView: function(show){
        if (show) {
            $('trail').style.display = 'block';
        }
        else {
            $('trail').style.display = 'none';
        }
    },
    showTrailLoading: function(show){
        if (show) {
            $('trail_loading').style.display = 'block';
        }
        else {
            $('trail_loading').style.display = 'none';
        }
    },
    showHideTrail: function(show){
        if (show) {
            $('hide_trail').style.display = 'block';//.style.visibility='visible';
        }
        else {
            $('hide_trail').style.display = 'none';//.style.visibility='hidden';
        }
    },
    showTrailInfo: function(show){
        if (show) {
            //$('trail_info').style.display='block';
            $('trail_info').style.visibility = 'visible';
        }
        else {
            //$('trail_info').style.display='none';
            $('trail_info').style.visibility = 'hidden';
        }
    },
    updateTrailInfo: function(trailArrayIterator){
        if (trailArrayIterator.trailArray.length > 0) {
            startDate = new Date;
            startDate.setTime(trailArrayIterator.oldest().time_stamp * 1000);
            
            finishDate = new Date;
            finishDate.setTime(trailArrayIterator.newest().time_stamp * 1000);
            
            //$('trail').style.visibility='visible';
            $('trail_start').innerHTML = startDate;
            $('trail_finish').innerHTML = finishDate;
        }
    },
    enablePrevious: function(enable){
        if (enable) {
            Event.observe($('previous'), 'click', WMPG.trailController.downloadPreviousTrail.bindAsEventListener(WMPG.trailController));
            $('previous').style.textDecoration = 'underline';
        }
        else {
            Event.stopObserving($('previous'), 'click', WMPG.trailController.downloadPreviousTrail.bindAsEventListener(WMPG.trailController));
            $('previous').style.textDecoration = 'none';
        }
    },
    enableNext: function(enable){
        if (enable) {
            Event.observe($('next'), 'click', WMPG.trailController.downloadNextTrail.bindAsEventListener(WMPG.trailController));
            $('next').style.textDecoration = 'underline';
        }
        else {
            Event.stopObserving($('next'), 'click', WMPG.trailController.downloadNextTrail.bindAsEventListener(WMPG.trailController));
            $('next').style.textDecoration = 'none';
        }
    }
};

WMPG.userView = {
    handeUsersChanged: function(userArray){
    
        this.showSearch(userArray && userArray.pub);
        
        this.showOtherUsers(userArray && userArray.pri);
        
        this.clearUserNames(false);
        this.clearUserNames(true);
        
        if (userArray && userArray.pub && userArray.pub.length) {
            el = $('public_users_div');
            this.addUserNames(userArray.pub, el);
        }
        if (userArray && userArray.pri && userArray.pri.length) {
            el = $('private_users_div');
            this.addUserNames(userArray.pri, el);
        }
    },
    showHideSearch: function(userArray){
        if (userArray && userArray.pub) {
            if (!$('search_div').hasChildNodes()) {
                $('search_div').appendChild($('search_form'));
            }
        }
        else {
            $('hidden_div').appendChild($('search_form'));
        }
    },
    showSearch: function(show){
        if (show) {
            $('search_form').style.display = 'block';
        }
        else {
            $('search_form').style.display = 'none';
        }
    },
    showOtherUsers: function(show){
        if (show) {
            $('show_others_form').style.display = 'block';
        }
        else {
            $('show_others_form').style.display = 'none';
        }
    },
    removeOutOfBounds: function(bounds){
        this.doRemoveOutOfBounds($('private_users_div'), bounds);
        this.doRemoveOutOfBounds($('public_users_div'), bounds);
    },
    doRemoveOutOfBounds: function(el, bounds){
        var current = el.firstChild;
        if (current) {
            while (next = current.nextSibling) {
                if (!bounds.containsLatLng(new GLatLng(current.user.lat, current.user.lng))) {
                    el.removeChild(current);
                }
                current = next;
            }
            el.removeChild(current);
        }
    },
    clearUserNames: function(privateUser){
        var elementId = "";
        if (privateUser) {
            elementId = 'private_users_div';
        }
        else {
            elementId = 'public_users_div';
        }
        while ($(elementId).firstChild) {
            $(elementId).removeChild($(elementId).firstChild);
        };
        this.userElements = new Hash();
    },
    addUserNames: function(array, parent){
        for (i = 0; i < array.length; i++) {
            if (array[i].lat) {
                var el = document.createElement('div');
                el.user = array[i];
                this.userElements.set(array[i].id.toString(), el);
                this.updateUserNameElement(el);
                parent.appendChild(el);
            }
        }
    },
    updateUserNameElement: function(el){
        user = el.user;
        userName = "Unregistered User";
        if (user.name) {
            userName = user.name;
        }
        userNameLink = "<a href=\"javascript:void(0)\">" + userName + "</a>";
        
        el.innerHTML = userNameLink;
        if (user.tp) {
            Event.observe(el, 'click', function(event){
                WMPG.trailController.downloadTrail(this.user);
            });
        }
        Event.observe(el, 'mouseover', function(event){
            WMPG.map.openUserInfoWindow(this.user);
        });
    }
};

var descTrailInterator = Class.create({
    initialize: function(trailArray){
        this.trailArray = trailArray;
        this.reset();
    },
    reset: function(){
        this.current = 0;
    },
    oldest: function(){
        return this.trailArray[0];
    },
    newest: function(){
        return this.trailArray[this.trailArray.length - 1];
    },
    next: function(){
        if (this.current < this.trailArray.length) {
            var item = this.trailArray[this.current];
            this.current++;
            return item;
        }
        else {
            return null;
        }
    }
});

var ascTrailInterator = Class.create({
    initialize: function(trailArray){
        this.trailArray = trailArray;
        this.reset();
    },
    reset: function(){
        this.current = this.trailArray.length - 1;
    },
    oldest: function(){
        return this.trailArray[this.trailArray.length - 1];
    },
    newest: function(){
        return this.trailArray[0];
    },
    next: function(){
        if (this.current > -1) {
            var item = this.trailArray[this.current];
            this.current--;
            return item;
        }
        else {
            return null;
        }
    }
});


var INITAL_DOWNLOAD = 1;
var NEXT_DOWNLOAD = 2;
var PREVIOUS_DOWNLOAD = 3;

WMPG.trailController = {
    downloadingTrail: false,
    perPage: 5,
    page: 1,
    initalDownload: true,
    setViewForDownload: function(){
        WMPG.trailView.showTrailView(true);
        WMPG.trailView.showTrailLoading(true);
        WMPG.trailView.showHideTrail(false);
        WMPG.trailView.showTrailInfo(false);
    },
    downloadTrail: function(user){
        this.state = INITAL_DOWNLOAD;
        this.handsetId = user.handset_id;
        var options = {
            parameters: {}
        };
        
        this.doDownloadTrail(options);
    },
    downloadNextTrail: function(){
        this.state = NEXT_DOWNLOAD;
        this.page--;
        var options = {
            parameters: {}
        };
        
        this.doDownloadTrail(options);
    },
    downloadPreviousTrail: function(){
        this.state = PREVIOUS_DOWNLOAD;
        this.page++;
        var options = {
            parameters: {}
        };
        
        this.doDownloadTrail(options);
    },
    clearTrail: function(event){
        WMPG.map.clearTrailOverlay();
        WMPG.trailView.showTrailView(false);
        this.trailIterator = null;
        this.trailArray = null;
    },
    doDownloadTrail: function(options){
        if (!this.downloadingTrail) {
            this.setViewForDownload();
            this.downloadingTrail = true;
            options.parameters.per_page = this.perPage;
            options.parameters.page = this.page;
            options.parameters.handset_id = this.handsetId, options.onSuccess = function(t){
                WMPG.trailController.handleTrailDownload(t);
            };
            options.onFailure = function(){
                WMPG.trailController.handleTransportError();
            };
            options.onlyLatestOfClass = 'trail';
            options.method = 'get';
            url = "/locations.json";
            new Ajax.Request(url, options);
            
        }
    },
    handleTrailDownload: function(transport){
        this.downloadingTrail = false;
        WMPG.map.clearTrailOverlay();
        WMPG.trailView.showTrailView(true);
        
        this.trailArray = transport.responseJSON.trail;
        
        
        if (this.trailArray.length > 2 &&
        (this.trailArray[0].time_stamp > this.trailArray[1].time_stamp)) {
            this.trailIterator = new ascTrailInterator(this.trailArray);
        }
        else {
            this.trailIterator = new descTrailInterator(this.trailArray);
        }
        
        WMPG.map.setBoundsToArray(this.trailArray);
        
        WMPG.trailView.updateTrailInfo(this.trailIterator);
        
        WMPG.trailView.showHideTrail(true);
        
        WMPG.trailView.showTrailLoading(false);
        
        WMPG.trailView.showTrailInfo(true);
        
        var length = this.trailArray.length;
        if (this.state == INITAL_DOWNLOAD) {
            WMPG.trailView.enablePrevious(this.perPage == length);
            WMPG.trailView.enableNext(false);
        }
        else 
            if (this.state == NEXT_DOWNLOAD) {
                WMPG.trailView.enablePrevious(true);
                WMPG.trailView.enableNext(this.page != 1);
            }
            else 
                if (this.state = PREVIOUS_DOWNLOAD) {
                    WMPG.trailView.enablePrevious(this.perPage == length);
                    WMPG.trailView.enableNext(true);
                }
        
        
        var latLngArrays = this.createLatLngArrays(this.trailIterator);
        WMPG.map.drawTrailMarkers(this.trailIterator);
        WMPG.map.drawTrails(latLngArrays, this.trailArray.length);
        WMPG.userView.removeOutOfBounds(WMPG.map.map.getBounds());
    },
    createLatLngArrays: function(trailIterator){
    
        var currentTrail = [];
        
        var latLngArrays = [];
        
        if (trailIterator.trailArray.length > 0) {
        
            var item = null;
            while (item = trailIterator.next()) {
            
                currentTrail.push(new GLatLng(item.lat, item.lng));
            }
        }
        
        if (currentTrail.length > 0) {
            latLngArrays.push(currentTrail);
        }
        
        return latLngArrays;
    },
    handleTransportError: function(){
        this.downloadingTrail = false;
    }
};

WMPG.userController = {
    setBounds: true,
    userReloadByBoundsOustanding: false,
    userReloadByNameOustanding: false,
    downloadingUsers: false,
    downloadUsers: function(){
        if (!this.downloadingUsers) {
            this.downloadingUsers = true;
            this.setBounds = true;
            url = "/handsets.json";
            new Ajax.Request(url, {
                method: 'get',
                onSuccess: function(t){
                    WMPG.userController.handleUserDownload(t)
                },
                onFailure: function(){
                    WMPG.userController.handleTransportError()
                },
                onlyLatestOfClass: 'users'
            });
        }
    },
    handleUserDownload: function(transport){
        this.downloadingUsers = false;
        this.userArray = transport.responseJSON;
        
        if (this.setBounds) {
            if (this.userArray.pub && this.userArray.pub.length) {
                WMPG.map.setBoundsToArray(this.userArray.pub);
            }
            else 
                if (this.userArray.pri && this.userArray.pri.length) {
                    WMPG.map.setBoundsToArray(this.userArray.pri);
                }
            
        }
        WMPG.map.updateUserMarkers(this.userArray);
        
        WMPG.userView.handeUsersChanged(this.userArray);
        
        if (this.setBounds) {
            this.setBounds = false;
        }
    },
    handleTransportError: function(){
        this.setBounds = false;
        this.userReloadByBoundsOustanding = false;
        this.userReloadByNameOustanding = false;
        this.downloadingUsers = false;
    }
};

WMPG.map = {
    setupMap: function(){
    
        this.map = new GMap2($('map_canvas'));
        var BOUNDS_ALL = new GLatLngBounds(new GLatLng(-90, -180), new GLatLng(90, 180));
        
        
        // -- MAP TYPES --
        
        var errmsg = "We are sorry, but we don't<br>have imagery at this zoom<br>level for this region.<br><br>Try zooming out for a<br>broader look.";
        
        // China overlay
        var dituCopyright = new GCopyright(1, BOUNDS_ALL, 0, "Map data &copy;2007 Mapabc.com");
        var dituCopyrightCollection = new GCopyrightCollection("");
        dituCopyrightCollection.addCopyright(dituCopyright);
        
        var dituLayer = new GTileLayer(dituCopyrightCollection, 0, 17);
        dituLayer.getTileUrl = function(tile, zoom){
            return "http://servicetile.mapabc.com/googlechina/maptile?v=w2.66&x=" +
            tile.x +
            "&y=" +
            tile.y +
            "&zoom=" +
            (17 - zoom);
        };
        dituMap = new GMapType([dituLayer], new GMercatorProjection(23), "Ditu", {
            shortName: "Ditu",
            alt: "Show maps from Google China (Ditu)",
            errorMessage: errmsg
        });
        
        var googleMapLayer = G_NORMAL_MAP.getTileLayers()[0];
        var googleMap = new GMapType([googleMapLayer], new GMercatorProjection(23), "Google Map", {
            shortName: "GoogleMap",
            alt: "Show maps from Google Maps",
            errorMessage: errmsg
        });
        this.map.addMapType(googleMap);
        
        this.map.addMapType(dituMap);
        
        // Microsoft overlay
        var msCopyright1 = new GCopyright(2, BOUNDS_ALL, 0, "&nbsp;&nbsp;&copy; 2007 NAVTEQ");
        var msCopyright2 = new GCopyright(3, BOUNDS_ALL, 10, "&nbsp;&nbsp;&copy; 2007 NAVTEQ&nbsp;&nbsp;&copy; 2007 AND");
        var msCopyrightCollection = new GCopyrightCollection("&copy; 2007 Microsoft Corporation");
        msCopyrightCollection.addCopyright(msCopyright1);
        msCopyrightCollection.addCopyright(msCopyright2);
        
        function getMSURL(tile, zoom, type, fmt, extra){
            var digit = ((tile.y & 1) << 1) + (tile.x & 1);
            var ret = "http://" + type + digit + ".ortho.tiles.virtualearth.net/tiles/" + type;
            for (var i = zoom - 1; i >= 0; i--) {
                ret += ((((tile.y >> i) & 1) << 1) + ((tile.x >> i) & 1));
            }
            ret += "." + fmt + "?g=110" + extra;
            return ret;
        }
        
        var msMapLayer = new GTileLayer(msCopyrightCollection, 1, 19);
        msMapLayer.getTileUrl = function(tile, zoom){
            return getMSURL(tile, zoom, "r", "png", "");
        };
        var msMap = new GMapType([msMapLayer], new GMercatorProjection(23), "MS Map", {
            shortName: "MS Map",
            alt: "Show road maps from Microsoft Live Search",
            errorMessage: errmsg
        });
        this.map.addMapType(msMap);
        
        var msBrCopyright = new GCopyright(6, BOUNDS_ALL, 0, "&nbsp;&nbsp;&copy; 2007 MapLink");
        var msBrCopyrightCollection = new GCopyrightCollection("&copy; 2007 Microsoft Corporation");
        msBrCopyrightCollection.addCopyright(msBrCopyright);
        
        var msBrMapLayer = new GTileLayer(msBrCopyrightCollection, 1, 19);
        msBrMapLayer.getTileUrl = function(tile, zoom){
            var digit = ((tile.y & 1) << 1) + (tile.x & 1) + 1;
            var ret = "http://img" + digit + ".maplink3.com.br/MapaVE.aspx?v=r|" + digit + "|r|";
            for (var i = zoom - 1; i >= 0; i--) {
                ret += ((((tile.y >> i) & 1) << 1) + ((tile.x >> i) & 1));
            }
            ret += "|png|66";
            return ret;
        };
        var msBrMap = new GMapType([msBrMapLayer], new GMercatorProjection(23), "MS Brasil Map", {
            shortName: "MS Brasil Map",
            alt: "Show road maps from Microsoft Live Brasil",
            errorMessage: errmsg
        });
        this.map.addMapType(msBrMap);
        
        // Yahoo overlay
        var yCopyright = new GCopyright(4, BOUNDS_ALL, 0, "&nbsp;&nbsp;Data &copy; 2007 Navteq, TeleAtlas");
        var yCopyrightCollection = new GCopyrightCollection("&copy; 2007 Yahoo Inc.");
        yCopyrightCollection.addCopyright(yCopyright);
        
        function getYURL(tile, zoom, baseUrl){
            var ret = baseUrl;
            ret += "&x=" + tile.x + "&y=" + (((1 << zoom) >> 1) - 1 - tile.y) + "&z=" + (18 - zoom);
            return ret;
        }
        
        var yMapLayer = new GTileLayer(yCopyrightCollection, 1, 17);
        yMapLayer.getTileUrl = function(tile, zoom){
            return getYURL(tile, zoom, "http://png.maps.yimg.com/png?t=m&v=4.1&s=256&f=j");
        };
        var yMap = new GMapType([yMapLayer], new GMercatorProjection(23), "Yahoo Map", {
            shortName: "Yahoo Map",
            alt: "Show road maps from Yahoo",
            errorMessage: errmsg
        });
        this.map.addMapType(yMap);
        
        var yInCopyright = new GCopyright(5, BOUNDS_ALL, 0, "&nbsp;&nbsp;Data &copy; 2007 CE Info Systems");
        var yInCopyrightCollection = new GCopyrightCollection("&copy; 2007 Yahoo Inc.");
        yInCopyrightCollection.addCopyright(yInCopyright);
        
        var yInMapLayer = new GTileLayer(yInCopyrightCollection, 1, 17);
        yInMapLayer.getTileUrl = function(tile, zoom){
            return getYURL(tile, zoom, "http://tile.in.maps.yahoo.com/tile?imgtype=png&v=0.96");
        };
        var yInMap = new GMapType([yInMapLayer], new GMercatorProjection(23), "Yahoo India Map", {
            shortName: "Yahoo India Map",
            alt: "Show road maps from Yahoo India",
            errorMessage: errmsg
        });
        this.map.addMapType(yInMap);
        
        // start osm
        var copyOSM = new GCopyrightCollection("<a href=\"http://www.openstreetmap.org/\">OpenStreetMap</a>");
        copyOSM.addCopyright(new GCopyright(1, new GLatLngBounds(new GLatLng(-90, -180), new GLatLng(90, 180)), 0, " "));
        
        var tilesMapnik = new GTileLayer(copyOSM, 1, 17, {
            tileUrlTemplate: 'http://tile.openstreetmap.org/{Z}/{X}/{Y}.png'
        });
        var tilesOsmarender = new GTileLayer(copyOSM, 1, 17, {
            tileUrlTemplate: 'http://tah.openstreetmap.org/Tiles/tile.php/{Z}/{X}/{Y}.png'
        });
        var googleMapLayer = G_NORMAL_MAP.getTileLayers()[0];
        
        var mapMapnik = new GMapType([tilesMapnik], G_NORMAL_MAP.getProjection(), "Mapnik");
        var mapOsmarender = new GMapType([tilesOsmarender], G_NORMAL_MAP.getProjection(), "Osmarend");
        
        this.map.addMapType(mapMapnik);
        this.map.addMapType(mapOsmarender);
        
        // end osm
        var googleSatLayer = G_HYBRID_MAP.getTileLayers()[0];
        var googleSat = new GMapType([googleSatLayer], new GMercatorProjection(23), "Google Sat", {
            shortName: "GoogleSat",
            alt: "Show satellite imagery from Google Maps",
            textColor: "white",
            linkColor: "white",
            errorMessage: errmsg
        });
        this.map.addMapType(googleSat);
        
        var msSatLayer = new GTileLayer(msCopyrightCollection, 1, 19);
        msSatLayer.getTileUrl = function(tile, zoom){
            return getMSURL(tile, zoom, "a", "jpeg", "");
        };
        var msSat = new GMapType([msSatLayer], new GMercatorProjection(23), "MS Sat", {
            shortName: "MS Sat",
            alt: "Show aerial imagery from Microsoft Live Search",
            textColor: "white",
            linkColor: "white",
            errorMessage: errmsg
        });
        this.map.addMapType(msSat);
        
        var ySatLayer = new GTileLayer(yCopyrightCollection, 1, 17);
        ySatLayer.getTileUrl = function(tile, zoom){
            return getYURL(tile, zoom, "http://aerial.maps.yimg.com/ximg?t=a&v=1.7&s=256");
        };
        var ySat = new GMapType([ySatLayer], new GMercatorProjection(23), "Yahoo Sat", {
            shortName: "Yahoo Sat",
            alt: "Show aerial imagery from Yahoo Maps",
            textColor: "white",
            linkColor: "white",
            errorMessage: errmsg
        });
        this.map.addMapType(ySat);
        
        var googleHybLayer = G_HYBRID_MAP.getTileLayers()[1];
        var googleHyb = new GMapType([googleSatLayer, googleHybLayer], new GMercatorProjection(23), "Google Hyb", {
            shortName: "GoogleHyb",
            alt: "Show imagery with street names from Google Maps",
            textColor: "white",
            linkColor: "white",
            errorMessage: errmsg
        });
        this.map.addMapType(googleHyb);
        
        var msHybLayer = new GTileLayer(msCopyrightCollection, 1, 19);
        msHybLayer.getTileUrl = function(tile, zoom){
            return getMSURL(tile, zoom, "h", "jpeg", "");
        };
        var msHyb = new GMapType([msHybLayer], new GMercatorProjection(23), "MS Hyb", {
            shortName: "MS Hyb",
            alt: "Show aerial imagery and street names from Microsoft Live Search",
            textColor: "white",
            linkColor: "white",
            errorMessage: errmsg
        });
        this.map.addMapType(msHyb);
        
        var yHybLayer = new GTileLayer(yCopyrightCollection, 1, 17);
        yHybLayer.getTileUrl = function(tile, zoom){
            return getYURL(tile, zoom, "http://aerial.maps.yimg.com/tile?t=p&v=2.5");
        };
        var yHyb = new GMapType([ySatLayer, yHybLayer], new GMercatorProjection(23), "Yahoo Hyb", {
            shortName: "Yahoo Hyb",
            alt: "Show aerial imagery and road maps from Yahoo Maps",
            textColor: "white",
            linkColor: "white",
            errorMessage: errmsg
        });
        this.map.addMapType(yHyb);
        
        var yInHybLayer = new GTileLayer(yInCopyrightCollection, 1, 17);
        yInHybLayer.getTileUrl = function(tile, zoom){
            return getYURL(tile, zoom, "http://aerial.in.maps.yahoo.com/tile?imgtype=png&v=0.93");
        };
        var yInHyb = new GMapType([ySatLayer, yInHybLayer], new GMercatorProjection(23), "Yahoo India Hyb", {
            shortName: "Yahoo India Hyb",
            alt: "Show aerial imagery and road maps from Yahoo India Maps",
            textColor: "white",
            linkColor: "white",
            errorMessage: errmsg
        });
        this.map.addMapType(yInHyb);
        
        this.map.addMapType(G_PHYSICAL_MAP);
        
        var googlePhysLayer = G_PHYSICAL_MAP.getTileLayers()[0];
        var googlePhys = new GMapType([googlePhysLayer], new GMercatorProjection(23), "Google Terrain", {
            shortName: "GooglePhys",
            alt: "Show street map with terrain from Google Maps",
            errorMessage: errmsg
        });
        this.map.addMapType(googlePhys);
        
        var msPhysLayer = new GTileLayer(msCopyrightCollection, 1, 19);
        msPhysLayer.getTileUrl = function(tile, zoom){
            return getMSURL(tile, zoom, "r", "png", "&shading=hill");
        };
        var msPhys = new GMapType([msPhysLayer], new GMercatorProjection(23), "MS Terrain", {
            shortName: "MS Terrain",
            alt: "Show road maps with terrain shading from Microsoft Live Search",
            errorMessage: errmsg
        });
        this.map.addMapType(msPhys);
        
        // 3D (Google Earth 3D satellite)
        this.map.addMapType(G_SATELLITE_3D_MAP);
        
        var hier = new GHierarchicalMapTypeControl();
        hier.clearRelationships();
        hier.addRelationship(G_NORMAL_MAP, googleMap, "Google Maps", true);
        hier.addRelationship(G_NORMAL_MAP, dituMap, "Google China Maps", false);
        hier.addRelationship(G_NORMAL_MAP, msMap, "Microsoft Live Road Maps", false);
        hier.addRelationship(G_NORMAL_MAP, msBrMap, "Microsoft Brasil Road Maps", false);
        hier.addRelationship(G_NORMAL_MAP, yMap, "Yahoo Maps", false);
        hier.addRelationship(G_NORMAL_MAP, yInMap, "Yahoo India Maps", false);
        hier.addRelationship(G_NORMAL_MAP, mapMapnik, "OpenStreetMap Mapnik", false);
        hier.addRelationship(G_NORMAL_MAP, mapOsmarender, "OpenStreetMapnik OSM Renderer", false);
        hier.addRelationship(G_SATELLITE_MAP, googleSat, "Google Satellite", true);
        hier.addRelationship(G_SATELLITE_MAP, msSat, "Microsoft Live Aerial Maps", false);
        hier.addRelationship(G_SATELLITE_MAP, ySat, "Yahoo Satellite", false);
        hier.addRelationship(G_HYBRID_MAP, googleHyb, "Google Hybrid", true);
        hier.addRelationship(G_HYBRID_MAP, msHyb, "Microsoft Live Hybrid Maps", false);
        hier.addRelationship(G_HYBRID_MAP, yHyb, "Yahoo Hybrid", false);
        hier.addRelationship(G_HYBRID_MAP, yInHyb, "Yahoo India Hybrid", false);
        hier.addRelationship(G_PHYSICAL_MAP, googlePhys, "Google Terrain", true);
        hier.addRelationship(G_PHYSICAL_MAP, msPhys, "Microsoft Live Terrain", false);
        
        this.map.addControl(hier, new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10, 25)));
        this.map.addControl(new GScaleControl());
        this.map.addControl(new GLargeMapControl(), new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10, 90)));
        this.map.addControl(new GOverviewMapControl());
        
        this.moveEndEventHandle = GEvent.addListener(this.map, "moveend", function(){
            WMPG.map.handleMoveEnd();
        });
        
        new Form.Element.Observer('search_text', 0.5, function(element, value){
            WMPG.userController.downloadUsersByName(value);
        });
        
        new Form.Element.Observer('show_users_radio_button', 0.5, function(element, value){
            WMPG.userController.setBounds = true;
            if (!WMPG.model.downloadPublicUsers()) {
                WMPG.map.removePublicUserMarkers();
            }
            WMPG.userController.downloadUsersByBounds(WMPG.map.map.getBounds());
        });
        
        this.publicUserMarkers = new Hash();
        this.privateUserMarkers = new Hash();
        this.map.setCenter(new GLatLng(0, 0), 2);
        this.map.setMapType(googleMap);
        this.trailOverlays = [];
        //$('trail').style.hidden = true;
        this.createTrailMarkerIcon();
    },
    createTrailMarkerIcon: function(){
        this.trailMarkerIcon = new GIcon();
        this.trailMarkerIcon.image = "/images/marker.gif";
        this.trailMarkerIcon.iconSize = new GSize(6, 6);
        this.trailMarkerIcon.iconAnchor = new GPoint(3, 3);
        this.trailMarkerIcon.infoWindowAnchor = new GPoint(3, 3);
    },
    handleMoveEnd: function(){
    },
    setBoundsToArray: function(array){
        bounds = new GLatLngBounds;
        for (var i = 0; i < array.length; i++) {
            if (array[i].lat) {
                bounds.extend(new GLatLng(array[i].lat, array[i].lng));
            }
        }
        center = bounds.getCenter();
        zoomLevel = this.map.getBoundsZoomLevel(bounds);
        this.map.setCenter(center, zoomLevel);
    },
    handleTransportError: function(){
        this.userReloadByBoundsOustanding = false;
        this.userReloadByNameOustanding = false;
        this.downloadingUsers = false;
    },
    removePublicUserMarkers: function(){
        this.publicUserMarkers.each(function(pair){
            WMPG.map.removeOverlay(pair.value);
        });
        this.publicUserMarkers = new Hash();
    },
    createMarker: function(user){
        latlng = new GLatLng(user.lat, user.lng);
        var marker = new GMarker(latlng, {
            icon: G_DEFAULT_ICON
        });
        marker.user = user;
        GEvent.addListener(marker, "click", function(){
            WMPG.map.openUserInfoWindow(this.user);
        });
        return marker;
    },
    updateUserMarkers: function(userArray){
        if (userArray.pub && userArray.pub.length) {
            this.doUpdateUserMarkers(userArray.pub, this.publicUserMarkers);
        }
        if (userArray.pri && userArray.pri.length) {
            this.doUpdateUserMarkers(userArray.pri, this.privateUserMarkers);
        }
    },
    doUpdateUserMarkers: function(userArray, markersHash){
        for (i = 0; i < userArray.length; i++) {
            user = userArray[i];
            if (user.lat) {
            
                marker = markersHash.get(user.id.toString());
                
                if (marker) {
                    latlng = marker.getLatLng();
                    if (!(latlng.lat() == user.lat && latlng.lng() == user.lng)) {
                        // update marker
                        markersHash.unset(user.id.toString());
                        this.removeOverlay(marker);
                        marker = this.createMarker(user);
                        markersHash.set(user.id.toString(), marker);
                        this.addOverlay(marker);
                    }
                    
                }
                else {
                    marker = this.createMarker(user);
                    markersHash.set(user.id.toString(), marker);
                    this.addOverlay(marker);
                }
                
            }
        }
        
    },
    openUserInfoWindow: function(user){
        date = new Date;
        date.setTime(user.time_stamp * 1000);
        userName = "Unregistered User";
        if (user.name) {
            userName = user.name;
        }
        if (WMPG.trailController.userId == user.id) {
            if (WMPG.trailController.downloadingTrail) {
            
            }
        }
        
        if (!this.infoWindowNode) {
            this.infoWindowNode = WMPG.model.infoWindowNode();
        }
        
        
        x = this.infoWindowNode.firstChild;
        
        while (x) {
            if (x.id == "user_name") {
                x.innerHTML = userName + " ";
            }
            if (x.id == "show_trail") {
                if (user.tp) {
                    x.style.visibility = 'visible';
                    x.user = user;
                    Event.observe(x, 'click', function(){
                        WMPG.trailController.downloadTrail(this.user);
                    });
                }
                else {
                    x.style.visibility = 'hidden';
                    Event.stopObserving(el, 'click', WMPG.trailController.downloadTrail);
                }
            }
            if (x.id == "last_update") {
                x.innerHTML = STR_LAST_UPDATE + " " + date + " ";
            }
            if (x.id == "altitude") {
                x.innerHTML = STR_LAST_ALTITUDE + user.alt + STR_METERS_SYMBOL;
            }
            
            x = x.nextSibling;
        }
        this.map.openInfoWindow(new GLatLng(user.lat, user.lng), this.infoWindowNode);
    },
    addOverlay: function(overlay){
        this.map.addOverlay(overlay);
    },
    removeOverlay: function(overlay){
        this.map.removeOverlay(overlay);
    },
    
    getColorString: function(pos, count){
        color = ((count - pos) / count) * 255 | 0;
        colorString = color.toString(16);
        
        if (colorString.length == 1) {
            colorString = "0" + colorString;
        }
        
        return "#" + colorString + "0000";
    },
    drawTrailMarkers: function(trailIterator){
        trailIterator.reset();
        var item = null;
        while ((item = trailIterator.next()) != null) {
            var marker = new GMarker(new GLatLng(item.lat, item.lng), {
                icon: this.trailMarkerIcon
            })
            this.addOverlay(marker);
        }
    },
    drawTrails: function(latLngArrays, totalNumberOfPoints){
        this.trailOverlays = [];
        numberOfShades = 10;
        pointsPerShadeCount = (totalNumberOfPoints / numberOfShades) | 0;
        pointsPerShade = pointsPerShadeCount;
        var pointCount = 0;
        for (var i = latLngArrays.length - 1; i > -1; i--) {
            if (latLngArrays[i].length > 1) {
                arrayStartIndex = totalNumberOfPoints - 1;
                for (var j = latLngArrays[i].length - 1; j > -1; j--) {
                    pointCount++;
                    breakLine = false;
                    if (pointsPerShade-- == 0) {
                        breakLine = true;
                        pointsPerShade = pointsPerShadeCount;
                    }
                    if (breakLine || j == 0) {
                        colorString = this.getColorString(pointCount, totalNumberOfPoints);
                        
                        drawArray = latLngArrays[i].slice(j, arrayStartIndex + 1);
                        var polyLine = new GPolyline(drawArray, colorString, 4, 0.7);
                        this.trailOverlays.push(polyLine);
                        this.map.addOverlay(polyLine);
                        
                        /*for (var k = 0; k < drawArray.length; k++) {
                         marker = new GMarker(drawArray[k]);
                         this.map.addOverlay(marker);
                         }*/
                        arrayStartIndex = j;
                    }
                }
                
                
                //this.map.addOverlay(new GPolyline(latLngArrays[i], "#FF0000", 4, 0.7));
            }
            else {
                //marker = new GMarker(latLngArrays[i][0]);
                //this.map.addOverlay(marker);
            }
        }
    },
    clearTrailOverlay: function(){
        this.trailArray = [];
        this.trailOverlays.each(function(el){
            WMPG.map.map.removeOverlay(el);
        })
    },
    drawTrail: function(trailArray){
        var points = [];
        for (var i = trailArray.length - 1; i > -1; --i) {
            update = trailArray[i];
            points.push(new GLatLng(update.lat, update.lng));
        }
        polyLine = new GPolyline(points, "#FF0000", 4, 0.7);
        this.map.addOverlay(polyLine);
        this.trailOverlays.push(polyLine);
    }
};

