import { EventEmitter, Input, Output, Directive } from '@angular/core';
import { DateAdapter } from '@ui/common';
import { BehaviorSubject } from 'rxjs';

interface SetPropertyOptions {
  emit: boolean;
}

@Directive()
export abstract class Datepicker {
  @Input()
  get selected() {
    return this.selectedSubject.value;
  }
  set selected(val: Date) {
    this.setSelected(val, { emit: false });
  }
  @Output()
  selectedChange = new EventEmitter<Date>();

  @Output()
  save = new EventEmitter<Date>();

  @Input()
  get displayed() {
    return this.displayedSubject.value;
  }
  set displayed(val: Date) {
    this.setDisplayed(val, { emit: false });
  }
  @Output()
  displayedChange = new EventEmitter<Date>();

  @Input()
  get min() {
    return this.displayedSubject.value;
  }
  set min(val: Date) {
    this.setMin(val);
  }
  @Input()
  get max() {
    return this.displayedSubject.value;
  }
  set max(val: Date) {
    this.setMax(val);
  }

  @Input()
  get disabledDaysOfWeek() {
    return this.disableDayOfWeekSubject.value;
  }
  set disabledDaysOfWeek(value: number[]) {
    // 0 = Sunday, 1 = Monday, ... 6 = Saturday
    this.disableDayOfWeekSubject.next(value);
  }

  protected selectedSubject = new BehaviorSubject<Date>(undefined);
  protected displayedSubject = new BehaviorSubject<Date>(this.dateAdapter.today());
  protected minSubject = new BehaviorSubject<Date>(undefined);
  protected maxSubject = new BehaviorSubject<Date>(undefined);
  protected disableDayOfWeekSubject = new BehaviorSubject<number[]>(undefined);

  get selected$() {
    return this.selectedSubject.asObservable();
  }
  get displayed$() {
    return this.displayedSubject.asObservable();
  }
  get min$() {
    return this.minSubject.asObservable();
  }
  get max$() {
    return this.maxSubject.asObservable();
  }
  get disabledDaysOfWeek$() {
    return this.disableDayOfWeekSubject.asObservable();
  }

  constructor(private dateAdapter: DateAdapter) {}

  nextMonth() {
    this.setDisplayed(this.dateAdapter.addCalendarMonths(this.displayed, 1));
  }

  previousMonth() {
    this.setDisplayed(this.dateAdapter.addCalendarMonths(this.displayed, -1));
  }

  setSelected(date: Date | string, { emit: emitChanged }: SetPropertyOptions = { emit: true }) {
    let _date: Date;
    if (this.dateAdapter.isValid(date)) {
      _date = date;
    } else if (typeof date === 'string') {
      _date = this.dateAdapter.parseDate(date);
    } else {
      this.selectedSubject.next(undefined);
    }

    if (this.dateAdapter.compareDate(this.selected, _date) !== 0) {
      this.selectedSubject.next(_date);
      if (emitChanged) {
        this.selectedChange.emit(_date);
      }
    }
  }

  setDisplayed(date: Date, { emit: emitChanged }: SetPropertyOptions = { emit: true }) {
    if (!this.dateAdapter.isValid(date)) {
      this.displayedSubject.next(undefined);
      return;
    }

    if (this.dateAdapter.compareDate(this.displayed, date) !== 0) {
      this.displayedSubject.next(date);
      if (emitChanged) {
        this.displayedChange.emit(date);
      }
    }
  }

  setMin(date: Date) {
    if (!!!date && !this.dateAdapter.isValid(date)) {
      throw new Error('Date is not Valid.');
    }

    this.minSubject.next(date);
  }

  setMax(date: Date) {
    if (!!!date && !this.dateAdapter.isValid(date)) {
      throw new Error('Date is not Valid.');
    }

    this.maxSubject.next(date);
  }

  onDestroy() {
    this.selectedSubject.complete();
    this.displayedSubject.complete();
    this.selectedChange.complete();
    this.displayedChange.complete();
    this.maxSubject.complete();
    this.minSubject.complete();
  }
}
