/**
 * maps.js
 * load google maps with markers and related popups
 */

import { GoogleMapsApi } from './maps/google-maps-api';
import { MapStyle } from './maps/map-style';
import { PopupWrapper } from './maps/popup-wrapper';
import MarkerClusterer from '@googlemaps/markerclustererplus';

/**
 * Declare constants
 */
const attributeBase = 'data-rs-maps';
let Popup = null;

export class Maps {
  constructor(element) {
    this.map = null;
    this.element = element;
    this.locations = [];
    this.marker = null;
    this.popup = null;
    // get elements
    this.googleMapsKey =
      this.element.hasAttribute(this.attributes.googleMapsKey) &&
      this.element.getAttribute(this.attributes.googleMapsKey);
    this.markerUrlPath = this.element.hasAttribute(this.attributes.markerUrl)
      ? this.element.getAttribute(this.attributes.markerUrl)
      : this.variables.defaultMarkerUrl;
    this.clusterMarkerUrlPath = this.element.hasAttribute(
      this.attributes.clusterMarkerUrl
    )
      ? this.element.getAttribute(this.attributes.clusterMarkerUrl)
      : this.variables.defaultClusterMarkerUrl;
    this.markerIsNotClickable =
      this.element.hasAttribute(this.attributes.markerIsNotClickable) || false;
    this.mapPlaceholderElement = this.element.querySelector(
      this.attributes.mapsPlaceholderElement
    );
    this.mapElement = this.mapPlaceholderElement.appendChild(
      document.createElement('div')
    );
    this.clusterEnabled = this.element.getAttribute(this.attributes.cluster);
    this.initializeMap();
  }

  /**
   * Declare attribute constants
   */
  get attributes() {
    return {
      clusterMarkerUrl: `${attributeBase}-cluster-marker-url`,
      markerUrl: `${attributeBase}-marker-url`,
      markerIsNotClickable: `${attributeBase}-marker-is-not-clickable`,
      mapsPlaceholderElement: `[${attributeBase}-placeholder]`,
      googleMapsKey: `${attributeBase}-google-maps-key`,
      cluster: `${attributeBase}-cluster`
    };
  }

  /**
   * Declare variable constants
   */
  get variables() {
    return {
      defaultMarkerUrl: '../_assets/image/marker.svg',
      defaultClusterMarkerUrl: '../_assets/image/m'
    };
  }

  /**
   * Load google maps API
   */
  initializeMap() {
    // create map, controls and markers after the API is ready
    const gmapApi = new GoogleMapsApi(this.googleMapsKey);
    gmapApi
      .load()
      .then(() => {
        this.createMap();
        this.customZoomControl(this.mapPlaceholderElement);
        this.handleMarkers();
        this.addMarker(this.locations);
        if (this.clusterEnabled) {
          this.setClusterMarkers();
        }
      })
      .catch((reason) => {
        console.error(reason);
      });
  }

  createMap() {
    //retrieve center location
    let zoom = parseInt(this.element.getAttribute('data-rs-maps-zoom'));
    if (isNaN(zoom)) {
      zoom = 6;
    }
    // get center of map
    const [centerLat, centerLng] = this.element
      .getAttribute('data-rs-maps')
      .split(',');
    // create map
    this.map = new google.maps.Map(this.mapPlaceholderElement, {
      center: new google.maps.LatLng(centerLat, centerLng),
      zoom: zoom,
      minZoom: 4,
      maxZoom: 17,
      disableDefaultUI: true,
      panControl: true,
      zoomControl: false,
      scaleControl: true,
      streetViewControl: false,
      styles: MapStyle.custom,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    });
  }

  /**
   * Create Popup class if not already exists
   * always AFTER the map is created
   */
  createPopup() {
    if (Popup === null) {
      Popup = PopupWrapper.popupClass;
    }
  }

  /**
   * Retrieve markers from html elements with attribute 'data-rs-map-marker'
   * and add them to the map
   */

