define([
        'jquery',
        'limesharp_stockists',
        'stockists_mapstyles',
        'stockists_search',
        'mage/translate'
    ],
    function ($, config, mapstyles, search_widget, $t) {
        return function (config) {
            let map;
            let markers = [];
            let circles = [];
            let currentZoom = 0;
            let currentClicked = false;
            let lastSearch = {};
            let startLocation = {
                lat: config.latitude,
                lng: config.longitude
            }

            //load maps api
            $.getScript("https://maps.googleapis.com/maps/api/js?v=3&sensor=false&key=" + config.apiKey + "&libraries=geometry,places", function () {
                initMap();
            });

            // Default maps onload
            let initMap = function() {
                let mapElement = document.getElementById('map-canvas');
                let loadedMapStyles = mapstyles[config.map_styles];
                let mapOptions = {
                    zoom: config.zoom,
                    scrollwheel: true,
                    center: startLocation,
                    styles: loadedMapStyles
                };

                lastSearch = {
                    searchTerms: startLocation,
                    marker: new google.maps.Marker()
                };

                map = new google.maps.Map(mapElement, mapOptions);
                var directionsService = new google.maps.DirectionsService();
                var directionsDisplay = new google.maps.DirectionsRenderer();
                directionsDisplay.setMap(map);

                //Create input search with hints, by google
                createPlaceSearchBox();

                //hander for UX click
                clickHandler();

                //First search attempt
                //if user grant geo permission we use its location
                //instead we use default position
                if (config.geolocation) {
                    getGeoLocation(map);
                } else {
                    geoError();
                }
            }

            //gets geolocation, if storeDirections is set then it is interpreted as a way to getDirection
            let getGeoLocation = function(map, storeDirections, userTravelMode, directionsService, directionsDisplay) {

                // attach click events for directions
                if (navigator.geolocation) {
                    $(document).on("click", ".get-directions", function () {
                        let storeDirections = {
                            latitude: $(".stockists-window").attr("data-latitude"),
                            longitude: $(".stockists-window").attr("data-longitude")
                        };

                        let userTravelMode = $(this).attr("data-directions");
                        getGeoLocation(map, storeDirections, userTravelMode, directionsService, directionsDisplay);
                    })
                }

                var geoOptions = function () {
                    return {
                        maximumAge: 5 * 60 * 1000,
                        timeout: 10 * 1000
                    }
                };

                navigator.geolocation.getCurrentPosition(geoSuccess, geoError, geoOptions);
            }

            let geoSuccess = function (position) {
                let icon = {
                    url: "https://maps.google.com/mapfiles/ms/icons/blue-dot.png", // url
                    url: "",
                    scaledSize: new google.maps.Size(45, 45), // scaled size
                };
                startLocation = {
                    lat: position.coords.latitude,
                    lng: position.coords.longitude,
                }
                let search_marker = new google.maps.Marker({
                    map: map,
                    position: startLocation,
                    icon: icon
                });
                searchShops(startLocation, search_marker)
            };

            let geoError = function () {
                //default location
                let noicon = {
                    url: "",
                    scaledSize: new google.maps.Size(1, 1), // scaled size
                };
                let search_marker = new google.maps.Marker({
                    map: map,
                    position: startLocation,
                    icon: noicon
                });
                searchShops(startLocation, search_marker)
            };

            let createPlaceSearchBox = function() {

                let searchBox = new google.maps.places.SearchBox(document.getElementById('pac-input'));

                searchBox.addListener('places_changed', function () {
                    let places = searchBox.getPlaces();
                    if ((places == null) || (places.length == 0)) {
                        let message = $t('Cannot get place geometry location');
                        self.showError(message);
                        return;
                    }
                    let searchTerms = {
                        lat: places[0].geometry.location.lat(),
                        lng: places[0].geometry.location.lng(),
                    }

                    let noicon = {
                        url: "",
                        scaledSize: new google.maps.Size(1, 1), // scaled size
                    };

                    let search_marker = new google.maps.Marker({
                        map: map,
                        position: {lat: searchTerms.lat, lng: searchTerms.lng},
                        icon: noicon
                    });

                    searchShops(searchTerms, search_marker);
                });
            }

            // Filter type
            let clickHandler = function() {

                //click on filter
                $(".filter-store-type").on("click", function () {
                    let marker = lastSearch.marker;
                    let searchTerms = lastSearch.searchTerms;

                    searchShops(searchTerms, marker);
                });

                // Click on Shop in List
                $("body").on("click", ".results-content", function () {
                    let id = $(this).attr('data-marker');
                    selectMarker(id);
                });
            }

            let centerMapOnPlace = function(marker) {

                marker.setMap(map);

                const circle = new google.maps.Circle({
                    map: map,
                    radius: config.radius * 1000,    // value from admin settings
                    fillColor: config.fillColor,
                    fillOpacity: config.fillOpacity,
                    strokeColor: config.strokeColor,
                    strokeOpacity: config.strokeOpacity,
                    strokeWeight: config.strokeWeight
                });
                circle.bindTo('center', marker, 'position');
                circles.push(circle);
                let bounds = new google.maps.LatLngBounds();
                bounds.union(circle.getBounds());

                google.maps.event.addListener(map, 'zoom_changed', function () {
                    zoomChangeBoundsListener = google.maps.event.addListener(map, 'bounds_changed', function (event) {
                        if (this.getZoom() > 18 && this.initialZoom == true) {
                            // Change max/min zoom here
                            this.setZoom(18);
                            this.initialZoom = false;
                        }
                        currentZoom = this.getZoom();
                        google.maps.event.removeListener(zoomChangeBoundsListener);
                    });
                });
                map.initialZoom = true;

                if ($("#map-canvas").is(":visible")) {
                    map.fitBounds(bounds);
                } else {
                    map.setZoom(config.zoom);
                    map.panTo(marker.position);
                }
            }

            // Adds a marker to the map and push to the array.
            let createShops = function(shops_collection, search_marker) {
                for (let data of shops_collection) {
                    let marker = createShopOnMap(data);
                    markers.push(marker);
                }
                createShopOnList(search_marker);
            }

            let createShopOnMap = function(data) {
                let image = {
                    url: config.map_pin
                };
                let infowindow = new google.maps.InfoWindow({
                    content: ""
                });
                console.log(image)

                google.maps.event.addListener(infowindow, 'closeclick', function () {
                    $(".results-store .results-content").removeClass("disabled");

                    let bounds = new google.maps.LatLngBounds();
                    bounds.union(circles[0].getBounds());
                    map.fitBounds(bounds);

                    currentClicked = false;

                    for (let i = 0; i < markers.length; i++) {
                        markers[i].setIcon(config.map_pin);
                    }
                });
                let latLng = new google.maps.LatLng(data.latitude, data.longitude);
                let record_id = "" + data.latitude + data.longitude;
                let marker = new google.maps.Marker({
                    record_id: record_id,
                    global_name: data.name,
                    global_address: data.address,
                    global_city: data.city,
                    global_postcode: data.postcode,
                    global_country: data.country,
                    position: latLng,
                    map: map,
                    icon: image,
                    title: data.name
                });
                marker.setMap(map);
                bindInfoWindow(marker, map, infowindow, data);

                return marker;
            }

            // Insert the shop on the result list
            let createShopOnList = function(marker) {
                $(".results-store").html('');
                for (let i = 0; i < markers.length; i++) {
                    let distance = google.maps.geometry.spherical.computeDistanceBetween(marker.position, markers[i].position);
                    let store_distance, unitOfLength
                    if ((distance / 1000) < config.radius) {
                        if (config.unit == "default") {
                            store_distance = parseFloat(distance * 0.001).toFixed(2);
                            unitOfLength = "km";
                        } else if (config.unit == "miles") {
                            store_distance = parseFloat(distance * 0.000621371192).toFixed(2);
                            unitOfLength = "miles";
                        }
                        let contentToAppend = "<div class='results-content' data-miles='" + store_distance + "' data-marker='" + markers[i].record_id + "'><p class='results-title'>" + markers[i].global_name + "</p>";
                        if (markers[i].global_address) {
                            contentToAppend += "<p class='results-address'>" + markers[i].global_address + "</p>";
                        }
                        if (markers[i].global_city) {
                            //contentToAppend += "<p class='data-phone'>" + markers[i].global_city + " " + markers[i].global_postcode + "</p>";
                            contentToAppend += "<p class='data-phone'>" + markers[i].global_city + "</p>";
                        }
                        contentToAppend += "<p class='data-miles'>" + store_distance + " " + unitOfLength + "</p></div>";
                        $(".results-store").append(contentToAppend);
                    }
                }
                let $wrapper = $('.results-store');

                //sort the result by distance
                $wrapper.find('.results-content').sort(function (a, b) {
                    return +a.dataset.miles - +b.dataset.miles;
                }).appendTo($wrapper);
            }

            // Deletes all markers and circles in the array by removing references to them.
            let deleteLastShops = function() {
                //Remove marker from map
                for (let i = 0; i < markers.length; i++) {
                    markers[i].setMap(null);
                }
                lastSearch.marker.setMap(null);
                markers = [];
                lastSearch.marker = null;

                //Remove shops from list
                $(".results-store").html('<p class="mt-2">' + $.mage.__('No store found in the required area') + '</p>');

                //Remove circles
                removeAllcircles();
            }

            // Deletes all circles from maps
            let removeAllcircles = function() {
                for (let i in circles) {
                    circles[i].setMap(null);
                }
                circles = [];
            }

            //Select Marker by List
            let selectMarker = function(id) {
                for (let i = 0; i < markers.length; i++) {
                    if (markers[i].record_id == id) {
                        google.maps.event.trigger(markers[i], 'click');
                    }
                }
            }

            //after the user has shared his geolocation, center map, insert marker and show stores
            let centerMap = function(coords, map, markers) {
                let latLng = new google.maps.LatLng(coords.latitude, coords.longitude);
                currentLocation.search(map, coords, latLng, config, markers);
            }

            //get driving directions from user location to store
            let getDirections = function(map, storeDirections, userLocation, userTravelMode, directionsService, directionsDisplay) {
                if (typeof userTravelMode === 'undefined') {
                    var directionsTravelMode = "DRIVING";
                } else {
                    var directionsTravelMode = userTravelMode;
                }

                var request = {
                    destination: new google.maps.LatLng(storeDirections.latitude, storeDirections.longitude),
                    origin: new google.maps.LatLng(userLocation.latitude, userLocation.longitude),
                    travelMode: google.maps.TravelMode[directionsTravelMode]
                };

                directionsService.route(request, function (response, status) {
                    if (status == google.maps.DirectionsStatus.OK) {
                        directionsDisplay.setDirections(response);
                        directionsDisplay.setPanel($('.directions-panel')[0]);
                    }
                });

                $(".directions-panel").show();

                //on close reset map and panel and center map to user location
                $("body").on("click", ".directions-panel .close", function () {
                    $(".directions-panel").hide();
                    directionsDisplay.setPanel(null);
                    directionsDisplay.setMap(null);
                    centerMap(userLocation, map, markers);
                });
            }

            // Draw infowindows
            let bindInfoWindow = function(marker, map, infowindow, data) {
                google.maps.event.addListener(marker, 'click', function (ev) {

                    if (this.record_id == currentClicked) {
                        return;
                    }
                    currentClicked = this.record_id;

                    var contentString = '<div class="stockists-window" data-latitude="' + marker.getPosition().lat() + '" data-longitude="' + marker.getPosition().lng() + '"><p class="stockists-title">' + data.name + '</p>';

                    if (data.external_link) {
                        contentString += '<p class="stockists-telephone"><a href="' + data.external_link + '" target="_blank">' + data.external_link + '</a></p>';
                    } else if (data.link) {
                        contentString += '<p class="stockists-telephone"><a href="/' + config.moduleUrl + '/' + data.link + '" target="_blank">Detail page</a></p>';
                    }
                    if (data.phone) {
                        contentString += '<p class="stockists-telephone">' + data.phone + '</p>';
                    }
                    if (data.telephone) {
                        contentString += '<p class="stockists-telephone">' + data.telephone + '</p>';
                    }
                    if (data.email) {
                        contentString += '<p class="stockists-address"><a href="mailto:' + data.email + '" target="_blank">' + data.email + '</a></p>';
                    }
                    if (data.address) {
                        contentString += '<p class="stockists-telephone">' + data.address + '</p>';
                    }
                    if (data.city) {
                        contentString += '<p class="stockists-telephone">' + data.city + '</p>';
                    }
                    if (data.postcode) {
                        contentString += '<p class="stockists-web">' + data.postcode + '</p>';
                    }

                    contentString += '<p class="ask-for-directions get-directions" data-directions="DRIVING"><a href="http://maps.google.com/maps?saddr=&daddr=' + marker.getPosition().lat() + ',' + marker.getPosition().lng() + '" target="_blank">' + $t("Get Directions") + '</a></p>';

                    //Working Hours
                    if (data.wo_general_indication || data.wo_monday || data.wo_tuesday || data.wo_wednesday || data.wo_thursday || data.wo_friday || data.wo_saturday || data.wo_sunday) {
                        contentString += '<p class="stockists-wo-title"><strong>' + $t("Working Hours") + ':</strong></p>';
                    }
                    if (data.wo_general_indication) {
                        contentString += '<p class="stockists-wo-general">' + data.wo_general_indication + '</p>';
                    }
                    if (data.wo_monday) {
                        contentString += '<p class="stockists-wo-day stockists-wo-monday"><strong>' + $t("Monday") + ': </strong>' + data.wo_monday + '</p>';
                    }
                    if (data.wo_tuesday) {
                        contentString += '<p class="stockists-wo-day stockists-wo-tuesday"><strong>' + $t("Tuesday") + ': </strong>' + data.wo_tuesday + '</p>';
                    }
                    if (data.wo_wednesday) {
                        contentString += '<p class="stockists-wo-day stockists-wo-wednesday"><strong>' + $t("Wednesday") + ': </strong>' + data.wo_wednesday + '</p>';
                    }
                    if (data.wo_thursday) {
                        contentString += '<p class="stockists-wo-day stockists-wo-thursday"><strong>' + $t("Thursday") + ': </strong>' + data.wo_thursday + '</p>';
                    }
                    if (data.wo_friday) {
                        contentString += '<p class="stockists-wo-day stockists-wo-friday"><strong>' + $t("Friday") + ': </strong>' + data.wo_friday + '</p>';
                    }
                    if (data.wo_saturday) {
                        contentString += '<p class="stockists-wo-day stockists-wo-saturday"><strong>' + $t("Saturday") + ': </strong>' + data.wo_saturday + '</p>';
                    }
                    if (data.wo_sunday) {
                        contentString += '<p class="stockists-wo-day stockists-wo-sunday"><strong>' + $t("Sunday") + ': </strong>' + data.wo_sunday + '</p>';
                    }

                    contentString += '</div>';
                    for (var i = 0; i < markers.length; i++) {
                        if (markers[i].record_id !== marker.record_id) {
                            google.maps.event.addListener(markers[i], 'click', function () {
                                infowindow.close();
                            });
                        }
                    }
                    infowindow.setContent(contentString);
                    infowindow.open(map, marker);

                    changeSelectedAppear(this);
                });
            }

            // Change state markers and shop in list onclick
            let changeSelectedAppear = function(clicked) {

                let $results = $(".results-store .results-content");
                for (var i = 0; i < markers.length; i++) {
                    //markers[i].setIcon(require.toUrl("images/icons/pin_off.svg"));
                    $results.removeClass("active");
                    $results.addClass("disabled");

                }
                //clicked.setIcon(require.toUrl("images/icons/pin_on.svg"));

                let $store_result = $results.filter("[data-marker='" + clicked.record_id + "']");
                $store_result.removeClass("disabled").addClass("active");
                smoothZoom(14, map.getZoom(), clicked.position);
                $store_result[0].scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'start'});

            }

            // full width template
            if (config.template == "full_width_sidebar" || config.template == "full_width_top") {
                $("body").addClass("full-width");
            }

            // the smooth zoom function
            let smoothZoom = function(max, cnt, center) {
                if (cnt >= max) {
                    return;
                } else {
                    z = google.maps.event.addListener(map, 'zoom_changed', function (event) {
                        google.maps.event.removeListener(z);
                        smoothZoom(max, cnt + 1, center);
                    });
                    setTimeout(function () {
                        map.setZoom(cnt);
                        map.panTo(center);
                    }, 80); // 80ms is what I found to work well on my system -- it might not work well on all systems
                }
            }

            let searchShops = function(searchTerms, search_marker) {

                deleteLastShops();

                // add filter status to coordinates
                searchTerms.boutique = + $("#input_boutique").is(':checked');
                searchTerms.servizio_su_misura = + $("#input_tailor").is(':checked');

                //Save the current search terms to easily repeat request on filter action
                lastSearch = {
                    marker: search_marker,
                    searchTerms: searchTerms
                }

                search_widget.search(searchTerms, config)
                    .then(function (shops_collection) {
                        createShops(shops_collection, search_marker);
                        centerMapOnPlace(search_marker);
                    });
            }
        };
    }
);
