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

import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { cloneDeep } from 'lodash-es';
import { Subject } from 'rxjs';
import { debounceTime, map, takeUntil } from 'rxjs/operators';
import { LsLocation } from '../model/lsLocation';
import { GenericLocationFormState, GenericLocationFormUIService } from './generic-location-form-u-i.service';
import { LocationFormValidator } from './location-form.validator';

/**
 * Generic form for default location entity.
 */
@Component({
  selector: 'bci-generic-location-form',
  templateUrl: './generic-location-form.component.html',
  styleUrls: ['./generic-location-form.component.scss'],
})
export class GenericLocationFormComponent implements OnDestroy {
  @Output() save = new EventEmitter<LsLocation>();
  @Output() cancel = new EventEmitter<void>();
  @Output() update = new EventEmitter<LsLocation>();

  form: FormGroup = new FormGroup({});
  formControlLongitude: FormControl;
  formControlLatitude: FormControl;
  formGroupBoundary: FormGroup;
  formState: GenericLocationFormState = this.uiStateService.state;

  supplementKeys = [];
  private readonly coordinateRegEx = '[-]?[0-9]{0,3}(|[\\.\\,]{1,1}[0-9]+)';
  private readonly altitudeRegEx = '[-]?[0-9]+(|[\\.\\,]{1,1}[0-9]+)';
  private ngUnsubscribe = new Subject<void>();
  private ngUnsubscribeChanges = new Subject<void>();

  private _location: LsLocation;

  get location() {
    return this._location;
  }

  @Input() set location(location: LsLocation) {
    this.ngUnsubscribeChanges.next();
    this._location = location;
    this.buildForm();
    this.form.reset();
    this.form.patchValue(!location ? {} : location);
    if (!!location) {
      this.form.valueChanges
        .pipe(takeUntil(this.ngUnsubscribeChanges), takeUntil(this.ngUnsubscribe))
        .pipe(
          debounceTime(250),
          map(() => this.getDirtyLocation())
        )
        .subscribe((dirtyLocation) => this.update.emit(dirtyLocation));
    }
  }

  get dirty() {
    return this.form.dirty;
  }

  constructor(private formBuilder: FormBuilder, private uiStateService: GenericLocationFormUIService) {
    this.buildForm();
  }

  setGeometries(locationGeometries: Partial<LsLocation>) {
    if (!locationGeometries || !this._location) {
      return;
    }
    const newGeometries: Partial<LsLocation> = {};
    if (locationGeometries.boundary === null || locationGeometries.boundary === undefined) {
      newGeometries.boundary = {
        type: undefined,
        coordinates: undefined,
      };
    } else {
      newGeometries.boundary = locationGeometries.boundary;
    }

    if (locationGeometries.geoCoordinate === null || locationGeometries.geoCoordinate === undefined) {
      newGeometries.geoCoordinate = {
        longitude: undefined,
        latitude: undefined,
      };
    } else {
      newGeometries.geoCoordinate = {
        longitude: locationGeometries.geoCoordinate.longitude,
        latitude: locationGeometries.geoCoordinate.latitude,
      };
    }
    this.form.patchValue(newGeometries, { emitEvent: false });
    this.form.markAsDirty();
  }

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

  triggerSave() {
    if (this.form.valid) {
      this.save.next(this.getDirtyLocation());
    }
  }

  reset() {
    this.form.reset();
  }

  triggerCancel() {
    this.cancel.next();
  }

  private getDirtyLocation(): LsLocation {
    const entity = Object.assign(cloneDeep(this.location), this.form.value) as LsLocation;
    if (!!entity.boundary) {
      if (entity.boundary.coordinates) {
        entity.boundary.type = 'Polygon';
      } else {
        delete entity.boundary;
      }
    }
    return entity;
  }

  private buildForm() {
    const supplementsGroup = this.formBuilder.group({});
    this.supplementKeys.length = 0;
    if (!!this.location && !!this.location.supplements) {
      for (const key of Object.keys(this.location.supplements)) {
        this.supplementKeys.push(key);
        supplementsGroup.addControl(key, this.formBuilder.control(['']));
      }
    }

    this.formControlLongitude = this.formBuilder.control(null, [
      Validators.pattern(this.coordinateRegEx),
      Validators.min(-180),
      Validators.max(180),
    ]);
    this.formControlLatitude = this.formBuilder.control(null, [
      Validators.pattern(this.coordinateRegEx),
      Validators.min(-90),
      Validators.max(90),
    ]);
    const geoCoordinateGroup = this.formBuilder.group({
      altitude: [null, Validators.pattern(this.altitudeRegEx)],
      longitude: this.formControlLongitude,
      latitude: this.formControlLatitude,
    });

    this.formGroupBoundary = this.formBuilder.group({
      type: [''],
      coordinates: [''],
    });

    this.form = this.formBuilder.group({
      name: ['', [Validators.required]],
      type: ['', [Validators.required]],

      address: this.formBuilder.group({
        street: [''],
        houseNumber: [''],
        postalCode: [''],
        city: [''],
        countryCode: [''],
      }),

      geoCoordinate: geoCoordinateGroup,
      boundary: this.formGroupBoundary,

      supplements: supplementsGroup,
    });
    this.form.setValidators(LocationFormValidator.checkGeoPosition);
  }
}
