/*
 * Copyright (C) 2021 StApps
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */
import {Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {Platform} from '@ionic/angular';
import {SCISO8601Date, SCUuid} from '@openstapps/core';
import {last} from 'lodash-es';
import moment, {Moment} from 'moment';
import {DateFormatPipe} from 'ngx-moment';
import {Subscription} from 'rxjs';
import {
  materialFade,
  materialManualFade,
  materialSharedAxisX,
} from '../../../animation/material-motion';
import {ScheduleProvider} from '../../calendar/schedule.provider';
import {ScheduleEvent, ScheduleResponsiveBreakpoint} from './schema/schema';
import {SwiperComponent} from 'swiper/angular';

/**
 * Component that displays the schedule
 */
@Component({
  selector: 'stapps-calendar-view',
  templateUrl: 'calendar-view.html',
  styleUrls: ['calendar-view.scss'],
  animations: [materialFade, materialSharedAxisX, materialManualFade],
})
export class CalendarViewComponent implements OnDestroy, OnInit {
  /**
   * UUID subscription
   */
  private _uuidSubscription: Subscription;

  /**
   * The day that the schedule started out on
   */
  baselineDate: Moment;

  /**
   * Oldest event to newest event
   */
  dateRange: [Date, Date];

  /**
   * The date range to initially display
   */
  displayDates: Moment[][] = [];

  /**
   * Hours for grid
   */
  readonly hours: number[];

  /**
   * Height of the slides based on the displayed hours
   */
  readonly hoursAmount: number;

  /**
   * Range of hours to display
   */
  @Input() readonly hoursRange = {
    from: 5,
    to: 22,
  };

  todaySlideIndex: number;

  initialSlideIndex?: Promise<number>;

  /**
   * Layout of the schedule
   */
  @Input() layout: ScheduleResponsiveBreakpoint;

  @ViewChild('mainSwiper', {static: false}) swiperRef: SwiperComponent;

  /**
   * Get the date format for the date field
   */
  // tslint:disable-next-line:prefer-function-over-method
  localDateFormat = moment.localeData().longDateFormat('L');

  /**
   * Route fragment
   */
  routeFragment = 'schedule/calendar';

  /**
   * Vertical scale of the schedule (distance between hour lines)
   */
  scale = 60;

  /**
   * unix -> (uid -> event)
   */
  testSchedule: Record<SCISO8601Date, Record<SCUuid, ScheduleEvent>> = {};

  /**
   * UUIDs
   */
  uuids: SCUuid[];

  /**
   * For use in templates
   */
  moment = moment;

  constructor(
    protected readonly scheduleProvider: ScheduleProvider,
    protected readonly activatedRoute: ActivatedRoute,
    protected readonly datePipe: DateFormatPipe,
    protected readonly platform: Platform,
  ) {
    // This could be done directly on the properties too instead of
    // here in the constructor, but because of TSLint member ordering,
    // some properties wouldn't be initialized, and if you disable
    // member ordering, auto-fixing the file can still cause reordering
    // of properties.
    this.hoursAmount = this.hoursRange.to - this.hoursRange.from + 1;
    this.hours = [...Array.from({length: this.hoursAmount}).keys()];
  }

  /**
   * Load events
   */
  async loadEvents(): Promise<number> {
    const dateSeries = await this.scheduleProvider.getDateSeries(this.uuids);

    this.testSchedule = {};

    for (const series of dateSeries.dates) {
      for (const date of series.dates) {
        const index = moment(date)
          .startOf('day')
          .diff(this.baselineDate, 'days');

        // fall back to default
        (this.testSchedule[index] ?? (this.testSchedule[index] = {}))[
          series.uid
        ] = {
          dateSeries: series,
          time: {
            start: moment(date).hours(),
            duration: series.duration,
          },
        };
      }
    }

    return this.todaySlideIndex;
  }

  /**
   * OnDestroy
   */
  ngOnDestroy(): void {
    this._uuidSubscription.unsubscribe();
  }

  /**
   * Initialize
   */
  ngOnInit() {
    let dayString: string | number | null =
      this.activatedRoute.snapshot.paramMap.get('date');
    if (dayString == undefined || dayString === 'now') {
      const urlFragment: string = last(window.location.href.split('/')) ?? '';

      dayString = /^\d{4}-\d{2}-\d{2}$/.test(urlFragment)
        ? urlFragment
        : moment.now();
    }

    this.baselineDate = moment(dayString).startOf('day');

    this.initialSlideIndex = new Promise(resolve => {
      this._uuidSubscription = this.scheduleProvider.uuids$.subscribe(
        async result => {
          this.uuids = result;
          resolve(await this.loadEvents());
        },
      );
    });
  }

  /**
   * Change page
   */
  onPageChange(index: number) {
    window.history.replaceState(
      {},
      '',
      `${this.routeFragment}/${this.baselineDate
        .clone()
        .add(index, 'days')
        .format('YYYY-MM-DD')}`,
    );
  }
}
