/* Activ'Map Plugin 2.0.0
 * Copyright (c) 2015-2020 Pandao
 * Documentation : https://activmap.pandao.eu/doc
 */
(function($){
    'use strict';
    $.activmap = {
        // Default values (if not modified in the plugin init)
        // DON'T CHANGE THE VALUES HERE, overwrite the options in your HTML file
        defaults: {
            places: [],            //list of places objects {title, address, phone, tags, lat, lng, img},
            lat: 51.507351,        //latitude of the center
            lng: -0.127758,        //longitude of the center
            zoom: 10,              //default zoom level between 0 and 21
            cluster: true,         //enables / disables clustering for large amounts of markers
            mapType: 'roadmap',    //map type : "roadmap", "satellite", "perspective"
            posPanel: 'left',      //position of the removable panel : "left" or "right"
            showPanel: true,       //shows / hides the removable panel
            radius: 0,             //max radius in kilometers
            unit: 'km',            //distance unit (km or mile)
            country: null,         //country limit for location input (ex. "ca": Canada, "us": United States, "fr": France...)
            autogeolocate: false,  //auto geolocation to set the center of the map
            allowMultiSelect: true,//allow user to select multiple filters
            icon: 'images/icons/marker.png',
            center_icon: 'images/icons/marker-center.png',
            styles: [{"featureType":"administrative.country","elementType":"geometry","stylers":[{"visibility":"simplified"},{"hue":"#ff0000"}]}],
            request: 'large',      //type of request "large" (show results of each filter) or "strict" (show results of all filters)
            locationTypes: ['geocode','establishment'], //types of locations returned by autocomplete requests
            show_center: true //show or hide the center of the map marker
        }
    };

    $.arrayIntersect = function(a, b)
    {
        return $.grep(a, function(i)
        {
            return $.inArray(i, b) > -1;
        });
    };
    
    $.fn.extend({
        
        activmap : function(settings){
            
            var s = $.extend({}, $.activmap.defaults, settings);

            //Map init
            var init_latlng = new google.maps.LatLng(s.lat,s.lng)
            var latlng = init_latlng;
            var opendInfoWindow = null;
            var markers = [];
            var infoWindow = [];
            var ids = [];
            var markerCluster;
            var centerMarker;
            var bounds = new google.maps.LatLngBounds();
            var num_places = 0;
            var old_results = 0;
            var mapTypeId = google.maps.MapTypeId.ROADMAP;
            if(s.mapType == 'satellite' || s.mapType == 'perspective') mapTypeId = google.maps.MapTypeId.HYBRID;
            var map = new google.maps.Map(document.getElementById('activmap-canvas'), {
                zoom: s.zoom,
                center: latlng,
                mapTypeId: mapTypeId,
                styles: s.styles,
                mapTypeControl: true,
                mapTypeControlOptions: {
                    style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR,
                    position: google.maps.ControlPosition.BOTTOM_CENTER
                },
                zoomControl: true,
                zoomControlOptions: {
                    position: google.maps.ControlPosition.LEFT_CENTER
                },
                scaleControl: true,
                streetViewControl: true,
                streetViewControlOptions: {
                    position: google.maps.ControlPosition.RIGHT_BOTTOM
                },
                fullscreenControl: false
            });
            bounds.extend(init_latlng); 
            var init_latlngBounds = map.getBounds();
            if(s.mapType == 'perspective') map.setTilt(45);
            
            //Containers
            var activmap_canvas = $('#activmap-container');
            var activmap_places = $('#activmap-places');
            var map_w = activmap_canvas.width();
            var cont_w = activmap_places.outerWidth();
            
            //Panel
            if(!s.showPanel) activmap_places.hide();
            else{
                if(s.posPanel == 'left'){
                    activmap_places.css({
                        left: -cont_w,
                        right: 'auto'
                    });
                    activmap_canvas.css({
                        float: 'right'
                    });
                }
                if(s.posPanel == 'right'){
                    activmap_places.css({
                        right: -cont_w,
                        left: 'auto'
                    });
                    activmap_canvas.css({
                        float: 'left'
                    });
                }
            }
            
            //if($('input[name="marker_type[]"]').length) $('input[name="marker_type[]"]').prop('checked',false);
            
            if($('input[name="activmap_radius"]').length){
                $('input[name="activmap_radius"]').prop('checked', false).each(function(){
                    if(s.radius == $(this).val()) $(this).prop('checked', true);
                });
            }

            _init = function(){

                //Places init
                $.each(s.places, function(i, place){
                    place.num_tags = 0;
                    place.id = i;
                    //Marker init
                    var myLatlng = new google.maps.LatLng(place.lat, place.lng);
                    var myIcon = (place.icon != '' && place.icon != undefined) ? place.icon : s.icon;
                    var marker = new google.maps.Marker({
                        map: map,
                        position: myLatlng,
                        icon: myIcon,
                        title: place.title
                    });
                    //Info window content building
                    var mycontent = '<div class="activmap-infowindow">';
                    if(place.img != undefined && place.img != '') mycontent += '<div class="activmap-brand"><img src="'+place.img+'"></div>';
                    mycontent += '<div class="activmap-details"><h4 class="activmap-title">'+place.title+'</h4>';
                    if(place.address != undefined && place.address != '') mycontent += '<div class="activmap-address">'+place.address+'</div>';
                    if(place.phone != undefined && place.phone != '') mycontent += '<div class="activmap-phone">'+place.phone+'</div>';
                    if(place.url != undefined && place.url != '') mycontent += '<a href="'+place.url+'" target="_blank" class="activmap-url">'+place.url+'</a>';
                    if(place.custom != undefined && place.custom != '') mycontent += '<div class="activmap-custom">'+place.custom+'</div>';
                    mycontent += '</div><div style="clear: both;"></div></div>';

                    //Info window init
                    infoWindow[i] = new google.maps.InfoWindow({
                        content  : mycontent,
                        position : myLatlng
                    });
                    
                    //infoWindow close event
                    google.maps.event.addListener(infoWindow[i], 'closeclick', function(){
                        $('.activmap-place').removeClass('active');
                        activmap_places.scrollTop();
                    });
                    
                    //Marker click event
                    google.maps.event.addListener(marker, 'click', function(){
                        if(opendInfoWindow != null) opendInfoWindow.close();
                        infoWindow[i].open(map, marker);
                        
                        map.setCenter(marker.getPosition());
                        var padbnds = _paddedBounds(20, 280, 50, 50);
                        map.fitBounds(padbnds);

                        opendInfoWindow = infoWindow[i];
                        
                        $('.activmap-place').removeClass('active');
                        $('#activmap-place_'+i).addClass('active');
                        activmap_places.scrollTop(activmap_places.scrollTop()+$('#activmap-place_'+i).position().top);
                    });
                    
                    marker.setVisible(false);
                    place.marker = marker;
                    markers.push(marker);
                    
                    place.title = place.title.replace(/<a\b[^>]*>(.*?)<\/a>/i,'$1');
                    place.html = '<div class="activmap-place" id="activmap-place_'+i+'"><h3>'+place.title+'</h3><p>'+place.address+'</p></div>';
                    
                    //Places indexed by tag name (checkbox name)
                    $.each(place.tags, function(j, tag){
                        if(ids[tag] === undefined) ids[tag] = [];
                        ids[tag].push(place.id);
                    }); 
                });
                
                
                //Cluster init
                if(s.cluster){
                    markerCluster = new MarkerClusterer(map, markers, {
                        maxZoom: 12,
                        gridSize: 40
                    });
                }
                
                //Center marker init
                if(s.show_center){
					centerMarker = new google.maps.Marker({
						map: map,
						position: latlng,
						icon: (s.center_icon != '' && s.center_icon != undefined) ? s.center_icon : s.icon
					});
					centerMarker.setVisible(true);
					markers.push(centerMarker);
				}
                
                //Order places in the panel
                _order();

                //Auto geolocation
                if(s.autogeolocate == true){
                    _geolocate();
                }
                
                //Geolocate event
                if($('#activmap-geolocate').length){
                    $('#activmap-geolocate').on('click', function(){
                        _geolocate();
                    });
                }
                
                //Target results event
                if($('#activmap-target').length){
                    $('#activmap-target').on('click', function(){
                        latlng = init_latlng;
                        _update_places_bounds();
                        _update_map();
                    });
                }
                
                //Location field change event
                if($('#activmap-location').length){
                    if(s.country !== null){
                        var options = {
                           types: s.locationTypes,
                           componentRestrictions: {country: s.country}
                        };
                    }else{
                        var options = {
                           types: s.locationTypes
                        };
                    }
                    var input = document.getElementById('activmap-location');
                    autocomplete = new google.maps.places.Autocomplete(input, options);
                
                    google.maps.event.addListener(autocomplete, 'place_changed', function(){
                        var place = autocomplete.getPlace();
                        latlng = place.geometry.location;
                        //map.setCenter(place.geometry.location);
                        if($('.activmap-place').length) _order();
                        $('input[name="marker_type[]"], select[name="marker_type[]"]').each(function(){
                            _update_places_tag($(this), false);
                        });
                        _update_center_marker();
                        //bounds = map.getBounds();
                        _update_map();
                    });
                }
                
                //Radius change event
                $('input[name="activmap_radius"]').on('change', function(){
                    s.radius = $(this).val();
                    $('input[name="marker_type[]"], select[name="marker_type[]"]').each(function(){
                        _update_places_tag($(this), false);
                    });
                    _update_map();
                });

                //Filter change event
                $('input[name="marker_type[]"], select[name="marker_type[]"]').on('change', function(){
                    _update_places_tag($(this), true);
                    _update_map();
                });

                //Reset click event
                $('#activmap-reset').on('click', function(){
                    $('input[name="marker_type[]"]').prop('checked',false);
                    $('select[name="marker_type[]"] > option').prop('selected', false);
                    $('input[name="marker_type[]"], select[name="marker_type[]"]').each(function(){
                        _update_places_tag($(this), false);
                    });
                    latlng = init_latlng;
                    _update_center_marker();
                    _update_map();
                    return false;
                });
                
                //Place (panel with results) click event
                $(document).on('click', '.activmap-place', function(){
                    var id = $(this).attr('id').replace('activmap-place_','');
                    google.maps.event.trigger(markers[id], 'click');
                });
                
                //Window resize event
                $(window).on('resize', function(){
                    setTimeout('_update_map()', 300);
                });
                
                //For each selected tags on page loading
                $('input[name="marker_type[]"]:checked').add($('select[name="marker_type[]"]').prop('selected', true)).each(function(){
                    _update_places_tag($(this), false);
                });
                
                _update_map();
            }
            
            /* _sort_by_dist() compares 2 distances
             * 
             * @param a coordinates of point 1
             * @param b coordinates of point 2
             */
            _sort_by_dist = function(a, b){
                return ((a.dist < b.dist) ? -1 : ((a.dist > b.dist) ? 1 : 0));
            }
            
            /* _order() sorts the places in the panel
             * 
             */
            _order = function(){
                $.each(s.places, function(i, place){
                    place.dist = _get_distance(place.marker.position, latlng);
                });
                s.places.sort(_sort_by_dist);
                $('.activmap-place').remove();
                $.each(s.places, function(i, place){
                    activmap_places.append(place.html);
                    if(place.isVisible) $('#activmap-place_'+place.id).show();
                });
            }
            
            /* _update_center_marker() modifies the position of the center marker
             * 
             */
            _update_center_marker = function(){
                centerMarker.setPosition(latlng);
                _update_places_bounds();
            }
            
            /* _geolocate() geolocates the user
             * 
             */
            _geolocate = function(){
                if(navigator.geolocation){
                    browserSupportFlag = true;
                    navigator.geolocation.getCurrentPosition(function(position){
                        initialLocation = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
                        latlng = initialLocation;
                        //map.setCenter(latlng);
                        if($('.activmap-place').length) _order();
                        $('input[name="marker_type[]"], select[name="marker_type[]"]').each(function(){
                            _update_places_tag($(this), false);
                        });
                        _update_center_marker();
                        //bounds = map.getBounds();
                        _update_map();
                    }, function(){
                        handleNoGeolocation(browserSupportFlag);
                    });
                }else{
                    browserSupportFlag = false;
                    handleNoGeolocation(browserSupportFlag);
                }
            }
            
            /* _handleNoGeolocation() handles the geolocation errors
             * 
             */
            _handleNoGeolocation = function(errorFlag){
                if(errorFlag == true){
                    console.log('Geolocation service failed.');
                    initialLocation = latlng;
                }else{
                    console.log('Your browser doesn\'t support geolocation.');
                    initialLocation = latlng;
                }
                map.setCenter(initialLocation);
            }
            
            /* _rad() gets the radius distance
             * 
             * @param a distance
             * 
             */
            _rad = function(a){
                return a * Math.PI / 180;
            }
            
            /* _paddedBounds() adds offsets all around the map
             * 
             * @param npad north padding in pixels
             * @param spad south padding in pixels
             * @param epad east padding in pixels
             * @param wpad west padding in pixels
             * 
             */
            _paddedBounds = function(npad, spad, epad, wpad) {
                var SW = map.getBounds().getSouthWest();
                var NE = map.getBounds().getNorthEast();
                var topRight = map.getProjection().fromLatLngToPoint(NE);
                var bottomLeft = map.getProjection().fromLatLngToPoint(SW);
                var scale = Math.pow(2, map.getZoom());

                var SWtopoint = map.getProjection().fromLatLngToPoint(SW);
                var SWpoint = new google.maps.Point(((SWtopoint.x - bottomLeft.x) * scale) + wpad, ((SWtopoint.y - topRight.y) * scale) - spad);
                var SWworld = new google.maps.Point(SWpoint.x / scale + bottomLeft.x, SWpoint.y / scale + topRight.y);
                var pt1 = map.getProjection().fromPointToLatLng(SWworld);

                var NEtopoint = map.getProjection().fromLatLngToPoint(NE);
                var NEpoint = new google.maps.Point(((NEtopoint.x - bottomLeft.x) * scale) - epad, ((NEtopoint.y - topRight.y) * scale) + npad);
                var NEworld = new google.maps.Point(NEpoint.x / scale + bottomLeft.x, NEpoint.y / scale + topRight.y);
                var pt2 = map.getProjection().fromPointToLatLng(NEworld);

                return new google.maps.LatLngBounds(pt1, pt2);
            }

            /* _getDistance() returns the distance between 2 points in meters
             * 
             * @param p1 coordinates of point 1
             * @param p2 coordinates of point 2
             * 
             */
            _get_distance = function(p1, p2){
                var R = (s.unit == 'km') ? 6378137 : 3963190;
                var dLat = _rad(p2.lat() - p1.lat());
                var dLong = _rad(p2.lng() - p1.lng());
                var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                    Math.cos(_rad(p1.lat())) * Math.cos(_rad(p2.lat())) *
                    Math.sin(dLong / 2) * Math.sin(dLong / 2);
                var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
                var d = R * c;
                return d;
            }
            
            /* _update_map() hides or shows the places panel. Shrinks, enlarges and refresh the map
             * 
             */
            _update_map = function(){
                map_w = $('#activmap-wrapper').width();
                cont_w = activmap_places.outerWidth();
                if(num_places > 0){
                    //results found
                    if(old_results == 0){
                        //no previous result
                        //side panel displaying for 1st time => shows places
                        if(s.showPanel){
                            if(s.posPanel == 'left'){
                                activmap_places.stop(true,true).animate({
                                    left: 0
                                },400);
                            }
                            if(s.posPanel == 'right'){
                                activmap_places.stop(true,true).animate({
                                    right: 0
                                },400);
                            }
                            //side panel is visible on screen device only => shrinks map
                            if(activmap_places.is(':visible')){
                                activmap_canvas.animate({
                                    width: map_w-cont_w
                                },400);
                            }
                        }
                    }else{
                        if(s.showPanel && activmap_places.is(':visible') && $(window).width()+17 > 1190) activmap_canvas.width(map_w-cont_w);
                        else activmap_canvas.width(map_w);
                    }
                }else{
                    //no result found
                    if(opendInfoWindow != null) opendInfoWindow.close();
                    if(s.showPanel){
                        if(activmap_places.is(':visible') && $(window).width()+17 > 1190){
                            //there were previous results
                            //side panel is visible on screen device only => hides places, enlarges map
                            activmap_canvas.animate({
                                width: map_w
                            },400);
                            if(s.posPanel == 'left'){
                                 activmap_places.animate({
                                    left: -cont_w
                                },400);
                            }
                            if(s.posPanel == 'right'){
                                 activmap_places.animate({
                                    right: -cont_w
                                },400);
                            }
                        }else
                            activmap_canvas.width(map_w);
                    }else
                        activmap_canvas.width(map_w);
                }
                old_results = num_places;
                setTimeout('_map_resize()',400);
            }
            
            /* _map_resize() updates bounds and clusters on map resize
             * 
             */
            _map_resize = function() {
                google.maps.event.trigger(map, 'resize');
                
                var listener = google.maps.event.addListenerOnce(map, 'idle', function(){ 
                    if(map.getZoom() > 16) map.setZoom(16);
                });
                
                if(num_places > 0){
                    map.fitBounds(bounds);
                    var padbnds = _paddedBounds(10, 150, 50, 50);
                    map.fitBounds(padbnds);
                    if(s.cluster) markerCluster.repaint();
                }else{
                    map.setZoom(s.zoom);
                    map.setCenter(latlng);
                    if(s.cluster) markerCluster.repaint();
                }
            }
            
            /* _update_places_bounds() updates map bounds according to visible places
             * 
             */
            _update_places_bounds = function(){
                bounds = new google.maps.LatLngBounds();
                var i = 0;
                $.each(s.places, function(j, place){
                    if(place.isVisible == true){
                        bounds.extend(place.marker.getPosition());
                        i++;
                    }
                });
                bounds.extend(centerMarker.getPosition());
                num_places = i;
            }
            
            /* _update_places_tag(input) updates the array of places and the map's bounds
             * 
             * @param input checkbox object
             * 
             */
            _update_places_tag = function(input, uncheck){
                
                //Checkbox selection
                if(!s.allowMultiSelect){
                    if(uncheck){
                        $('.marker-selector').removeClass('active');
                        $('input[name="marker_type[]"]').not(input).prop('checked',false);
                        $('select[name="marker_type[]"]').not(input).find('option').prop('selected', false);
                    }
                }
                var checked = (input.prop('tagName') == 'SELECT') ? input.prop('selected') : input.prop('checked');
                
                if(checked == true)
                    input.parents('.marker-selector').addClass('active');
                else
                    input.parents('.marker-selector').removeClass('active');
                    
                var val;
                var i = 0;
                var p_ids = [];
                var ids_copy = [];
                $('input[name="marker_type[]"]:checked').add($('select[name="marker_type[]"]').prop('selected', true)).each(function(){
                    val = $(this).val();
                    if(val != ''){
                        if(ids[val] === undefined) ids[val] = [];
                        if(i == 0) p_ids = $.merge([], ids[val]);
                        else{
                            if(s.request == 'strict') p_ids = $.arrayIntersect(ids[val], p_ids);
                            if(s.request == 'large'){
                                ids_copy = $.merge([], ids[val]);
                                p_ids = $.merge(ids_copy, p_ids);
                            }
                        }
                        i++;
                    }
                });
                
                if(opendInfoWindow != null) opendInfoWindow.close();
                    
                $.each(s.places, function(j, place){
                    place.num_tags = 0;
                    place.dist = 0;
                    if($.inArray(place.id, p_ids) >= 0){
                        place.dist = _get_distance(place.marker.position, latlng);
                        //the place is in all tags => set visible
                        if(s.radius == 0 || place.dist <= s.radius*1000){
                            place.num_tags++;
                            place.marker.setVisible(true);
                            place.isVisible = true;
                            $('#activmap-place_'+place.id).show();
                        }else{
                            //perform only if a checkbox is clicked
                            if(place.num_tags > 0) place.num_tags--;
                            if(place.num_tags == 0){
                                place.marker.setVisible(false);
                                place.isVisible = false;
                                place.isVisible = false;
                                $('#activmap-place_'+place.id).hide();
                            }
                        }
                    }else{
                        //perform only if a checkbox is clicked
                        if(place.num_tags > 0) place.num_tags--;
                        if(place.num_tags == 0){
                            place.marker.setVisible(false);
                            place.isVisible = false;
                            $('#activmap-place_'+place.id).hide();
                        }
                    }
                });
                
                _update_places_bounds();
                
                $('#activmap-results-num').html(num_places+' result(s)');
            }

            if(!Array.isArray(s.places)){
                $.ajax({
                    url: s.places,
                    dataType: 'json',
                    cache: false,
                    error: function (jqXHR, textStatus, errorThrown) {
                        console.log(jqXHR);
                        console.log(textStatus);
                        console.log(errorThrown);
                    },
                    success: function(data){
                        var obj = eval(data);
                        s.places = obj.places;
                        _init();
                    }
                });
            }else
                _init();
        }
    });
})(jQuery);
