<template>
  <div class="viewer-container">
    <MultiFlatmapVuer
      :availableSpecies="availableSpecies"
      @flatmapChanged="flatmapChanged"
      @ready="multiFlatmapReady"
      :state="entry.state"
      :mapManager="mapManager"
      @resource-selected="flatmaprResourceSelected(entry.type, $event)"
      style="height: 100%; width: 100%"
      :initial="entry.resource"
      :helpMode="helpMode"
      :helpModeActiveItem="helpModeActiveItem"
      :helpModeDialog="useHelpModeDialog"
      @help-mode-last-item="onHelpModeLastItem"
      @shown-tooltip="onTooltipShown"
      @shown-map-tooltip="onMapTooltipShown"
      @annotation-open="onAnnotationOpen"
      @annotation-close="onAnnotationClose"
      :annotationSidebar="annotationSidebar"
      @connectivity-info-open="onConnectivityInfoOpen"
      @connectivity-graph-error="onConnectivityGraphError"
      :connectivityInfoSidebar="connectivityInfoSidebar"
      ref="multiflatmap"
      :displayMinimap="true"
      :showStarInLegend="showStarInLegend"
      :enableOpenMapUI="true"
      :openMapOptions="openMapOptions"
      :flatmapAPI="flatmapAPI"
      :sparcAPI="apiLocation"
      @pan-zoom-callback="flatmapPanZoomCallback"
      @open-map="openMap"
      @finish-help-mode="endHelp"
      @pathway-selection-changed="onPathwaySelectionChanged"
      @open-pubmed-url="onOpenPubmedUrl"
      @mapmanager-loaded="onMapmanagerLoaded"
    />

    <HelpModeDialog
      v-if="helpMode && useHelpModeDialog"
      ref="multiflatmapHelp"
      :multiflatmapRef="multiflatmapRef"
      :lastItem="helpModeLastItem"
      @show-next="onHelpModeShowNext"
      @finish-help-mode="onFinishHelpMode"
    />
  </div>
</template>

<script>
/* eslint-disable no-alert, no-console */
import Tagging from '../../services/tagging.js';
import ContentMixin from "../../mixins/ContentMixin";
import EventBus from "../EventBus";
import {
  capitalise,
  availableSpecies,
  getBodyScaffoldInfo,
  transformObjToString
} from "../scripts/utilities";
import DyncamicMarkerMixin from "../../mixins/DynamicMarkerMixin";

import YellowStar from "../../icons/yellowstar";

import { MultiFlatmapVuer } from "@abi-software/flatmapvuer";
import "@abi-software/flatmapvuer/dist/style.css";
import { HelpModeDialog } from '@abi-software/map-utilities'
import '@abi-software/map-utilities/dist/style.css'

const getOpenMapOptions = (species) => {
  const options = [
    {
      display: "Open AC Map",
      key: "AC"
    },
    {
      display: "Open FC Map",
      key: "FC"
    },
    {
      display: "Open 3D Human Map",
      key: "3D"
    },
  ]
  switch (species) {
    case "Human Male":
    case "Human Female":
    case "Rat":
      options.push({
        display: "Open Sync Map",
        key: "SYNC"
      });
      break;
    default:
      break;
  }
  return options;
}

