<template>
  <div>
    <div
      id="geocoder-container"
      :class="{ isExpanded }"
    >
      <button
        class="button-geocoder"
        title="Rechercher une adresse"
        type="button"
        @click="toggleGeocoder"
      >
        <i class="search icon" />
      </button>
    </div>
    <div
      id="geocoder-select-container"
      :class="{ isExpanded }"
    >
      <!-- internal-search should be disabled to avoid filtering options, which is done by the api calls anyway https://stackoverflow.com/questions/57813170/vue-multi-select-not-showing-all-the-options -->
      <!-- otherwise approximate results are not shown (cannot explain why though) -->
      <Multiselect
        v-if="isExpanded"
        ref="multiselect"
        v-model="selection"
        class="expanded-geocoder"
        :options="addresses"
        :options-limit="limit"
        :allow-empty="true"
        :internal-search="false"
        track-by="id"
        label="label"
        :show-labels="false"
        :reset-after="true"
        select-label=""
        selected-label=""
        deselect-label=""
        :searchable="true"
        :placeholder="placeholder"
        :show-no-results="true"
        :loading="loading"
        :clear-on-select="false"
        :preserve-search="true"
        @search-change="search"
        @select="select"
        @open="retrievePreviousPlaces"
        @close="close"
      >
        <template
          slot="option"
          slot-scope="props"
        >
          <div class="option__desc">
            <span class="option__title">{{ props.option.label }}</span>
          </div>
        </template>
        <template slot="clear">
          <div
            v-if="selection"
            class="multiselect__clear"
            @click.prevent.stop="selection = null"
          >
            <i class="close icon" />
          </div>
        </template>
        <span slot="noResult">
          Aucun résultat.
        </span>
        <span slot="noOptions">
          Saisissez les premiers caractères ...
        </span>
      </Multiselect>
      <div style="display: none;">
        <div
          id="marker"
          title="Marker"
        />
      </div>
    </div>
  </div>
</template>

<script>
import Multiselect from 'vue-multiselect';
import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import axios from 'axios';
import mapService from '@/services/map-service';
const apiAdressAxios = axios.create({
  baseURL: 'https://api-adresse.data.gouv.fr',
  withCredentials: false,
});

export default {
  name: 'Geocoder',

  components: {
    Multiselect
  },

  data() {
    return {
      loading: false,
      limit: 10,
      selection: null,
      text: null,
      selectedAddress: null,
      addresses: [],
      resultats: [],
      placeholder: 'Rechercher une adresse ...',
      isExpanded: false
    };
  },

  mounted() {
    this.addressTextChange = new Subject();
    this.addressTextChange.pipe(debounceTime(200)).subscribe((res) => this.getAddresses(res));
  },

  methods: {
    toggleGeocoder() {
      this.isExpanded = !this.isExpanded;
      if (this.isExpanded) {
        this.retrievePreviousPlaces();
        this.$nextTick(()=> this.$refs.multiselect.activate());
      }
    },

    getAddresses(query){
      if (query.length < 3) {
        this.addresses = [];
        return;
      }
      const coords = mapService.getMapCenter();
      let url = `https://api-adresse.data.gouv.fr/search/?q=${query}&limit=${this.limit}`;
      if (coords) url += `&lon=${coords[0]}&lat=${coords[1]}`;
      apiAdressAxios.get(url)
        .then((retour) => {
          this.resultats = retour.data.features;
          this.addresses = retour.data.features.map(x=>x.properties);
        });
    },

    selectAddresse(event) {
      this.selectedAddress = event;
      if (this.selectedAddress !== null && this.selectedAddress.geometry) {
        let zoomlevel = 14;
        const { type } = this.selectedAddress.properties;
        if (type === 'housenumber') {
          zoomlevel = 19;
        } else if (type === 'street') {
          zoomlevel = 16;
        } else if (type === 'locality') {
          zoomlevel = 16;
        }
        // On ajoute un point pour localiser la ville
        mapService.addOverlay(this.selectedAddress.geometry.coordinates, zoomlevel);
        // On enregistre l'adresse sélectionné pour le proposer à la prochaine recherche
        this.setLocalstorageSelectedAdress(this.selectedAddress);
        this.toggleGeocoder();
      }
    },

    search(text) {
      this.text = text;
      this.addressTextChange.next(this.text);
    },

    select(e) {
      this.selectAddresse(this.resultats.find(x=>x.properties.label === e.label));
      this.$emit('select', e);
    },

    close() {
      this.$emit('close', this.selection);
    },

    setLocalstorageSelectedAdress(newAdress) {
      let selectedAdresses = JSON.parse(localStorage.getItem('geocontrib-selected-adresses'));
      selectedAdresses = Array.isArray(selectedAdresses) ? selectedAdresses : [];
      selectedAdresses = [ newAdress, ...selectedAdresses ];

      const uniqueLabels = [...new Set(selectedAdresses.map(el => el.properties.label))];
      const uniqueAdresses = uniqueLabels.map((label) => {
        return selectedAdresses.find(adress => adress.properties.label === label);
      });

      localStorage.setItem(
        'geocontrib-selected-adresses',
        JSON.stringify(uniqueAdresses.slice(0, 5))
      );
    },

    getLocalstorageSelectedAdress() {
      return JSON.parse(localStorage.getItem('geocontrib-selected-adresses')) || [];
    },

    retrievePreviousPlaces() {
      const previousAdresses = this.getLocalstorageSelectedAdress();
      if (previousAdresses.length > 0) {
        this.addresses = previousAdresses.map(x=>x.properties);
        this.resultats = previousAdresses;
      }
    },
  }
};
</script>

