import { Component, OnInit, ChangeDetectionStrategy, forwardRef, ViewChild, ElementRef, Output, Input, EventEmitter, Renderer2, ChangeDetectorRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, Validator, AbstractControl, ValidationErrors } from '@angular/forms';
import { DatePipe } from '@angular/common';
import { ApplyMaskService } from '../../services/apply-mask.service';
import { ForceFocusMsgDirective } from '../../directives/force-focus-msg/force-focus-msg.directive';
import { NbAdjustment, NbDatepickerComponent, NbDynamicOverlay, NbDynamicOverlayHandler, NbMediaBreakpointsService, NbPosition, NbThemeService, NbTrigger } from '@nebular/theme';
import { MsgBoxService } from '../../services/msg-box.service';
import { first, map, takeUntil } from 'rxjs/operators';
import { StringUtils } from '../../utilities/string-utils';
import { CalendarViewComponent } from './calendar-view/calendar-view.component';
import { Subject } from 'rxjs';
import { DateUtils } from '../../utilities/date-utils';
const DATE_FORMAT = 'MM/dd/yyyy'
@Component({
  selector: 'op-date-input',
  templateUrl: './date-input.component.html',
  styleUrls: ['./date-input.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => DateInputComponent),
      multi: true
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => DateInputComponent),
      multi: true,
    },
    NbDynamicOverlayHandler,
    NbDynamicOverlay
  ]
})
export class DateInputComponent implements OnInit, Validator {

  @ViewChild('datePickerContainer', { static: true }) datePickerContainer: ElementRef;
  @ViewChild('nativeDateInput', { static: true }) nativeDatePickerInput: ElementRef;

  @ViewChild('inputBox', { static: true }) input: ElementRef;
  @ViewChild(ForceFocusMsgDirective, { static: false }) popover: ForceFocusMsgDirective;
  private _value: Date;
  private _initialized: boolean;
  private _legalInput: boolean = false;
  private _lastBlurValue: Date;

  private dynamicOverlay: NbDynamicOverlay;
  private _calendarIsShown: boolean = false;
  private _passwordTag: string = null;

  _readOnly: boolean;

  //this is for password protected field
  passwordSecured: boolean = false;

  onChange = (value: Date) => { };
  onTouched = () => { };
  name: string;
  invalid: boolean = false;
  @Input() id?: string = "";
  @Input() placeholder: string;
  @Input() inputClass: string;
  @Input() size: string = 'medium';
  @Input() min: Date;
  @Input() max: Date;
  @Input() maskExpression: string = 'M0/d0/0000';
  _disabled = false;
  _required = false;
  showingMobileInput = false;

  destroy$: Subject<void> = new Subject<void>();

  @Input()
  public set readonly(value) {
    this._readOnly = typeof value !== 'undefined' && value !== false;
  }
  @Input()
  public set required(value) {
    this._required = typeof value !== 'undefined' && value !== false;
  }

  skipHoliday: boolean
  @Input("skipHoliday")
  public set input_skipHoliday(value) {
    this.skipHoliday = typeof value !== "undefined" && value !== false;
  }

  @Input()
  public set passwordTag(value) {
    this._passwordTag = value;
    this.passwordSecured = true;
  }
  public get passwordTag() {
    return this._passwordTag;
  }

  public get showUnlockBtn(): boolean {
    return !this.readonly && this.passwordSecured;
  }

  @Output() dateChange = new EventEmitter<Date>();
  @Output() focus = new EventEmitter();

  constructor(
    public datepipe: DatePipe,
    private applyMaskService: ApplyMaskService,
    private cdRef: ChangeDetectorRef,
    private msgBoxService: MsgBoxService,
    private renderer: Renderer2,
    private el: ElementRef,
    private dynamicOverlayHandler: NbDynamicOverlayHandler,
    private breakpointService: NbMediaBreakpointsService,
    private themeService: NbThemeService,
  ) {
  }

  ngAfterViewInit() {
    this.renderer.removeAttribute(this.el.nativeElement, 'id');
    setTimeout(() => {
      this.dynamicOverlay = this.configureDynamicOverlay().build();
    }, 400);

  }

  writeValue(value: Date): void {
    if (value) {
      this.value = value;
      this.displayValue = DateUtils.format(this.value, DATE_FORMAT)
    }
    else if (value !== undefined) {
      this.value = null;
      this.displayValue = '';
    }
    this._lastBlurValue = this.value;

    this.renderer.setProperty(this.nativeDatePickerInput.nativeElement, 'valueAsDate', value);
  }

