( function( $ ) {
	'use strict';

	var themeStyles = {
		standard: null,
		silver: [
			{ elementType: 'geometry', stylers: [ { color: '#f5f5f5' } ] },
			{ elementType: 'labels.icon', stylers: [ { visibility: 'off' } ] },
			{ elementType: 'labels.text.fill', stylers: [ { color: '#616161' } ] },
			{ elementType: 'labels.text.stroke', stylers: [ { color: '#f5f5f5' } ] },
			{ featureType: 'administrative.land_parcel', elementType: 'labels.text.fill', stylers: [ { color: '#bdbdbd' } ] },
			{ featureType: 'poi', elementType: 'geometry', stylers: [ { color: '#eeeeee' } ] },
			{ featureType: 'poi', elementType: 'labels.text.fill', stylers: [ { color: '#757575' } ] },
			{ featureType: 'road', elementType: 'geometry', stylers: [ { color: '#ffffff' } ] },
			{ featureType: 'road.arterial', elementType: 'labels.text.fill', stylers: [ { color: '#757575' } ] },
			{ featureType: 'road.highway', elementType: 'geometry', stylers: [ { color: '#dadada' } ] },
			{ featureType: 'water', elementType: 'geometry', stylers: [ { color: '#c9c9c9' } ] }
		],
		retro: [
			{ elementType: 'geometry', stylers: [ { color: '#ebe3cd' } ] },
			{ elementType: 'labels.text.fill', stylers: [ { color: '#523735' } ] },
			{ elementType: 'labels.text.stroke', stylers: [ { color: '#f5f1e6' } ] },
			{ featureType: 'administrative', elementType: 'geometry.stroke', stylers: [ { color: '#c9b2a6' } ] },
			{ featureType: 'landscape.natural', elementType: 'geometry', stylers: [ { color: '#dfd2ae' } ] },
			{ featureType: 'poi', elementType: 'geometry', stylers: [ { color: '#dfd2ae' } ] },
			{ featureType: 'road', elementType: 'geometry', stylers: [ { color: '#f5f1e6' } ] },
			{ featureType: 'road.highway', elementType: 'geometry', stylers: [ { color: '#f8c967' } ] },
			{ featureType: 'water', elementType: 'geometry.fill', stylers: [ { color: '#b9d3c2' } ] }
		],
		dark: [
			{ elementType: 'geometry', stylers: [ { color: '#212121' } ] },
			{ elementType: 'labels.icon', stylers: [ { visibility: 'off' } ] },
			{ elementType: 'labels.text.fill', stylers: [ { color: '#757575' } ] },
			{ elementType: 'labels.text.stroke', stylers: [ { color: '#212121' } ] },
			{ featureType: 'poi', elementType: 'labels.text.fill', stylers: [ { color: '#757575' } ] },
			{ featureType: 'road', elementType: 'geometry', stylers: [ { color: '#383838' } ] },
			{ featureType: 'road.highway', elementType: 'geometry', stylers: [ { color: '#3c3c3c' } ] },
			{ featureType: 'transit', elementType: 'geometry', stylers: [ { color: '#2f2f2f' } ] },
			{ featureType: 'water', elementType: 'geometry', stylers: [ { color: '#000000' } ] }
		],
		night: [
			{ elementType: 'geometry', stylers: [ { color: '#242f3e' } ] },
			{ elementType: 'labels.text.fill', stylers: [ { color: '#746855' } ] },
			{ elementType: 'labels.text.stroke', stylers: [ { color: '#242f3e' } ] },
			{ featureType: 'administrative.locality', elementType: 'labels.text.fill', stylers: [ { color: '#d59563' } ] },
			{ featureType: 'poi', elementType: 'labels.text.fill', stylers: [ { color: '#d59563' } ] },
			{ featureType: 'road', elementType: 'geometry', stylers: [ { color: '#38414e' } ] },
			{ featureType: 'road.highway', elementType: 'geometry', stylers: [ { color: '#746855' } ] },
			{ featureType: 'transit', elementType: 'geometry', stylers: [ { color: '#2f3948' } ] },
			{ featureType: 'water', elementType: 'geometry', stylers: [ { color: '#17263c' } ] }
		],
		aubergine: [
			{ elementType: 'geometry', stylers: [ { color: '#1d2c4d' } ] },
			{ elementType: 'labels.text.fill', stylers: [ { color: '#8ec3b9' } ] },
			{ elementType: 'labels.text.stroke', stylers: [ { color: '#1a3646' } ] },
			{ featureType: 'administrative.country', elementType: 'geometry.stroke', stylers: [ { color: '#4b6878' } ] },
			{ featureType: 'administrative.land_parcel', elementType: 'labels.text.fill', stylers: [ { color: '#64779e' } ] },
			{ featureType: 'landscape.natural', elementType: 'geometry', stylers: [ { color: '#023e58' } ] },
			{ featureType: 'poi', elementType: 'geometry', stylers: [ { color: '#283d6a' } ] },
			{ featureType: 'road', elementType: 'geometry', stylers: [ { color: '#304a7d' } ] },
			{ featureType: 'road.highway', elementType: 'geometry', stylers: [ { color: '#2c6675' } ] },
			{ featureType: 'water', elementType: 'geometry', stylers: [ { color: '#0e1626' } ] }
		]
	};

	var apiState = {
		loaded: false,
		loading: false,
		key: '',
		queue: []
	};

	function runApiQueue() {
		var queue = apiState.queue.slice();
		apiState.queue = [];

		queue.forEach( function( callback ) {
			callback();
		} );
	}

	window.IAEWGoogleMapsInit = function() {
		apiState.loaded = true;
		apiState.loading = false;
		runApiQueue();
	};

	function loadGoogleMapsApi( apiKey, callback ) {
		if ( window.google && window.google.maps ) {
			apiState.loaded = true;
			callback();
			return;
		}

		apiState.queue.push( callback );

		if ( apiState.loading ) {
			return;
		}

		apiState.loading = true;
		apiState.key = apiKey;

		var script = document.createElement( 'script' );
		script.src = 'https://maps.googleapis.com/maps/api/js?key=' + encodeURIComponent( apiKey ) + '&callback=IAEWGoogleMapsInit';
		script.async = true;
		script.defer = true;
		script.onerror = function() {
			apiState.loading = false;
			apiState.queue = [];
		};

		document.head.appendChild( script );
	}

	function getMapOptions( settings ) {
		return {
			zoom: settings.zoom || 12,
			center: { lat: 37.422, lng: -122.0841 },
			styles: themeStyles[ settings.theme ] || null,
			mapTypeControl: false,
			streetViewControl: false,
			fullscreenControl: true
		};
	}

	function createInfoContent( item ) {
		var parts = [];

		if ( item.title ) {
			parts.push( '<strong>' + item.title + '</strong>' );
		}

		if ( item.address ) {
			parts.push( '<div>' + item.address + '</div>' );
		}

		if ( item.info ) {
			parts.push( '<p>' + item.info + '</p>' );
		}

		return parts.join( '' );
	}

	function hasCoordinates( item ) {
		return typeof item.lat === 'number' && ! Number.isNaN( item.lat ) && typeof item.lng === 'number' && ! Number.isNaN( item.lng );
	}

	function resolveLocation( geocoder, item ) {
		return new Promise( function( resolve, reject ) {
			if ( hasCoordinates( item ) ) {
				resolve( { lat: item.lat, lng: item.lng } );
				return;
			}

			if ( ! item.address ) {
				reject();
				return;
			}

			geocoder.geocode( { address: item.address }, function( results, status ) {
				if ( status === 'OK' && results && results[ 0 ] ) {
					var location = results[ 0 ].geometry.location;
					resolve( {
						lat: location.lat(),
						lng: location.lng()
					} );
					return;
				}

				reject( status );
			} );
		} );
	}

	function renderBasicMarker( map, geocoder, infoWindow, settings ) {
		resolveLocation( geocoder, settings.basicMarker || {} ).then( function( position ) {
			var marker = new google.maps.Marker( {
				position: position,
				map: map,
				title: settings.basicMarker.title || ''
			} );

			map.setCenter( position );

			if ( settings.basicMarker && ( settings.basicMarker.title || settings.basicMarker.address || settings.basicMarker.info ) ) {
				marker.addListener( 'click', function() {
					infoWindow.setContent( createInfoContent( settings.basicMarker ) );
					infoWindow.open( map, marker );
				} );
			}
		} );
	}

	function renderRoute( map, settings ) {
		if ( ! settings.route || ! settings.route.origin || ! settings.route.destination ) {
			return;
		}

		var directionsService = new google.maps.DirectionsService();
		var directionsRenderer = new google.maps.DirectionsRenderer( {
			map: map,
			suppressMarkers: false,
			polylineOptions: {
				strokeColor: '#1c7ed6',
				strokeWeight: 5
			}
		} );

		directionsService.route(
			{
				origin: settings.route.origin,
				destination: settings.route.destination,
				travelMode: google.maps.TravelMode[ settings.route.travelMode ] || google.maps.TravelMode.DRIVING
			},
			function( response, status ) {
				if ( status === 'OK' ) {
					directionsRenderer.setDirections( response );
				}
			}
		);
	}

	function highlightMarkerItem( $scope, index ) {
		var $items = $scope.find( '.iaew-google-map__marker-item' );
		$items.removeClass( 'is-active' );
		$items.eq( index ).addClass( 'is-active' );
	}

	function bindMarkerList( $scope, map, markers ) {
		var $items = $scope.find( '.iaew-google-map__marker-item' );

		if ( ! $items.length ) {
			return;
		}

		$items.off( 'click.iaewGoogleMap' ).on( 'click.iaewGoogleMap', function() {
			var index = parseInt( $( this ).data( 'marker-index' ), 10 );

			if ( Number.isNaN( index ) || ! markers[ index ] ) {
				return;
			}

			map.panTo( markers[ index ].getPosition() );
			map.setZoom( Math.max( map.getZoom(), 13 ) );
			google.maps.event.trigger( markers[ index ], 'click' );
			highlightMarkerItem( $scope, index );
		} );
	}

	function renderMultipleMarkers( $scope, map, geocoder, infoWindow, settings ) {
		var bounds = new google.maps.LatLngBounds();
		var markerInstances = [];
		var items = Array.isArray( settings.markers ) ? settings.markers : [];

		Promise.all(
			items.map( function( item ) {
				return resolveLocation( geocoder, item ).then( function( position ) {
					return {
						item: item,
						position: position
					};
				} ).catch( function() {
					return null;
				} );
			} )
		).then( function( resolvedItems ) {
			resolvedItems.forEach( function( result, index ) {
				if ( ! result ) {
					return;
				}

				var marker = new google.maps.Marker( {
					position: result.position,
					map: map,
					title: result.item.title || ''
				} );

				marker.addListener( 'click', function() {
					infoWindow.setContent( createInfoContent( result.item ) );
					infoWindow.open( map, marker );
					highlightMarkerItem( $scope, index );
				} );

				markerInstances[ index ] = marker;
				bounds.extend( result.position );
			} );

			if ( ! bounds.isEmpty() ) {
				map.fitBounds( bounds, 60 );
			}

			bindMarkerList( $scope, map, markerInstances );
			highlightMarkerItem( $scope, 0 );
		} );
	}

	function initMapWidget( $scope ) {
		var $canvas = $scope.find( '.iaew-google-map__canvas' ).first();

		if ( ! $canvas.length ) {
			return;
		}

		var rawSettings = $canvas.attr( 'data-map-settings' );

		if ( ! rawSettings ) {
			return;
		}

		var settings = {};

		try {
			settings = JSON.parse( rawSettings );
		} catch ( error ) {
			return;
		}

		if ( ! settings.apiKey ) {
			return;
		}

		loadGoogleMapsApi( settings.apiKey, function() {
			var map = new google.maps.Map( $canvas.get( 0 ), getMapOptions( settings ) );
			var geocoder = new google.maps.Geocoder();
			var infoWindow = new google.maps.InfoWindow();

			if ( settings.variant === 'with-routes' ) {
				renderRoute( map, settings );
				return;
			}

			if ( settings.variant === 'multiple-markers' ) {
				renderMultipleMarkers( $scope, map, geocoder, infoWindow, settings );
				return;
			}

			renderBasicMarker( map, geocoder, infoWindow, settings );
		} );
	}

	$( window ).on( 'elementor/frontend/init', function() {
		elementorFrontend.hooks.addAction( 'frontend/element_ready/impact_widgets_elementor_google-map.default', initMapWidget );
	} );
}( jQuery ) );
