import * as keycode from '@angular/cdk/keycodes';
import { AfterViewInit, Component, EventEmitter, Inject, Input, LOCALE_ID, OnDestroy, OnInit, Optional, Output, ViewChild } from '@angular/core';
import { FormControl, FormGroup, NgForm } from '@angular/forms';
import { DxDateBoxComponent } from 'devextreme-angular';
import { DisabledDate, dxCalendarOptions } from 'devextreme/ui/calendar';
import { Subscription } from 'rxjs';
import { EN_DateFormats } from '../../consts';
import { HenselTranslateService } from '../../services/localization/hensel-translate.service';
import { getDateFormat } from '../../utils';

export const enum EN_PickerTpe {
  date = 1,
  datetime = 2
}

@Component({
  // tslint:disable-next-line: component-selector
  selector: 'hensel-date-time-picker',
  templateUrl: './hensel-date-time-picker.component.html',
  styleUrls: ['./hensel-date-time-picker.component.scss'],
})

export class HenselDateTimePickerComponent implements OnDestroy, AfterViewInit, OnInit {
  private defDdateOutOfRangeMessage: string = 'core.hdatepicker.dateoutofrange';
  @ViewChild(DxDateBoxComponent) dateBox: DxDateBoxComponent;
  public itemsControl = new FormControl();

  private _placeholder: string = '';
  public get placeholder(): string {
    return this._placeholder + (!!this._placeholder && this.required && ! this.disabled ? ' *' : '');
  }

  @Input()
  public set placeholder(value: string) {
    this._placeholder = value;
  }

  public get date(): Date {
    return this.itemsControl.value;
  }

  @Input()
  public set date(value: Date) {
    if (!!value && !(value instanceof Date)) value = new Date(value);
    if (this.pickerType === EN_PickerTpe.date) value?.setHours(0, 0, 0, 0);
    this.itemsControl.setValue(value);
    this.change(value);
  }

  @Input() name: string;
  private _displayFormat: string = 'datetime';
  public get displayFormat(): string {
    return this._displayFormat;
  }
  @Input()
  public set displayFormat(value: string) {
    this._displayFormat = value;
    this.date = this.date; //to consider date format
    this.initCalendarOptions();
  }


  @Output() public dateChange = new EventEmitter();
  @Input() required: boolean;
  @Input() minDate: Date;
  @Input() maxDate: Date;
  private _canClear: boolean = true;
  public get canClear(): boolean {
    return this._canClear;
  }
  @Input()
  public set canClear(value: boolean) {
    this._canClear = value;
    this.btnClear.visible = value;
  }
  @Input() stepMinute: number = 15;


  private _disabledDates?: Array<Date> | ((data: DisabledDate) => boolean);
  public get disabledDates(): Array<Date> | ((data: DisabledDate) => boolean) {
    return this._disabledDates;
  }
  @Input() public set disabledDates(value: Array<Date> | ((data: DisabledDate) => boolean)) {
    this._disabledDates = value;
    this.initCalendarOptions();
  }

  private _showWeekNumbers: boolean = true;
  public get showWeekNumbers(): boolean {
    return this._showWeekNumbers;
  }
  @Input() public set showWeekNumbers(value: boolean) {
    this._showWeekNumbers = value;
    this.initCalendarOptions();
  }

  private _showTodayButton: boolean = true;
  public get showTodayButton(): boolean {
    return this._showTodayButton;
  }
  @Input() public set showTodayButton(value: boolean) {
    this._showTodayButton = value;
    this.initCalendarOptions();
  }

  @Input() customValidationMessage: string;

  private _dateOutOfRangeMessage: string = this.defDdateOutOfRangeMessage;
  public get dateOutOfRangeMessage(): string {
    return this._dateOutOfRangeMessage;
  }

  @Input()
  public set dateOutOfRangeMessage(value: string) {
    this._dateOutOfRangeMessage = !value ? undefined /*this.defDdateOutOfRangeMessage*/ : value;
  }

  public get getDisplayFormat(): string {
    return this.displayFormat === 'date' ? 'date' : 'datetime';
  }

  public get formControlName(): string {
    return 'henselDateTimePickerControl_' + this.name;
  }

  public get pickerType(): EN_PickerTpe {
    if (this.getDisplayFormat === 'date') {
      return EN_PickerTpe.date;
    } else {
      return EN_PickerTpe.datetime;
    }
  }

  public get timeIsShown(): boolean {
    return this.pickerType === EN_PickerTpe.datetime;
  }

