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

import { Inject, Injectable } from '@angular/core';
import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { FeatureEntityMap } from '../../generic-maps/open-layers-map/feature-entity-map';
import { LocationService } from '../location.service';
import { LsLocation, LsBoundary, LsPosition } from '../model';

/**
 * Service for selection and modification handling of locations.
 */
@Injectable()
export class LocationMapService {
  featureToEntityMap = new FeatureEntityMap<LsLocation>();
  private _leaveDrawModeRequest = new Subject<void>();
  leaveDrawModeRequest = this._leaveDrawModeRequest.asObservable();

  private _selectedLocations = new BehaviorSubject<LsLocation[]>([]);

  /**
   * Observable emitting set of selected locations whenever the selection changes.
   */
  locationsSelected: Observable<LsLocation[]> = this._selectedLocations
    .asObservable()
    .pipe(distinctUntilChanged(this.equalSet));

  /**
   * Set of currently selected locations.
   */
  get selectedLocations(): LsLocation[] {
    return this._selectedLocations.getValue();
  }

  /**
   * Change set of selected locations.
   */
  set selectedLocations(locations: LsLocation[]) {
    this._selectedLocations.next(locations);
  }

  private _locationBeingEdited = new BehaviorSubject<LsLocation>(null);

  locationBeingEditedObservable: Observable<LsLocation> = this._locationBeingEdited.asObservable();
  locations = combineLatest([this.locationBeingEditedObservable, this.locationService.items]).pipe(
    map((value) => {
      const [locationBeingEdited, items] = value;
      if (!!locationBeingEdited) {
        const filteredLocations = items.filter((item) => item.id !== locationBeingEdited.id);
        filteredLocations.push(locationBeingEdited);
        return filteredLocations;
      } else {
        return items;
      }
    })
  );

  locationLayers = this.locationService.layers;

  get locationBeingEdited(): LsLocation {
    return this._locationBeingEdited.getValue();
  }

  set locationBeingEdited(location: LsLocation) {
    this._locationBeingEdited.next(location);
    if (!!location) {
      this.selectedLocations = [location];
    }
  }

  constructor(@Inject(LocationService) private locationService: LocationService) {}

  hasPosition(location: Partial<LsLocation>) {
    const position: LsPosition = location.geoCoordinate;
    return (
      !!position &&
      position.longitude !== undefined &&
      position.longitude !== null &&
      position.latitude !== undefined &&
      position.latitude !== null
    );
  }

  hasBoundary(location: Partial<LsLocation>) {
    const boundary: LsBoundary = location.boundary;
    return !!boundary && !!boundary.coordinates && boundary.coordinates.length > 0;
  }

  leaveDrawMode() {
    this._leaveDrawModeRequest.next();
  }

  private equalSet(previous: LsLocation[], current: LsLocation[]): boolean {
    if (previous.length !== current.length) {
      return false;
    }
    const previousSet = new Set(previous);
    return current.every((location) => previousSet.has(location));
  }
}