  handleMarkers() {
    // create the popup class
    this.createPopup();
    this.locations = [].slice
      .call(this.element.querySelectorAll('[data-rs-map-marker]'))
      .map((markerElement) => {
        //build marker item
        let [lat, lng] = markerElement
          .getAttribute('data-rs-map-marker')
          .split(',');
        const item = {};
        item.lat = lat.trim();
        item.lng = lng.trim();
        item.content = document.createElement('div');
        item.content.innerHTML = markerElement.innerHTML;

        return item;
      });

    // handle click on map related to popups
    google.maps.event.addListener(this.map, 'click', () => {
      if (this.marker !== null && typeof this.marker !== 'undefined') {
        this.marker.open = false;
        this.marker.customPopup.hide();
      }
    });
  }

  /**
   * Add marker to the map
   * @param {any} item Object containing base settings
   */
  addMarker(locations) {
    // create icon for marker
    let icon = {
      url: this.markerUrlPath,
      size: new google.maps.Size(34, 48)
    };

    this.markers = locations.map((item) => {
      let popup = new Popup(
        new google.maps.LatLng(item.lat, item.lng),
        item.content
      );

      const marker = new google.maps.Marker({
        position: new google.maps.LatLng(item.lat, item.lng),
        icon: icon,
        map: this.map,
        customPopup: popup
      });

      popup.marker = marker;

      if (!this.markerIsNotClickable) {
        google.maps.event.addListener(marker, 'click', () => {
          // determine to show or hide the popup
          let openMarker = false;
          if (marker.open !== true) {
            openMarker = true;
          }
          if (this.marker !== null && marker !== this.marker) {
            this.marker.open = false;
            this.marker.customPopup.hide();
          }

          // set active marker
          this.marker = marker;

          // handle display popup
          if (openMarker) {
            this.marker.customPopup.setMap(this.marker.map);
            this.marker.open = true;
          } else {
            this.marker.customPopup.hide();
            this.marker.open = false;
          }
        });
      }

      return marker;
    });
  }

  setClusterMarkers() {
    const clusterOptions = {
      imagePath: this.clusterMarkerUrlPath,
      imageExtension: 'svg'
    };

    const markerClusterer = new MarkerClusterer(
      this.map,
      this.markers,
      clusterOptions
    );

    // Change styles after cluster is created
    const styles = markerClusterer.getStyles();
    for (let i = 0; i < styles.length; i++) {
      styles[i].textColor = 'white';
      styles[i].textSize = 16;
      styles[i].width = 58;
      styles[i].height = 66;
      styles[i].textLineHeight = 66;
    }
  }

  /**
   * Create a custom zoom control
   * @param {any} controlContainer
   */
  customZoomControl(controlContainer) {
    // hide control container
    let controlWrapper = controlContainer.appendChild(
      document.createElement('div')
    );
    controlWrapper.classList.add('gmaps__control-container');
    controlWrapper.classList.add('gmaps__control-container--loading');

    // create element zoom in/out button
    let zoomInButton = document.createElement('div');
    let zoomOutButton = document.createElement('div');

    // create classes for buttons
    zoomInButton.classList.add('gmaps__button--zoom');
    zoomInButton.classList.add('gmaps__button--zoom-in');
    zoomOutButton.classList.add('gmaps__button--zoom');
    zoomOutButton.classList.add('gmaps__button--zoom-out');

    // append controls
    controlWrapper.appendChild(zoomInButton);
    controlWrapper.appendChild(zoomOutButton);

    // Setup the click event listener - zoomIn
    google.maps.event.addDomListener(zoomInButton, 'click', () => {
      this.map.setZoom(this.map.getZoom() + 1);
    });

    // Setup the click event listener - zoomOut
    google.maps.event.addDomListener(zoomOutButton, 'click', () => {
      this.map.setZoom(this.map.getZoom() - 1);
    });
  }

  /**
   * Get selector
   */
  static getSelector() {
    return `[${attributeBase}]`;
  }
}
