import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef, Output, EventEmitter, ViewRef } from '@angular/core';

@Component({
  selector: 'lib-date-picker',
  templateUrl: './date-picker.component.html',
  styleUrls: ['./date-picker.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DatePickerComponent implements OnInit {
  @Input() selected: Date;
  @Input() disabled = false;
  expanded = false;
  today = new Date();
  baseDate: Date;
  currentDay: number;
  selectedMonth: number;
  selectedYear: number;
  baseDay: number;
  baseMonth: number;
  baseYear: number;
  month: number;
  months = {
    0: 'Januar',
    1: 'Februar',
    2: 'März',
    3: 'April',
    4: 'Mai',
    5: 'Juni',
    6: 'Juli',
    7: 'August',
    8: 'September',
    9: 'Oktober',
    10: 'November',
    11: 'Dezember',
  };
  year: number;
  monthMatrix = [];
  weeks = {
    0: 'M',
    1: 'D',
    2: 'M',
    3: 'D',
    4: 'F',
    5: 'S',
    6: 'S',
  };
  @Output() changed = new EventEmitter<Date>();
  constructor(private cdr: ChangeDetectorRef) {}

  ngOnInit() {
    this.initialize();
  }

  initialize() {
    this.baseDate = this.selected ? this.selected : this.today;
    this.currentDay = this.baseDate.getDate();
    this.month = this.baseDate.getMonth();
    this.selectedMonth = this.month;
    this.year = this.baseDate.getFullYear();
    this.selectedYear = this.year;
    this.baseDay = this.currentDay;
    this.baseMonth = this.selectedMonth;
    this.baseYear = this.selectedYear;
    this.monthMatrix = this.fillMonthMatrix(this.month, this.year);
  }

  selectDay(day: number) {
    this.currentDay = day;
    this.selectedMonth = this.month;
    this.selectedYear = this.year;
  }

  previousMonth() {
    if (this.month === 0) {
      this.month = 11;
      this.year -= 1;
    } else {
      this.month -= 1;
    }
    this.monthMatrix = this.fillMonthMatrix(this.month, this.year);
  }

  nextMonth() {
    if (this.month === 11) {
      this.month = 0;
      this.year += 1;
    } else {
      this.month += 1;
    }
    this.monthMatrix = this.fillMonthMatrix(this.month, this.year);
  }

  confirm() {
    const baseDay = this.baseDate.getDate();
    const baseMonth = this.baseDate.getMonth();
    const baseYear = this.baseDate.getFullYear();
    if (this.currentDay === baseDay && this.selectedMonth === baseMonth && this.selectedYear === baseYear) {
      this.toggle();
      return;
    }
    const newDate = new Date(this.selectedYear, this.selectedMonth, this.currentDay);
    this.changed.emit(newDate);
    this.toggle();
  }

  fillMonthMatrix(month: number, year: number) {
    const weeks = [[0, 1, 2, 3, 4, 5, 6]],
      firstDate = new Date(year, month, 1),
      lastDate = new Date(year, month + 1, 0),
      numDays = lastDate.getDate();

    let start = 1;
    let end = firstDate.getDay() === 0 ? firstDate.getDay() + 1 : 7 - firstDate.getDay() + 1;
    while (start <= numDays) {
      const isFirstWeek = end <= 7;
      const week = Array(7)
        .fill(0)
        .map((_, i) => {
          const day = i + 1 + start - 1;
          const firstWeekZeroCondition = isFirstWeek && i < 7 - day;
          if (firstWeekZeroCondition) {
            return 0;
          }
          const zeroShiftingFactor = isFirstWeek ? 6 - (end - start) : 0;
          const result = day - zeroShiftingFactor;
          return result < 1 ? 0 : result > numDays ? 0 : result;
        });
      weeks.push(week);
      start = end + 1;
      end = end + 7;
      if (end > numDays) {
        end = numDays;
      }
    }
    return weeks;
  }

  toggle() {
    if (this.disabled) {
      return;
    }
    this.expanded = !this.expanded;
    if (this.expanded) {
      this.initialize();
    }
  }

  detectChanges() {
    setTimeout(() => {
      if (this.cdr !== null && this.cdr !== undefined && !(this.cdr as ViewRef).destroyed) {
        this.cdr.detectChanges();
      }
    }, 0);
  }
}