  registerOnChange(fn: (value: Date) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState?(isDisabled: boolean): void {
    this._disabled = isDisabled;
  }

  //#endregion
  ngOnInit() {

    this.dynamicOverlayHandler
      .host(this.input)
      .componentType(CalendarViewComponent);


    const { md } = this.breakpointService.getBreakpointsMap();
    this.themeService.onMediaQueryChange()
      .pipe(
        map(([, currentBreakpoint]) => currentBreakpoint.width < md),
        takeUntil(this.destroy$),
      )
      .subscribe((isLessThanMd: boolean) => {
        this.showingMobileInput = isLessThanMd;
      });


  }

  public get displayValue(): string {
    return this.input.nativeElement.value;
  }
  public set displayValue(v: string) {
    this.input.nativeElement.value = v;
  }

  public get value(): Date {
    return this._value;
  }
  public set value(v: Date) {

    var changedByLimited = false;
    if (v) {
      //Check max and min
      if (v < this.min) {
        v = this.min;
        changedByLimited = true;
      } else if (v > this.max) {
        v = this.max;
        changedByLimited = true;
      }
    }

    if (v != this._value) {
      this._value = v;
      this.onChange(this.value);
    }
    //this.datePicker.hide();
    if (changedByLimited) {
      this.displayValue = DateUtils.format(v, DATE_FORMAT);
    }
  }

  toggleCalendar() {
    this._calendarIsShown = !this._calendarIsShown;
    if (this._calendarIsShown) {
      this.dynamicOverlay = this.configureDynamicOverlay().rebuild();
    }
    this.dynamicOverlay.toggle();
    // if (this.datePicker.isShown) {

    //   this.datePicker.hide();
    // } else {

    //   this.datePicker.show();
    // }
  }

  testWriteValue($e) {

  }
  setDisplayFormat() {

    this.displayValue = this.applyMaskService
      .applyMask(this.displayValue, this.maskExpression)

    //this.validate();
  }

  preventIllegalsInput(e) {
    this._legalInput = false;
    let regMatch = e.key.match(/.{1}/g);
    this._legalInput = (regMatch != undefined && regMatch.length == 1);
  }

  applyMask(e) {
    if (!this._legalInput) {
      this.setDisplayFormat();
    } else {
      let focusLocation = this.input.nativeElement.selectionEnd;
      this.displayValue = this.applyMaskService.applyMask(this.displayValue,
        this.maskExpression,
        {
          inputKey: e.key,
          inputPosition: focusLocation
        });

      this.setCursorPosition(this.applyMaskService.focusIndex);
    }

    this.triggerChange();
  }

  validate(control: AbstractControl): ValidationErrors {

    if (this._required && !this.value && this.displayValue.length == 0) {
      this.invalid = true;
    }
    else {
      this.invalid = !this.applyMaskService.validate(this.displayValue, this.maskExpression);
    }

    if (this.invalid && this.input.nativeElement.parentElement.className.indexOf('ng-touched') > -1) {
      if (this.popover) this.popover.show();
      return { key: 'Date', }
    }

    if (this.popover) this.popover.hide();
    return null;
  }

  triggerChange(wipeOutInvalidDate = false) {
    if (this.displayValue.length == this.maskExpression.length) {
      var newValue = new Date(this.displayValue);
      if (
        this.value == null ||
        false === DateUtils.isSameDate(newValue, this.value)
      ) {
        this.value = newValue;
      }
    }
    else if (this.displayValue.length == 0 || wipeOutInvalidDate) {
      this.value = null;
      this.displayValue = '';
    }

    if (this.value != this._lastBlurValue) {
      this._lastBlurValue = this.value;
      this.onChange(this.value);
      this.dateChange.emit(this.value);
    }

  }

  focusout() {
    if (false == this._calendarIsShown) {

      //For quick input `07/09/21` => `07/09/2021`
      if (this.displayValue.match(/^\d{2}\/\d{2}\/\d{2}$/g)) {
        this.displayValue = this.displayValue.slice(0, 6) + '20' + this.displayValue.slice(6, 8);
      }
      this.triggerChange(true);
    }
  }

  pickDate(date: Date) {
    if (!this._disabled && !this._readOnly) {
      this.displayValue = DateUtils.format(date, DATE_FORMAT);
      this.triggerChange();
    }
    this.hide();
    this.input.nativeElement.focus();
    this.dynamicOverlay = this.configureDynamicOverlay().rebuild();
  }

  setCursorPosition(position) {
    let input = this.input.nativeElement;
    if (input.setSelectionRange) {
      input.focus();
      input.setSelectionRange(position, position);
    }
    else if (input.createTextRange) {
      var range = input.createTextRange();
      range.collapse(true);
      range.moveEnd('character', position);
      range.moveStart('character', position);
      range.select();
    }
  }



  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
    this.dynamicOverlayHandler.destroy();
  }



  private configureDynamicOverlay() {

    //.el[0].scrollTop = (i - MAX_VISIBLE_ITEMS / 2) * VISIBLE_ITEM_HEIGHT;

    var windowHeight = window.innerHeight;
    //include input box height
    //var itemHeight = (visibleItemsCount + 1) * VISIBLE_ITEM_HEIGHT;
    var itemHeight = 405;
    //this.input.nativeElement.offsetHeight
    //var y = (this.input.nativeElement.offsetHeight + itemHeight) > windowHeight ? (windowHeight - itemHeight) : this.input.nativeElement.offsetHeight;
    var y = this.input.nativeElement ? this.input.nativeElement.getBoundingClientRect().y : 0;
    let menuOnBottom = (y + itemHeight) < windowHeight;

    return this.dynamicOverlayHandler
      .position(menuOnBottom ? NbPosition.BOTTOM_START : NbPosition.TOP_START)
      .trigger(NbTrigger.NOOP)
      .offset(menuOnBottom ? 2 : -11)
      .adjustment(NbAdjustment.NOOP)
      .context({
        position: menuOnBottom ? NbPosition.BOTTOM : NbPosition.TOP,
        date: this.value,
        class: 'text-left',
        width: this.input.nativeElement.parentElement.clientWidth,
        hide: () => { this.hide(); },
        focus: () => { this.isFocused = true; },
        dateChange: (date) => { this.pickDate(date); }
      });
  }
  isFocused: boolean = false;
  hide() {
    this._calendarIsShown = false;
    if (this.dynamicOverlay && !this.cdRef['destroyed']) {
      this.dynamicOverlay.hide();
      this.cdRef.detectChanges();
    }
  }

}
