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

import { Directive, Inject, OnDestroy, OnInit, Optional } from '@angular/core';
import { Subject } from 'rxjs';
import { distinctUntilChanged, first, map, takeUntil } from 'rxjs/operators';
import { InteractionsManager, OpenLayersMapComponent } from '../generic-maps/index';
import { DrawInteractionMode } from '../generic-maps/open-layers-map/interactions/modes/draw-interaction-mode';
import { ModificationInteractionMode } from '../generic-maps/open-layers-map/interactions/modes/modification-interaction-mode';
import { SelectionInteractionMode } from '../generic-maps/open-layers-map/interactions/modes/selection-interaction-mode';
import {
  BOUNDARY_ADD_INTERACTION,
  BOUNDARY_REMOVE_ACTION,
  MODIFY_INTERACTION,
  MOVE_INTERACTION,
  POSITION_ADD_INTERACTION,
  POSITION_REMOVE_ACTION,
  SELECT_INTERACTION,
  Supplier,
} from './interaction.tokens';
import { LocationMapService } from './map/location-map.service';

export enum LocationMapInteractions {
  SELECT = 'select',
  MODIFY = 'modify',
  MOVE = 'move',
  POSITION_ADD = 'add-position',
  POSITION_REMOVE = 'remove-position',
  BOUNDARY_ADD = 'add-boundary',
  BOUNDARY_REMOVE = 'remove-boundary',
}

@Directive({
  selector: '[bciLocationMap]',
  providers: [],
})
export class LocationInteractionDirective implements OnInit, OnDestroy {
  interactionManager: InteractionsManager;
  private ngUnsubscribe = new Subject<void>();

  private _interactionMode: string;

  get interactionMode() {
    return this._interactionMode;
  }

  set interactionMode(value: string) {
    switch (value) {
      case LocationMapInteractions.SELECT: {
        if (!this.selectionInteractionModeSupplier) {
          console.warn('no selection interaction defined');
          return;
        }
        this.interactionManager.switchMode(this.selectionInteractionModeSupplier());
        break;
      }
      case LocationMapInteractions.MODIFY: {
        if (!this.editInteractionModeSupplier) {
          console.warn('no edit interaction defined');
          return;
        }
        this.interactionManager.switchMode(this.editInteractionModeSupplier());
        break;
      }
      case LocationMapInteractions.MOVE: {
        if (!this.moveInteractionModeSupplier) {
          console.warn('no move interaction defined');
          return;
        }
        this.interactionManager.switchMode(this.moveInteractionModeSupplier());
        break;
      }
      case LocationMapInteractions.POSITION_ADD: {
        if (!this.addPositionInteractionModeSupplier) {
          console.warn('no add position interaction defined');
          return;
        }
        this.interactionManager.switchMode(this.addPositionInteractionModeSupplier());
        break;
      }
      case LocationMapInteractions.POSITION_REMOVE: {
        if (!this.removePositionAction) {
          console.warn('no remove position action defined');
          return;
        }
        this.removePositionAction();
        setTimeout(() => (this.interactionMode = LocationMapInteractions.MODIFY));
        break;
      }
      case LocationMapInteractions.BOUNDARY_ADD: {
        if (!this.addBoundaryInteractionModeSupplier) {
          console.warn('no add boundary interaction defined');
          return;
        }
        this.interactionManager.switchMode(this.addBoundaryInteractionModeSupplier());
        break;
      }
      case LocationMapInteractions.BOUNDARY_REMOVE: {
        if (!this.removeBoundaryAction) {
          console.warn('no remove boundary action defined');
          return;
        }
        this.removeBoundaryAction();
        setTimeout(() => (this.interactionMode = LocationMapInteractions.MODIFY));
        break;
      }
      case null: {
        this.interactionManager.switchMode();
        break;
      }
      default: {
        alert('Undefined interactionManager mode.');
      }
    }
    this._interactionMode = value;
  }

  constructor(
    protected mapComponent: OpenLayersMapComponent,
    protected locationMapService: LocationMapService,
    @Optional()
    @Inject(SELECT_INTERACTION)
    private selectionInteractionModeSupplier: Supplier<SelectionInteractionMode>,
    @Optional() @Inject(MODIFY_INTERACTION) private editInteractionModeSupplier: Supplier<ModificationInteractionMode>,
    @Optional() @Inject(MOVE_INTERACTION) private moveInteractionModeSupplier: Supplier<ModificationInteractionMode>,
    @Optional()
    @Inject(BOUNDARY_ADD_INTERACTION)
    private addBoundaryInteractionModeSupplier: Supplier<DrawInteractionMode>,
    @Optional()
    @Inject(POSITION_ADD_INTERACTION)
    private addPositionInteractionModeSupplier: Supplier<DrawInteractionMode>,
    @Optional() @Inject(BOUNDARY_REMOVE_ACTION) private removeBoundaryAction: Supplier<boolean>,
    @Optional() @Inject(POSITION_REMOVE_ACTION) private removePositionAction: Supplier<boolean>
  ) {
    mapComponent.loaded.pipe(first(), takeUntil(this.ngUnsubscribe)).subscribe(() => this.afterMapLoaded());
  }

  ngOnInit(): void {
    this.locationMapService.leaveDrawModeRequest.pipe(takeUntil(this.ngUnsubscribe)).subscribe(() => {
      // delay to avoid stray selections
      setTimeout(() => (this.interactionMode = LocationMapInteractions.MODIFY), 500);
    });
  }

  ngOnDestroy(): void {
    if (!!this.interactionManager) {
      this.interactionManager.destroy();
    }
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }

  afterMapLoaded(): void {
    this.interactionManager = new InteractionsManager(this.mapComponent.map);
    this.initInteractions();
  }

  private initInteractions() {
    this.interactionMode = LocationMapInteractions.SELECT;

    // Switch between modification- and selection-mode when editing a location
    this.locationMapService.locationBeingEditedObservable
      .pipe(
        takeUntil(this.ngUnsubscribe),
        map((locationBeingEdited) => !!locationBeingEdited),
        distinctUntilChanged()
      )
      .subscribe((editing) => {
        if (editing && this.interactionMode === LocationMapInteractions.SELECT) {
          this.interactionMode = LocationMapInteractions.MODIFY;
        } else if (!editing && this.interactionMode !== LocationMapInteractions.SELECT) {
          this.interactionMode = LocationMapInteractions.SELECT;
        }
      });
  }
}