  public get parentFormGroup(): FormGroup {
    const group = {};
    group[this.formControlName] = this.itemsControl;
    return this.parent ? this.parent.form : new FormGroup(
      group
    );
  }

  private _parent: NgForm;
  public get parent(): NgForm {
    return this._parent;
  }
  @Input()
  public set parent(value: NgForm) {
    this._parent = value;
    // if (this._parent && this.required !== undefined && this._selectedItemIds.length ==== 0) this.parent.control.setErrors({ required: true });
    if (!this.parent) this.parent = this._parentForm;
    this.itemsControl.markAllAsTouched();
    if (this._parent) {
      this.parent.control.addControl(this.formControlName, this.itemsControl);
      this.hsRequiredsubscription = this.parent.valueChanges.subscribe(() => { if (!this.isValid) this.parent.control.setErrors({ required: true }); });


/*      it is working without special code
        this.hsRequiredsubscription = this.parent.valueChanges.subscribe(() => {
        if (this.required && this.empty()) this.parent.control.setErrors({ required: true });
        if (!this.empty()) {
          if (this.minDate && this.date < this.minDate) this.parent.control.setErrors({ matDatepickerMin: true });
          if (this.maxDate && this.date > this.maxDate) this.parent.control.setErrors({ matDatepickerMax: true });
        }
      });*/
    }
  }

  private _disabled: boolean = false;
  public get disabled(): boolean {
    return this._disabled;
  }

  @Input()
  public set disabled(value: boolean) {
    if (value) this.itemsControl.disable();
    else this.itemsControl.enable();

    this._disabled = value;
  }

  private _autofocus: any = false;
  public get autofocus(): any {
    return this._autofocus;
  }
  @Input()
  public set autofocus(value: any) {
    this._autofocus = value;
  }


  public hasFocus: boolean = false;

  public btnClear: any = {
    icon: 'close',
    stylingMode: 'text',
    tabIndex: -1,
    onClick: () => { this.date = null; }
  };

  public calendarOptions: dxCalendarOptions = {};

  private hsRequiredsubscription: Subscription;

  constructor(
    public translate: HenselTranslateService,
    @Optional() private _parentForm: NgForm,
    @Inject(LOCALE_ID) private localeId: string
  ) { }

  ngOnInit(): void {
    this.initCalendarOptions();
    setTimeout(() => {
      if (!this.parent) this.parent = this._parentForm;
    }, 0);
  }

  initCalendarOptions() {
    this.calendarOptions = {
      showWeekNumbers: this.showWeekNumbers,
      showTodayButton: this.pickerType === EN_PickerTpe.datetime ? false : this.showTodayButton,
      disabledDates: this.disabledDates
    };
  }


  isEmpty = () => {
    return !(this.date);
  }


  public get pickerDisplayFormat(): string {
    return getDateFormat(this.localeId, this.pickerType === EN_PickerTpe.datetime ? EN_DateFormats.DateTime : EN_DateFormats.Date);
  }


  public get isValid() {
    return this.isNotEmpty && this.isValidDate();
  }

  change(e) {
    if (this.parent?.control) {
      this.parent.control.updateValueAndValidity();
      // setTimeout(() => {this.parent.control.updateValueAndValidity();}, 5000);
    }
  }

  public inputChanged(e) {
    this.change(e);
    this.date = this.date; //to return date w/o time if type of picker Date
    this.dateChange.emit(this.date);
  }


  public get isNotEmpty(): boolean {
    // wenn disabled: keine Validation anzeigen (also valid)
    // wenn enabled: wenn required: date != null and != undefined
    // wenn enabled: wenn nicht required: valid
    return this.disabled || (!this.required || !this.isEmpty()) ;
  }


  public isValidDate() {
    return this.disabled || !this.customNotValid() && (this.isEmpty() || ((!this.minDate || this.minDate <= this.date) && (!this.maxDate || this.maxDate >= this.date)));
  }

  focused(el: any) {
    const res = document?.activeElement?.parentElement?.parentElement?.parentElement?.parentElement === el.element.nativeElement;
    return res;
  }


  public customNotValid(): boolean {
    return !!this.customValidationMessage;
  }

  ngAfterViewInit(): void {
    const elInput = this.dateBox.instance.field(); // input element
    (<any>elInput).autofocus = this.autofocus || (this.autofocus === ''); //wenn set attribute autofocus it is == ''
  }
  ngOnDestroy(): void {
    if (this.hsRequiredsubscription) this.hsRequiredsubscription.unsubscribe();
  }

  doKeyDown(e, tocheck: boolean): void {
    if (e.event.keyCode === keycode.ESCAPE && tocheck) e.event.stopPropagation();
  }

}
