/*
 * Copyright (C) 2018 Robert Bosch GmbH Copyright (C) 2018 Robert Bosch Manufacturing Solutions GmbH, Germany. All rights reserved.
 */

import { AfterViewInit, Directive, Input, OnDestroy, OnInit, Optional } from '@angular/core';
import TileLayer from 'ol/layer/Tile';
import { Vector } from 'ol/source';

import { Observable, Subject } from 'rxjs';
import { first, takeUntil, withLatestFrom } from 'rxjs/operators';
import { OpenLayersLayerFactory } from '../../generic-maps/open-layers-map/open-layers-layer-factory';
import { OpenLayersMapComponent } from '../../generic-maps/open-layers-map/open-layers-map.component';
import { LsLocation } from '../model/lsLocation';
import { LocationMapService } from './location-map.service';
import { LocationRendererDirective } from './location-renderer.directive';
import { LocationStyleService } from './location-style.service';

@Directive({
  selector: '[bciLocationMap]',
})
/**
 * Use renderer to render the locations from the location map service onto a map layer.
 */
export class LocationMapDirective implements OnInit, AfterViewInit, OnDestroy {
  @Input() locations: Observable<LsLocation[]>;
  @Input() customBaseLayer: TileLayer;

  protected locationVectorSource: Vector;
  protected ngUnsubscribe = new Subject<void>();

  constructor(
    protected mapComponent: OpenLayersMapComponent,
    protected locationMapService: LocationMapService,
    protected locationStyleService: LocationStyleService,
    private openLayersLayerFactory: OpenLayersLayerFactory,
    @Optional() protected renderer: LocationRendererDirective
  ) {
    mapComponent.loaded.pipe(first(), takeUntil(this.ngUnsubscribe)).subscribe(() => this.afterMapLoaded());
  }

  // eslint-disable-next-line no-empty-function
  afterMapLoaded(): void {}

  ngOnInit(): void {
    if (!this.locations) {
      this.locations = this.locationMapService.locations;
    }
  }

  ngAfterViewInit(): void {
    this.initMapLayers();
    this.initFeatureRendering();
  }

  ngOnDestroy(): void {
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }

  /**
   * Initialize Map with it's layers etc.
   */
  protected initMapLayers() {
    // Base tile layer
    if (this.customBaseLayer) {
      this.mapComponent.addLayer(this.customBaseLayer);
    } else {
      this.mapComponent.addOSM(1, 0.25);
    }

    // Location Layer
    this.locationVectorSource = this.openLayersLayerFactory.createVectorSource();
    this.mapComponent.addLayer(
      this.openLayersLayerFactory.createVectorLayer(this.locationVectorSource, 10, <any>{
        style: this.locationStyleService.locationStyleDefault,
        declutter: false,
      })
    );
  }

  private initFeatureRendering() {
    // react to changed locations
    this.locations
      .pipe(withLatestFrom(this.locationMapService.locationsSelected), takeUntil(this.ngUnsubscribe))
      .subscribe(([locations, selectedLocations]) => {
        const selectedLocationIds = new Set(selectedLocations.map((l) => l.id));
        const newSelectedLocations = locations.filter((l) => selectedLocationIds.has(l.id));
        if (!!this.renderer) {
          this.renderer.render(this.locationVectorSource, locations);
          this.renderer.updateStyles(newSelectedLocations);
        }
        this.locationMapService.selectedLocations = newSelectedLocations;
      });

    // react to changed selection
    this.locationMapService.locationsSelected.pipe(takeUntil(this.ngUnsubscribe)).subscribe((selectedLocations) => {
      if (!!this.renderer) {
        this.renderer.updateStyles(selectedLocations);
      }
    });
  }
}
