<script lang="ts">
  import type { SpecialVisualizationState } from "../SpecialVisualization";
  import LocationInput from "../InputElement/Helpers/LocationInput.svelte";
  import { UIEventSource } from "../../Logic/UIEventSource";
  import { Tiles } from "../../Models/TileRange";
  import { Map as MlMap } from "maplibre-gl";
  import { BBox } from "../../Logic/BBox";
  import type { MapProperties } from "../../Models/MapProperties";
  import ShowDataLayer from "../Map/ShowDataLayer";
  import type { FeatureSource, FeatureSourceForLayer } from "../../Logic/FeatureSource/FeatureSource";

  import SnappingFeatureSource from "../../Logic/FeatureSource/Sources/SnappingFeatureSource";
  import FeatureSourceMerger from "../../Logic/FeatureSource/Sources/FeatureSourceMerger";
  import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
  import { Utils } from "../../Utils";

  /**
   * An advanced location input, which has support to:
   * - Show more layers
   * - Snap to layers
   *
   * This one is mostly used to insert new points
   */
  export let state: SpecialVisualizationState;
  /**
   * The start coordinate
   */
  export let coordinate: { lon: number, lat: number };
  export let snapToLayers: string[] | undefined;
  export let targetLayer: LayerConfig;
  export let maxSnapDistance: number = undefined;

  export let snappedTo: UIEventSource<string | undefined>;
  export let value: UIEventSource<{ lon: number, lat: number }>;
  if (value.data === undefined) {
    value.setData(coordinate);
  }

  let preciseLocation: UIEventSource<{ lon: number, lat: number }> = new UIEventSource<{ lon: number; lat: number }>(coordinate);
  const xyz = Tiles.embedded_tile(coordinate.lat, coordinate.lon, 16);
  const map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined);
  let initialMapProperties: Partial<MapProperties> = {
    zoom: new UIEventSource<number>(19),
    maxbounds: new UIEventSource(undefined),
    /*If no snapping needed: the value is simply the map location;
    * If snapping is needed: the value will be set later on by the snapping feature source
    * */
    location: snapToLayers.length === 0 ? value : new UIEventSource<{ lon: number; lat: number }>(coordinate),
    bounds: new UIEventSource<BBox>(undefined),
    allowMoving: new UIEventSource<boolean>(true),
    allowZooming: new UIEventSource<boolean>(true),
    minzoom: new UIEventSource<number>(18)
  };

  initialMapProperties.bounds.addCallbackAndRunD((bounds: BBox) => {
    const max = bounds.pad(3).squarify();
    initialMapProperties.maxbounds.setData(max);
    return true; // unregister
  });

  if (snapToLayers?.length > 0) {

    const snapSources: FeatureSource[] = [];
    for (const layerId of (snapToLayers ?? [])) {
      const layer: FeatureSourceForLayer = state.perLayer.get(layerId);
      snapSources.push(layer);
      if (layer.features === undefined) {
        continue;
      }
      new ShowDataLayer(map, {
        layer: layer.layer.layerDef,
        zoomToFeatures: false,
        features: layer
      });
    }
    const snappedLocation = new SnappingFeatureSource(
      new FeatureSourceMerger(...Utils.NoNull(snapSources)),
      // We snap to the (constantly updating) map location
      initialMapProperties.location,
      {
        maxDistance: maxSnapDistance ?? 15,
        allowUnsnapped: true,
        snappedTo,
        snapLocation: value
      }
    );

    new ShowDataLayer(map, {
      layer: targetLayer,
      features: snappedLocation
    });
  }


</script>

<div class="w-full h-64">
  <LocationInput {map} mapProperties={initialMapProperties}
                 value={preciseLocation}></LocationInput>
</div>
