import L from 'leaflet';
import 'leaflet-draw';
// This was added to fix a bug in the version Leaflet.draw. https://github.com/Leaflet/Leaflet.draw/issues/1026
if (typeof window !== 'undefined') {
  window.type = true;
}
const MIN_LAT = -90;
const MAX_LAT = 90;
const MIN_LNG = -180;
const MAX_LNG = 180;
export class Leaflet {
  constructor(drawOptions) {
    this.drawOptions = drawOptions;
    this.listeners = [];
    this.mapboxLayer = L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
      attribution: '© <a href="https://www.mapbox.com/about/maps/">Mapbox</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> <strong><a href="https://www.mapbox.com/map-feedback/" target="_blank">Improve this map</a></strong>',
      tileSize: 512,
      maxZoom: 18,
      zoomOffset: -1,
      id: 'mapbox/streets-v11',
      accessToken: 'pk.eyJ1Ijoiam9uY2FybHNvbm5hc2EiLCJhIjoiY2ptbTYyOWI5MGZwajNxbWxjdmYweTE5eiJ9.PgiaxHKrA5elPqzN7Ask6w',
    });
    this.drawOptions = drawOptions;
  }
  initializeMap(container, options) {
    if (this.map) {
      this.map.off();
      this.map.remove();
    }
    const southWest = L.latLng(-90, -360);
    const northEast = L.latLng(90, 360);
    const maxBounds = L.latLngBounds(southWest, northEast);
    this.map = L.map(container, {
      zoomControl: false,
      maxBounds,
      maxBoundsViscosity: 0.8,
    });
    this.setView(options);
    this.mapboxLayer.addTo(this.map);
  }
  setView(options) {
    this.map.setView([options.latitude, options.longitude], options.zoom, {
      minZoom: options.minZoom,
      maxZoom: options.maxZoom,
    });
  }
  setZoom(zoomLevel) {
    this.map.setZoom(zoomLevel);
  }
  addNavigationControl(position) {
    this.navigationControl = L.control.zoom({ position });
    this.navigationControl.addTo(this.map);
  }
  removeNavigationControl() {
    if (!this.navigationControl)
      return;
    this.navigationControl.remove();
    this.navigationControl = null;
  }
  addDrawControl(position = 'topleft') {
    this.drawnItems = new L.FeatureGroup();
    this.drawnItems.addTo(this.map);
    this.drawControl = new L.Control.Draw({
      position: position,
      draw: Object.assign({ polyline: false, polygon: false, circlemarker: false }, this.drawOptions),
      edit: {
        featureGroup: this.drawnItems,
      },
    });
    this.drawControl.addTo(this.map);
    this.map.on(L.Draw.Event.DRAWSTART, e => {
      const { layerType } = e;
      const mapDrawClicked = new CustomEvent('mapDrawClicked', {
        bubbles: true,
        detail: {
          layerType: layerType,
        },
      });
      e.target._container.dispatchEvent(mapDrawClicked);
    });
    this.map.on(L.Draw.Event.CREATED, e => {
      this.drawnItems.clearLayers();
      const { layerType } = e;
      if (layerType === 'rectangle') {
        // Do marker specific actions
        this.drawnItems.addLayer(e.layer);
        this.trigger('draw', Object.assign({ layerType: layerType }, this.drawnItems.getLayers()[0]._bounds));
      }
      if (layerType === 'circle') {
        // Do marker specific actions
        this.drawnItems.addLayer(e.layer);
        this.trigger('draw', Object.assign(Object.assign({ layerType: layerType }, this.drawnItems.getLayers()[0]._latlng), { radius: this.drawnItems.getLayers()[0]._mRadius }));
      }
      if (layerType === 'marker') {
        // Do marker specific actions
        this.drawnItems.addLayer(e.layer);
        this.trigger('draw', Object.assign({ layerType: layerType }, this.drawnItems.getLayers()[0]._latlng));
      }
    });
    this.map.on(L.Draw.Event.EDITED, () => this.trigger('draw', this.drawnItems.getLayers()[0]));
    this.map.on(L.Draw.Event.DELETED, () => this.trigger('clear'));
    window.addEventListener('clearSpatialInput', () => {
      this.drawnItems.clearLayers();
    });
  }
  removeDrawControl() {
    if (!this.drawControl)
      return;
    this.drawControl.remove();
    this.drawControl = null;
  }
  drawRectangle(bounds) {
    this.drawnItems.clearLayers();
    L.rectangle(bounds, {
      stroke: true,
      color: '#3388ff',
      weight: 4,
      opacity: 0.5,
      fill: true,
      fillColor: null,
      fillOpacity: 0.2,
    }).addTo(this.drawnItems);
  }
  drawCircle(circle) {
    this.drawnItems.clearLayers();
    L.circle([+circle.lat, +circle.lng], { radius: +circle.radius }).addTo(this.drawnItems);
  }
  drawMarker(marker) {
    this.drawnItems.clearLayers();
    L.marker([+marker.lat, +marker.lng]).addTo(this.drawnItems);
  }
  refreshSize() {
    this.map.invalidateSize();
  }
  fitWorld() {
    this.map.fitBounds([
      [-180, -90],
      [180, 90],
    ]);
  }
  on(eventName, callback) {
    this.listeners.push({
      eventName,
      callback,
    });
  }
  trigger(eventName, detail = null) {
    this.listeners.filter(listener => listener.eventName === eventName).forEach(listener => listener.callback(detail));
  }
  normalizePoint({ lat, lng }) {
    return this.map.wrapLatLng({ lat, lng });
  }
  normalizeLat(lat) {
    if (lat > MAX_LAT)
      return MAX_LAT;
    else if (lat < MIN_LAT)
      return MIN_LAT;
    return lat;
  }
  normalizeLng(lng) {
    if (lng < MIN_LNG || lng > MAX_LNG) {
      let rem = lng % MAX_LNG;
      return (rem < 0 ? MAX_LNG : MIN_LNG) + rem;
    }
    return lng;
  }
  normalizeBox(box) {
    return [this.normalizeLng(parseFloat(box[0])), this.normalizeLat(parseFloat(box[1])), this.normalizeLng(parseFloat(box[2])), this.normalizeLat(parseFloat(box[3]))];
  }
}