export default {
  name: "MultiFlatmap",
  mixins: [ContentMixin, DyncamicMarkerMixin],
  components: {
    MultiFlatmapVuer,
    HelpModeDialog,
  },
  data: function () {
    return {
      zoomLevel: 6,
      flatmapReady: false,
      availableSpecies: availableSpecies(),
      scaffoldResource: { },
      showStarInLegend: false,
      openMapOptions: getOpenMapOptions("Human Male"),
    }
  },
  methods: {
    /**
     * Toggle sync mode on/off depending on species and current state
     */
    toggleSyncMode: async function () {
      if (this.syncMode == false) {
        let action = undefined;
        if (this.activeSpecies === "Rat") {
          action = {
            contextCard: undefined,
            discoverId: undefined,
            label: "Rat Body",
            resource: "https://mapcore-bucket1.s3.us-west-2.amazonaws.com/WholeBody/31-May-2021/ratBody/ratBody_syncmap_metadata.json",
            title: "View 3D scaffold",
            layout: "2horpanel",
            type: "SyncMap",
          };
        } else if ((this.activeSpecies === "Human Male") || (this.activeSpecies === "Human Female")) {
          //Dynamically construct the whole body scaffold for human and store it
          if (!("human" in this.scaffoldResource)) {
            this.scaffoldResource["human"] = await getBodyScaffoldInfo(this.apiLocation, "human");
          }
          action = {
            contextCardUrl: this.scaffoldResource["human"].datasetInfo.contextCardUrl,
            discoverId: this.scaffoldResource["human"].datasetInfo.discoverId,
            s3uri: this.scaffoldResource["human"].datasetInfo.s3uri,
            version: this.scaffoldResource["human"].datasetInfo.version,
            label: "Human Body",
            resource: this.scaffoldResource["human"].url,
            title: "View 3D scaffold",
            layout: "2vertpanel",
            type: "SyncMap",
            isBodyScaffold: true,
          };
        }
        if (action)
          EventBus.emit("SyncModeRequest", { flag: true, action: action });
      } else {
        EventBus.emit("SyncModeRequest", { flag: false });
      }
    },
    getState: function () {
      if (this.flatmapReady) return this.$refs.multiflatmap.getState();
      else return undefined;
    },
    flatmapPanZoomCallback: function (payload) {
      if (this.mouseHovered) {
        const result = {
          paneIndex: this.entry.id,
          eventType: "panZoom",
          payload: payload,
          type: this.entry.type,
        };
        this.$emit("resource-selected", result);
      }
    },
    /**
     * Perform a local search on this contentvuer
     */
    search: function (term) {
      const flatmap = this.$refs.multiflatmap.getCurrentFlatmap();
      //First search and show the result
      return flatmap.searchAndShowResult(term, true);
    },
    /**
     * Append the list of suggested terms to suggestions
     */
    searchSuggestions: function (term, suggestions) {
      const flatmap = this.$refs.multiflatmap.getCurrentFlatmap();
      if (term && flatmap.mapImp) {
        const results = flatmap.mapImp.search(term);
        const featureIds = results.__featureIds || results.featureIds;
        featureIds.forEach(id => {
          const annotation = flatmap.mapImp.annotation(id);
          if (annotation && annotation.label)
            suggestions.push(annotation.label);
        });
      }
    },
    flatmaprResourceSelected: function (type, resource) {
      const map = this.$refs.multiflatmap.getCurrentFlatmap();
      this.resourceSelected(type, resource);

      if (resource.eventType === 'click' && resource.feature.type === 'feature') {
        const eventData = {
          label: resource.label || '',
          id: resource.feature.id || '',
          featureId: resource.feature.featureId || '',
          taxonomy: resource.taxonomy || '',
          resources: resource.resource.join(', ')
        };
        const paramString = transformObjToString(eventData);
        // `transformStringToObj` function can be used to change it back to object
        Tagging.sendEvent({
          'event': 'interaction_event',
          'event_name': 'portal_maps_connectivity',
          'category': paramString,
          "location": type + ' ' + map.viewingMode
        });
      }
    },
    onPathwaySelectionChanged: function (data) {
      const { label, property, checked, selectionsTitle } = data;
      // GA Tagging
      // Event tracking for maps' pathway selection change
      Tagging.sendEvent({
        'event': 'interaction_event',
        'event_name': 'portal_maps_pathway_change',
        'category': label + ' [' + property + '] ' + checked,
        'location': selectionsTitle
      });
    },
    onOpenPubmedUrl: function (url) {
      // GA Tagging
      // Event tracking for open pubmed url from popup
      Tagging.sendEvent({
        'event': 'interaction_event',
        'event_name': 'portal_maps_pubmed_url',
        'file_path': url,
        'location': 'map_popup_button',
      });
    },
    /**
     * Handle sync pan zoom event
     */
    handleSyncPanZoomEvent: function (data) {
      //Prevent recursive callback
      if (!this.mouseHovered) {
        if (data.type !== this.entry.type) {
          const zoom = data.payload.zoom;
          const center = data.payload.target;
          const height = this.$el.clientHeight;
          const width = this.$el.clientWidth;
          const max = Math.max(width, height);
          let sW = width / max / zoom;
          const sH = height / max / zoom;
          const origin = [
            center[0] / 2 + 0.5 - sW / 2,
            0.5 - center[1] / 2 - sH / 2,
          ];
          this.$refs.multiflatmap
            .getCurrentFlatmap()
            .mapImp.panZoomTo(origin, [sW, sH]);
        }
      }
    },
    displayTooltip: function (info) {
      if (info) {
        let name = info.name;
        if (name) {
          this.search(name);
        } else {
          const flatmap = this.$refs.multiflatmap.getCurrentFlatmap();
          flatmap.mapImp.clearSearchResults();
        }
      }
    },
    zoomToFeatures: function (info, forceSelect) {
      let name = info.name;
      const flatmap = this.$refs.multiflatmap.getCurrentFlatmap().mapImp;
      if (name) {
        const results = flatmap.search(name);
        if (results.featureIds.length > 0) {
          if (forceSelect) {
            flatmap.selectFeatures(results.featureIds);
          }
          flatmap.zoomToFeatures(results.featureIds);
        } else {
          flatmap.clearSearchResults();
        }
      } else {
        flatmap.clearSearchResults();
      }
    },
    highlightFeatures: function (info) {
      let name = info.name;
      const flatmap = this.$refs.multiflatmap.getCurrentFlatmap().mapImp;
      if (name) {
        const results = flatmap.search(name);
        if (results.featureIds.length > 0) {
          flatmap.zoomToFeatures(results.featureIds, { noZoomIn: true });
          /*
          flatmap.highlightFeatures([
            flatmap.modelForFeature(results.featureIds[0]),
          ]);
          */
        }
      }
    },
    updateProvCard: function() {
      const imp = this.getFlatmapImp();
      if (imp) {
        let provClone = {id: this.entry.id, prov: imp.provenance};
        EventBus.emit("mapImpProv", provClone);
        this.$emit("flatmap-provenance-ready", provClone);
      }
    },
    flatmapChanged: async function (activeSpecies) {
      this.activeSpecies = activeSpecies;
      this.openMapOptions = getOpenMapOptions(activeSpecies);
      this.$emit("species-changed", activeSpecies);
      if (!(this.entry.state && (this.entry.state.species === this.activeSpecies))) {
        if (this.syncMode == true)
          await this.toggleSyncMode();
      }
      this.updateProvCard();

      // GA Tagging
      // Event tracking for maps' species change
      Tagging.sendEvent({
        'event': 'interaction_event',
        'event_name': 'portal_maps_species_change',
        'category': this.activeSpecies
      });
    },
    multiFlatmapReady: function (flatmap) {
      if (flatmap) {
        flatmap.enablePanZoomEvents(true); // Use zoom events for dynamic markers
        this.flatmapReady = true;
        const flatmapImp = flatmap.mapImp;
        this.flatmapMarkerUpdate(flatmapImp);
        this.updateProvCard();
        this.loadConnectivityKnowledge(flatmapImp);
        EventBus.emit("mapLoaded", flatmap);
      }
    },
    getFlatmapImp: function () {
      if (this.entry.type === "MultiFlatmap" && this.flatmapReady && this.$refs.multiflatmap) {
        return this.$refs.multiflatmap.getCurrentFlatmap()["mapImp"];
      } else {
        return undefined;
      }
    },
    flatmapAreaSearch() {
      const flatmapImp = this.getFlatmapImp();
      let shownMarkers = flatmapImp.visibleMarkerAnatomicalIds();
      let returnedAction = {
        type: "Facets",
        label: "Unused",
        val: shownMarkers.map(marker => this.idNamePair[marker]),
      };
      EventBus.emit("PopoverActionClick", returnedAction);
    },
    restoreFeaturedMarkers: function (flatmap) {

      this.settingsStore.resetFeaturedMarkerIdentifier();
      const markers = this.settingsStore.featuredMarkers;
      this.updateFeaturedMarkers(markers, flatmap);
    },
    // updateFeaturedMarkers will step through the featured markers and add them to the map
    updateFeaturedMarkers: function (markers, flatmap) {
      this.showStarInLegend = false; // will show if we have a featured marker
      for (let index = 0; index < markers.length; ++index) {
        if (markers[index]) {
          const markerIdentifier =
            this.settingsStore.featuredMarkerIdentifiers[index];
          if (!markerIdentifier) {
            // Add the featured marker to the legend if we have a featured marker
            const markerExists = this.addFeaturedMarker(markers[index], index, flatmap);
            if (markerExists) {
              this.showStarInLegend = true;
            }
          }
        }
      }
    },
    // addFeaturedMarker: add a featured marker to the map at the specified uberon location
    addFeaturedMarker: function (marker, index, flatmap) {
      const markerSpecies =
        this.settingsStore.featuredMarkerSpecies[index];
      if (markerSpecies && !this.activeSpecies.startsWith(markerSpecies)) {
        return false;
      }
      let flatmapImp = flatmap;
      if (!flatmapImp) {
        flatmapImp = this.getFlatmapImp();
      }

      if (flatmapImp) {
        // create the star marker
        let wrapperElement = document.createElement("div");
        wrapperElement.innerHTML = YellowStar;

        // add it to the flatmap
        const markerIdentifier = flatmapImp.addMarker(marker, {
          element: wrapperElement,
          className: "highlight-marker",
          cluster: false
        });

        // update the store with the marker identifier
        this.settingsStore.updateFeaturedMarkerIdentifier({
          index,
          markerIdentifier,
        });
        return true;
      }
      return false;
    },
    /**
     * Change the view mode of the current flatmap
     */
    changeViewingMode: function (modeName) {
      const flatmap = this.$refs.multiflatmap.getCurrentFlatmap();
      flatmap.changeViewingMode(modeName);
    },
    showConnectivityTooltips: function (payload) {
      if (this.flatmapReady) {
        const flatmap = this.$refs.multiflatmap.getCurrentFlatmap();
        flatmap.showConnectivityTooltips(payload);
      }
    },
    changeConnectivitySource: function (payload) {
      if (this.flatmapReady) {
        const flatmap = this.$refs.multiflatmap.getCurrentFlatmap();
        flatmap.changeConnectivitySource(payload);
      }
    },
  },
  computed: {
    facetSpecies() {
      return this.settingsStore.facets.species;
    },
    featuredMarkers() {
      return this.settingsStore.featuredMarkers;
    },
  },
  watch: {
    syncMode: function (val) {
      if (this.$refs.multiflatmap.getCurrentFlatmap())
        this.$refs.multiflatmap.getCurrentFlatmap().enablePanZoomEvents(val);
    },
    featuredMarkers: function (markers) {
      if (!this.flatmapReady) {
        return;
      }

      this.updateFeaturedMarkers(markers, undefined);
    },
  },
  mounted: function () {
    this.getFeaturedDatasets();

    EventBus.on('annotation-close', () => {
      if (this.flatmapReady && this.$refs.multiflatmap) {
        const currentFlatmap = this.$refs.multiflatmap.getCurrentFlatmap();
        currentFlatmap.annotationEventCallback({}, { type: 'aborted' })
      }
    });

    EventBus.on('show-connectivity', (payload) => {
      const { featureIds, offset } = payload;
      if (this.flatmapReady && this.$refs.multiflatmap) {
        const currentFlatmap = this.$refs.multiflatmap.getCurrentFlatmap();
        if (currentFlatmap) {
          currentFlatmap.moveMap(featureIds, {
            offsetX: offset ? -150 : 0,
            zoom: 4,
          });
        }
      }
    });

    EventBus.on('show-reference-connectivities', (payload) => {
      if (this.flatmapReady && this.$refs.multiflatmap) {
        const currentFlatmap = this.$refs.multiflatmap.getCurrentFlatmap();
        if (currentFlatmap) {
          currentFlatmap.showConnectivitiesByReference(payload);
        }
      }
    });

    EventBus.on('connectivity-hovered', (payload) => {
      this.showConnectivityTooltips(payload);
    });

    EventBus.on('connectivity-source-change', (payload) => {
      this.changeConnectivitySource(payload);
    });

    EventBus.on("markerUpdate", () => {
      if (this.flatmapReady) {
        this.flatmapMarkerUpdate(this.$refs.multiflatmap.getCurrentFlatmap().mapImp);
      }
    });
    EventBus.on("hoverUpdate", () => {
      if (this.flatmapReady) {
        this.cardHoverHighlight();
      }
    });
    EventBus.on("connectivity-query-filter", (payload) => {
      if (this.flatmapReady && this.$refs.multiflatmap) {
        const currentFlatmap = this.$refs.multiflatmap.getCurrentFlatmap();
        if (currentFlatmap && currentFlatmap.mapImp) {
          this.connectivityQueryFilter(currentFlatmap, payload)
        }
      }
    });
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">

.viewer-container {
  width: 100%;
  height: 100%;
}

:deep(.maplibregl-popup) {
  z-index: 11;
}

:deep(.maplibregl-marker) {
  &.standard-marker {
    cursor: pointer !important;
    z-index: 2;
  }
  &.highlight-marker {
    visibility: visible !important;
    cursor: pointer !important;
    z-index: 1;
    div {
      scale: 0.5;
      width: 0;
    }
  }
}

</style>

<style src="../../assets/mapicon-species-style.css"></style>