
/**
 * the ConnectNearMe class generates and manages a google
 * Map object and uses geolocation (if approved) to watch
 * the visitors position (also movement) and identify
 * potentially near users.
 **/
function ConnectNearMe()
{
    this.map            = null;
    this.infowindow     = null;
    this.coords         = {};
    this.updateInterval = null;
    this.myPosMarker    = null;
    this.userMarkers    = [];
    this.autoComplete   = null;
    this.spiderAround   = null;

    this.markerIcons = {
        currentPosition: "static/images/google_maps_icon/me-marker.png",
        unloggedUser: "static/images/google_maps_icon/inactive-marker.png",
        loggedUser: "static/images/google_maps_icon/watch-blu.png",
        pulledOver: "static/images/google_maps_icon/yellow-marker.png",
        alertUser: "static/images/google_maps_icon/RedDot.gif"
    }

    /**
     * register a watchPosition interval. This function can be
     * used in pages where a Google Map is available. It will
     * get the visitor's current position by geolocation and
     * watch it move + update the marker when movement is
     * registered.
     *
     * @return ConnectNearMe
     **/
    this.initialize = function()
    {
        var self = this;

        // get current position, then create map and center.
        this.watchMyPosition(function(obj)
        {
            coordinates = obj.getCoordinates();
            current_pos = new google.maps.LatLng(coordinates.latitude, coordinates.longitude);

            if (obj.map != null) {
                obj.updateMyMarker(coordinates, true);
                return false;
            }

            // first time map creation.
            obj.map = new google.maps.Map(document.getElementsByClassName('map-canvas')[0], {
                center: current_pos,
                zoom: 12
            });

            obj.initSpider();

            icon = obj.markerIcons.currentPosition;
            html = $("#my-marker").html();
            obj.myPosMarker = obj.createMarker(current_pos, html, icon, "My Position");

            // also update the trigger_location_type button to be sure
            // that pressing it will go to the right current position that
            // we just updated.
            $("#trigger_location_type").attr("data-position", coordinates.latitude + "," + coordinates.longitude);

            // when the map is displayed, we can save the user position
            obj.updateCoordinatesRequest(coordinates);

            // map is displayed, we can also get user's positions
            obj.addMarkersFromDirectory();

            // register "force-refresh" click
            obj.registerForceRefresh();

            // update all 30 seconds.
            obj.registerCoordinatesUpdate();

            // on document ready we must be sure that if the user
            // has set the use_neighborhood_as_default to 1, it
            // update the marker to neighborhood coords.
            if ("nbh" == $("#trigger_location_type").attr("data-currently")) {
                geoloc = $("#trigger_location_type").attr("data-neighborhood");
                geoloc = geoloc.split(",");
                coords = {latitude: geoloc[0], longitude: geoloc[1]};
                obj.updateMyMarker(coords, false);
            }
        });

        return this;
    }

    this.initSpider = function()
    {
        this.spiderAround = new OverlappingMarkerSpiderfier(this.getMap(), {markersWontMove: true, markersWontHide: true, keepSpiderfied: true});

        if (this.infowindow == null)
            this.infowindow = new google.maps.InfoWindow();

        var map  = this.getMap();
        var self = this;
        this.spiderAround.addListener("click", function(marker, event)
        {
            self.infowindow.setContent(marker.content);
            self.infowindow.open(self.getMap(), marker);
        });

        var shadow = new google.maps.MarkerImage(
            'https://www.google.com/intl/en_ALL/mapfiles/shadow50.png',
            new google.maps.Size(37, 34),  // size   - for sprite clipping
            new google.maps.Point(0, 0),   // origin - ditto
            new google.maps.Point(10, 34)  // anchor - where to meet map location
        );
        this.spiderAround.addListener('spiderfy', function(markers) {
            for(var i = 0; i < markers.length; i ++) {
                markers[i].setShadow(null);
            }
            self.infowindow.close();
        });
        this.spiderAround.addListener('unspiderfy', function(markers) {
            for(var i = 0; i < markers.length; i ++) {
                markers[i].setShadow(shadow);
            }
        });
    }

    /**
     * getter method for map property.
     *
     * @return google.maps.Map
     **/
    this.getMap = function()
    {
        return this.map;
    }

    /**
     * getter method for coords property.
     * Represent the current visitor's coordinates.
     *
     * @return Object {latitude: decimal, longitude: decimal}
     **/
    this.getCoordinates = function()
    {
        return this.coords;
    }

    /**
     * update a user_id's current marker position to the last
     * one fetched from database. The coords parameter must be
     * a object containing 'latitude' and 'longitude' fields.
     *
     * @param   integer     user_id
     * @param   object      coords  MUST contain 'latitude' and 'longitude'
     * @param   boolean     is_logged_in
     * @return google.maps.Marker
     **/
    this.updateUserMarker = function(user_id, coords, is_logged_in)
    {
        if (user_id == parseInt($("#current_user_id").text()))
            return false;

        icon        = is_logged_in ? this.markerIcons.loggedUser : this.markerIcons.unloggedUser;
        current_pos = new google.maps.LatLng(coords.latitude, coords.longitude);
        marker      = null;

        for (i = 0; i < this.userMarkers.length; i++) {
            var obj = this.userMarkers[i];

            if (parseInt(obj.uid) != user_id)
                continue;

            marker = obj.marker;

            // if the marker is at the same position we don't need to update.
            if (marker.getPosition().lat() == coords.latitude
                && marker.getPosition().lng() == coords.longitude)
                return marker;

            // marker needs an update, then finish.
            marker.setPosition(current_pos);
            marker.setIcon(icon);
            return marker;
        }

        // this is a new user, not listed on the directory.
        // the marker will be created and then stored.

        // first we add the directory item to the bottom of the list.
        $(".news-row").last().css("border-bottom", "1px solid #dcdcdc");
        $(html).insertAfter($(".news-row").last());

        info_html = $(".user-coords[data-uid='" + user_id + "'] .infowindow-content").html();
        marker    = this.createMarker(current_pos, info_html, icon, "User Position");

        this.userMarkers.push({"uid": user_id, "marker": marker});
        return marker;
    }

    /**
     * This method is used to execute the navigator.geolocation
     * API call to watchPosition(). Also it defines 2 callback
     * functions that will be called respectively on success
     * geolocation and on error.
     * The callback parameter must be a function that will
     * be executed after the geolocation process.
     *
     * @param callback  callable
     * @return ConnectNearMe
     **/
    this.watchMyPosition = function(callback)
    {
        var obj = this;
        function successWatch(position)
        {
            geolocation = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
            obj.coords  = position.coords;
            callback(obj);
        }

        function errorWatch()
        {
            fallback = $("#fallback-coordinates").text().split(",");
            obj.coords = {latitude: fallback[0], longitude: fallback[1]};
            callback(obj);
        }

        watchOpts = {
            enableHighAccuracy:true,
            timeout:60000,
            maximumAge:0
        };
        var watchId = navigator.geolocation.watchPosition(successWatch, errorWatch, watchOpts);
        return this;
    }

    /**
     * This method is used to execute the navigator.geolocation
     * API call to getCurrentPosition(). This is used to get a
     * coordinates pair about the actual current position. This
     * method does not track movement of the visitor, it only sets
     * the coords property of the ConnectNearMe object and executes
     * the given callback function given as parameter.
     *
     * @param callback  callable
     * @return ConnectNearMe
     **/
    this.getCurrentPosition = function(callback)
    {
        var obj = this;
        function successCallback(position)
        {
            geolocation = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
            obj.coords  = position.coords;
            callback(obj);
        }

        function errorCallback()
        {
            fallback = $("#fallback-coordinates").text().split(",");
            obj.coords = {latitude: fallback[0], longitude: fallback[1]};
            callback(obj);
        }

        watchOpts = {
            enableHighAccuracy:true,
            timeout:60000,
            maximumAge:0
        };
        var watchId = navigator.geolocation.getCurrentPosition(successCallback, errorCallback, watchOpts);
        return this;
    }

    /**
     * This method is used to register listeners for
     * the Default Location property of the user.
     * This method initializes a google.maps.places.Autocomplete
     * field which, on-place_changed will update the displayed
     * marker to the selected auto-complete place location.
     * This method also initializes the save default location
     * button so that when it is pressed, the coordinates of the
     * auto-completed place are used as the users default location.
     *
     * @return ConnectNearMe
     **/
    this.registerDefineLocation = function()
    {
        var self    = this;

        var input   = document.getElementById('define_my_location');
        var options = {
          types: ['geocode']
        };

        this.autoComplete = new google.maps.places.Autocomplete(input, options);
        google.maps.event.addListener(this.autoComplete, 'place_changed', function() {
            place  = self.autoComplete.getPlace().geometry.location;
            coords = {latitude: place.A, longitude: place.F};
            self.updateMyMarker(coords, false);

            $("#trigger_location_type").attr("data-currently", 'nbh');
            $("#trigger_location_type").html("Use My GPS Location");
        });

        $("#save_default_location").click(function(e){
        	alert('Click...');
            address= $("#define_my_location").val();
            place  = self.autoComplete.getPlace().geometry.location;
            coords = {latitude: place.A, longitude: place.F};
            self.saveDefaultLocationRequest(coords, address);

            // also update the trigger_location_type button to be sure
            // that pressing it will go to the right neighborhood that
            // we just saved.
            $("#trigger_location_type").attr("data-neighborhood", coords.latitude + "," + coords.longitude);
        });

        $("#trigger_location_type").click(function(e)
        {
            displayed = $(this).attr("data-currently");

            switch (displayed) {
                default:
                case 'gps':
                    // trigger FROM gps TO neighborhood
                    geoloc = $(this).data("neighborhood");
                    text   = "Use My GPS Location";
                    flag   = 1;
                    break;
                case 'nbh':
                    // trigger FROM neighborhood TO gps
                    geoloc = $(this).data("position");
                    text   = "Use My Default Location";
                    flag   = 0;
                    break;
            }
            $(this).attr("data-currently", (displayed == 'nbh' ? 'gps' : 'nbh'));
            $(this).html(text);

            geoloc = geoloc.split(",");
            coords = {latitude: geoloc[0], longitude: geoloc[1]};

            self.updateMyMarker(coords, false);

            $.ajax({
                url: "/directory/ajaxUpdateNeighborhoodFlag",
                type: "post",
                async: true,
                data: {
                    uid: $("#current_user_id").text(),
                    flag: flag
                }
            });
        });

        return this;
    }

    /**
     * This method registers the interval for
     * updating the user's current coordinates
     * every 10 seconds.
     *
     * @return ConnectNearMe
     **/
    this.registerCoordinatesUpdate = function()
    {
        var obj = this;
        this.updateInterval = setInterval(function()
        {
            obj.updateUserCoordinates();
        }, 10000);

        this.updateUserCoordinates();
        return this;
    }

    /**
     * This method executes a call to getCurrentPosition
     * and then executes an ajax request for saving the
     * retrieved location data.
     *
     * @return ConnectNearMe
     **/
    this.updateUserCoordinates = function()
    {
        // no coords provided, get current pos.
        this.getCurrentPosition(function(obj)
        {
            obj.updateCoordinatesRequest(obj.getCoordinates());
        });

        return this;
    }

    /**
     * This method executes an ajax call for saving coordinates
     * data into the users.last_coordinates field.
     *
     * @return ConnectNearMe | false
     **/
    this.updateCoordinatesRequest = function(coords)
    {
        var uid = parseInt($("#current_user_id").text());
        //if (isNaN(uid) || uid == 0)
           // return false;
        
        //alert('UserID: ' + uid + ' and Lat: ' + coords.latitude);
        //data was coords: {latitude: coords.latitude, longitude: coords.longitude}

        $.ajax({
            url: "https://devshares.com/directory/ajaxUpdateCoordinates",
            type: "post",
            async: true,
            data: {
                uid: uid, 
                latitude: coords.latitude,
                longitude: coords.longitude
            },
            success: function(d) {
            	//alert('Data is: ' + d);	
			}
        });

        return this;
    }

    /**
     * This method executes an ajax call for saving the default
     * location coordinates of the user. (user.neighborhood_coordinates)
     *
     * @return ConnectNearMe | false
     **/
    this.saveDefaultLocationRequest = function(coords, address)
    {
        var uid  = parseInt($("#current_user_id").text());
        var addr = $("#define_my_location").val();
        //if (isNaN(uid) || uid == 0 || !addr.length)
        //    return false;
        
        //alert('UID: ' + uid + ' and Address: ' + address + ' and Addr: ' + addr + ' and Coords: ' + coords  + ' and Lat: ' + coords.latitude + ' and Lng: ' + coords.longitude);

        $.ajax({
            url: "https://yb.orderitfrom.com/directory/ajaxUpdateDefaultLocation",
            type: "post",
            async: true,
            data: {
                uid: uid,
				'latitude': coords.latitude,
				'longitude': coords.longitude,
				'default_coords': coords,
                'address': address
            }
        });

        return this;
    }

    /**
     * This method iterates through .user-coords elements
     * and creates markers for the given user on the map.
     *
     * @return ConnectNearMe
     **/
    this.addMarkersFromDirectory = function()
    {
        var self = this;

        // also we need to iterate through listed users for generating
        // the markers on the map corresponding to each user.
        $(".user-coords").each(function()
        {
            var current_user = parseInt($("#current_user_id").text());
            coords  = $(this).attr("data-coordinates");
            nbh_c   = $(this).attr("data-nbh-coordinates");
            uid     = $(this).attr("data-uid");
            logged  = parseInt($(this).attr("data-logstate"));

            if (uid == current_user)
                return true;

            info_html = $(this).find(".infowindow-content").html();

            if (coords == "Unknown" && nbh_c == "Unknown")
                // no coords for user.
                return true;

            // specific coordinates not set - use neighborhood coords.
            if (coords == "Unknown")
                coords = nbh_c;

            // create marker for the user.
            coords = coords.split(",");
            gpos   = new google.maps.LatLng(coords[0], coords[1]);
            icon   = logged === 1 ? self.markerIcons.loggedUser : self.markerIcons.unloggedUser;

            // create marker and store.
            marker = self.createMarker(gpos, info_html, icon, "User Position");
            self.userMarkers.push({"uid": uid, "marker": marker});
        });

        this.liveMarkersTick();
        return this;
    }

    /**
     * This method executes an AJAX request that fetches users
     * and their respective coordinates from the database.
     * This method is used in liveMarkersTick() to be executed
     * every 5 seconds.
     *
     * @return ConnectNearMe
     **/
    this.fetchLiveMarkers = function()
    {
        var self = this;

        $.ajax({
            url: "/directory/ajaxGetUsersCoordinates",
            success: function(d)
            {
                var d = eval(d);
                for (i in d) {
                    user_row   = d[i];
                    user_id    = user_row.id;
                    new_coords = user_row.coords;
                    is_logged  = user_row.logged === 1;
                    html       = user_row.html;

                    geoloc     = new_coords.split(",");
                    new_coords = {latitude: geoloc[0], longitude: geoloc[1]};

                    // find marker and update (or create) as needed.
                    marker = self.updateUserMarker(user_id, new_coords, is_logged, html);
                }
            }
        });

        return this;
    }

    /**
     * Allows to automatically check for new user positions
     * or logged_in states in real-time on the map.
     * @return void
     **/
    this.liveMarkersTick = function()
    {
        var self = this;
        self.fetchLiveMarkers();

        setTimeout(function() { self.liveMarkersTick(); }, 5000);
    }

    /**
     * This method registers a click listener
     * for the Force Refresh feature link.
     *
     * @return ConnectNearMe
     **/
    this.registerForceRefresh = function()
    {
        var self = this;

        $("#force-refresh-position").click(function(e)
        {
            e.preventDefault();

            self.watchMyPosition(function(obj)
            {
                if (obj.map != null) {
                    // user position changed.
                    obj.updateMyMarker(obj.getCoordinates(), true);
                    return false;
                }
            });

            return false;
        })

        return this;
    }

    /**
     * create a marker on the google map. Provided
     * argument MUST be a google.maps.LatLng object.
     *
     * @param coord     google.maps.LatLng
     * @param text      string
     * @param icon      string
     * @return ConnectNearMe
     **/
    this.createMarker = function(coord, text, icon, title)
    {
        var self   = this;
        var marker = new google.maps.Marker({
            flat: true,
            map: this.getMap(),
            position: coord,
            icon: icon,
            content: text,
            title: title,
            optimized: false,
            visible: true
        });
        self.spiderAround.addMarker(marker);
        return marker;
    }

    /**
     * This method UPDATES a google map marker to the given
     * new coordinates. Also it will pan the map so that the
     * movement can be seen.
     *
     * @param coords                Object {latitude: decimal, longitude: decimal}
     * @param update_last_coord     boolean     wether to update the user coord in DB, or not
     * @return ConnectNearMe
     **/
    this.updateMyMarker = function(coords, update_last_coord)
    {
        current_pos = new google.maps.LatLng(coords.latitude, coords.longitude);

        this.map.panTo(current_pos);
        this.myPosMarker.setPosition(current_pos);

        if (update_last_coord)
            this.updateCoordinatesRequest(coords);

        return this;
    }
}
