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

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

    constructor (map, markerClusterer) {
        this.map = map;
        if (markerClusterer) {
            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);

        // NOTE: temporarily disabling the `setMap` method because `removeMarker` will use it to remove the
        //       marker from the map inducing a blank of about 250ms before it is re-added to the map.
        this.temporarilyDisableMapSetterOnMarker(
            marker.mapsMarker,
            () => {
                if (this.markerClusterer) {
                    this.markerClusterer.removeMarker(marker.mapsMarker, true);
                }
            }
        );
        marker.mapsMarker.setMap(this.map);

        trackOutdoorActiveIfConfigured(marker);
    }

    closeInfoWindow() {
        if (!this.infoWindow) {
            return;
        }

        this.infoWindow.close();

        if (this.infoWindow.currentMarker) {
            if (this.markerClusterer) {
                this.markerClusterer.addMarker(this.infoWindow.currentMarker.mapsMarker);
            }
            this.infoWindow.currentMarker = null;
        }
    }

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

    /**
     * @private
     * @returns {Promise<InfoBox>}
     */
    async initializeInfoBox() {
        const { default: InfoBox } = await import(
            /* webpackChunkName: "google-maps" */
            'exports-loader?InfoBox!../../../vendor/googlemaps/infobox'
        );

        return new InfoBox({
            alignBottom: true,
            disableAutoPan: false,
            infoBoxClearance: new google.maps.Size(60, 40),
            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;
        const infoWindowTriangleOffset = 18;

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

    /**
     * @param {google.Maps.Marker} mapsMarker
     * @param {function} cb
     * @private
     */
    temporarilyDisableMapSetterOnMarker(mapsMarker, cb) {
        const setMapOriginal = mapsMarker.setMap;
        mapsMarker.setMap = () => {};

        cb();

        mapsMarker.setMap = setMapOriginal;
    }
}