<style lang="less">
#marker {
    width: 14px;
    height: 14px;
    border: 2px solid #fff;
    border-radius: 7px;
    background-color: #3399CC;
}

#geocoder-container {
  position: absolute;
  right: 6px;
  // each button have (more or less depends on borders) .5em space between
  // zoom buttons are 60px high, geolocation and full screen button is 34px high with borders
  top: calc(1.6em + 60px + 34px + 34px);
  pointer-events: auto;
  z-index: 999;
  border: 2px solid rgba(0,0,0,.2);
  background-clip: padding-box;
  padding: 0;
  border-radius: 4px;
  display: flex;

  .button-geocoder {
    border: none;
    padding: 0;
    margin: 0;
    text-align: center;
    background-color: #fff;
    color: rgb(39, 39, 39);
    width: 30px;
    height: 30px;
    font: 700 18px Lucida Console,Monaco,monospace;
    border-radius: 2px;
    line-height: 1.15;

    i {
      margin: 0;
      font-size: 0.9em;
    }
  }
  .button-geocoder:hover {
    cursor: pointer;
    background-color: #ebebeb;
  }

  .expanded-geocoder {
    max-width: 400px;
  }

  &&.isExpanded {
    .button-geocoder {
      /*height: 41px;*/
      color: rgb(99, 99, 99);
      border-radius: 2px 0 0 2px;
    }
  }
}

#geocoder-select-container{
  position: absolute;
  right: 46px;
  // each button have (more or less depends on borders) .5em space between
  // zoom buttons are 60px high, geolocation and full screen button is 34px high with borders
  top: calc(1.6em + 60px + 34px + 34px - 4px);
  pointer-events: auto;
  z-index: 999;
  border: 2px solid rgba(0,0,0,.2);
  background-clip: padding-box;
  padding: 0;
  border-radius: 4px;
  display: flex;
  // /* keep placeholder width when opening dropdown */
  .multiselect {
    min-width: 208px;
  }
  /* keep font-weight from overide of semantic classes */
  .multiselect__placeholder, .multiselect__content, .multiselect__tags  {
    font-weight: initial !important;
  }
  /* keep placeholder eigth */
  .multiselect .multiselect__placeholder {
    margin-bottom: 9px !important;
    padding-top: 1px;
  }
  /* keep placeholder height when opening dropdown without selection */
  input.multiselect__input {
    padding: 3px 0 0 0 !important;
  }
  /* keep placeholder height when opening dropdown with already a value selected */
  .multiselect__tags .multiselect__single {
    padding: 1px 0 0 0 !important;
    margin-bottom: 9px;
  }
  .multiselect__tags {
    border: 0 !important;
    min-height: 41px !important;
    padding: 10px 40px 0 8px;
  }
  .multiselect input {
    line-height: 1em !important;
    padding: 0 !important;
  }
  .multiselect__content-wrapper {
    border: 2px solid rgba(0,0,0,.2);
  }

}
</style>