import { trackOutdoorActiveIfConfigured } from '../../utilities';

/** @implements {MapWithInfoWindow} */
export class InfoWindowHandler {

    constructor (map, markerClusterer) {
        this.map = map;
        this.markerClusterer = markerClusterer;
        this.initializeInfoBox().then(infoBox => this.infoWindow = infoBox);
    }

    /**
     * @public
     * @param {Marker} marker
     * @param {string} content
     */
    openInfoWindow (marker, content) {
        if (!this.infoWindow) {
            setTimeout(() => this.openInfoWindow(marker, content), 250);
            return;
        }

        this.infoWindow.currentMarker = marker;
        this.updateMapItemPositions(marker);
        this.infoWindow.setContent(content);
        this.infoWindow.open(this.map, marker.mapsMarker);

        trackOutdoorActiveIfConfigured(marker);
    }

    closeInfoWindow() {
        this.infoWindow.close();
        this.infoWindow.currentMarker = null;
    }

    /**
     * @private
     * @param {Marker} marker
     */
    updateMapItemPositions(marker) {
        this.infoWindow.setOptions({
            pixelOffset: new google.maps.Size(...this.getInfoWindowOffset(marker)),
        });

        if (this.map.getZoom() < 12) {
            this.map.setZoom(12);
        }
        this.map.setCenter({ lat: marker.coordinates.latitude, lng: marker.coordinates.longitude });
    }

    /**
     * @private
     * @returns {Promise<InfoBox>}
     */
    async initializeInfoBox() {
        const { default: InfoBox } = await import('exports-loader?InfoBox!../../../vendor/googlemaps/infobox');
        this.onPopupMarkerClusteringStateChange(isClustered => {
            if(isClustered) {
                this.closeInfoWindow();
            }
        });

        return new InfoBox({
            alignBottom: true,
            disableAutoPan: true,
            boxClass: 'tb-map-info-window-wrapper',
            boxStyle: { overflow: 'hidden' },
            closeBoxURL: '',
        });
    }

    /**
     * TODO: Remove magic number if possible.
     *
     * Currently the width of info windows is set to 266px
     * via CSS. We need to offset the horizontal position
     * by 50% of that to keep horizontal center.
     *
     * The vertical offset is the height of the marker.
     *
     * @param {Marker} marker
     * @returns {number[]}
     * @private
     */
    getInfoWindowOffset (marker) {
        const infoWindowWidth = 266;

        return [
            -1 * (infoWindowWidth / 2),
            -1 * marker.style.height,
        ];
    }

    /**
     * @private
     * @param {Function} listener
     */
    onPopupMarkerClusteringStateChange(listener) {
        this.markerClusterer.addListener('clusteringend', () => {
            if (!this.infoWindow.currentMarker) {
                return;
            }

            const isClustered = this.isMarkerClustered(this.infoWindow.currentMarker.mapsMarker);
            listener(isClustered);
        });
    }

    /**
     * @private
     * @param {google.maps.Marker} marker
     * @returns {boolean}
     */
    isMarkerClustered(marker) {
        for (const cluster of this.markerClusterer.getClusters()) {
            const clusteredMarkers = cluster.getMarkers();

            // Clusters with only 1 marker are the marker itself
            if (clusteredMarkers.length <= 1) {
                continue;
            }

            for (const clusteredMarker of clusteredMarkers) {
                if (clusteredMarker === marker && clusteredMarker.getMap() == null) {
                    return true;
                }
            }
        }

        return false;
    }

}
