/*
 * 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 {
  AfterViewInit,
  Component,
  HostListener,
  OnInit,
  ViewChild,
} from '@angular/core';
import {Location} from '@angular/common';
import {ActivatedRoute, Router} from '@angular/router';
import {AnimationController, ModalController} from '@ionic/angular';
import {SharedAxisChoreographer} from '../../../animation/animation-choreographer';
import {materialSharedAxisX} from '../../../animation/material-motion';
import {ModalEventCreatorComponent} from './modal/modal-event-creator.component';
import {ScheduleResponsiveBreakpoint} from './schema/schema';
import {animate, style, transition, trigger} from '@angular/animations';

/**
 * This needs to be sorted by break point low -> high
 *
 * Last entry must have `until: Infinity`
 */
const responsiveConfig: ScheduleResponsiveBreakpoint[] = [
  {
    until: 768,
    days: 1,
    startOf: 'day',
  },
  {
    until: 1700,
    days: 3,
    startOf: 'day',
  },
  {
    until: Number.POSITIVE_INFINITY,
    days: 7,
    startOf: 'week',
  },
];

const fabAnimations = trigger('fabAnimation', [
  transition(':leave', [
    style({opacity: 1, transform: 'translate(0vw, 0vh) scale(100%)'}),
    animate(
      '100ms ease-in',
      style({opacity: 0, transform: 'translate(-5vw, -5vh) scale(200%)'}),
    ),
  ]),
  transition(':enter', [
    style({opacity: 0, transform: 'translate(-5vw, -5vh) scale(200%)'}),
    animate(
      '200ms ease-out',
      style({opacity: 1, transform: 'translate(0vw, 0vh) scale(100%)'}),
    ),
  ]),
]);

/**
 * Component that displays the schedule
 */
@Component({
  selector: 'stapps-schedule-page',
  templateUrl: 'schedule-page.html',
  styleUrls: ['schedule-page.scss'],
  animations: [materialSharedAxisX, fabAnimations],
})
export class SchedulePageComponent implements OnInit, AfterViewInit {
  /**
   * Current width of the window
   */
  private currentWindowWidth: number = window.innerWidth;

  /**
   * Actual Segment Tab
   */
  actualSegmentValue?: string | null;

  fabVisible = true;

  /**
   * Layout
   */
  layout: ScheduleResponsiveBreakpoint = SchedulePageComponent.getDaysToDisplay(
    this.currentWindowWidth,
  );

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

  @ViewChild('segment') segmentView!: HTMLIonSegmentElement;

  /**
   * Choreographer for the tab switching
   */
  tabChoreographer: SharedAxisChoreographer<string | null | undefined>;

  /**
   * Amount of days that should be shown according to current display width
   */
  static getDaysToDisplay(width: number): ScheduleResponsiveBreakpoint {
    // the search could be optimized, but probably would have little
    // actual effect with five entries.
    // we can be sure we get an hit when the last value.until is infinity
    // (unless someone has a display that reaches across the universe)
    return (
      responsiveConfig.find(value => width < value.until) ??
      responsiveConfig[responsiveConfig.length - 1]
    );
  }

  constructor(
    private readonly modalController: ModalController,
    private readonly activatedRoute: ActivatedRoute,
    private readonly animationController: AnimationController,
    private router: Router,
    private location: Location,
  ) {}

  ngOnInit() {
    this.tabChoreographer = new SharedAxisChoreographer(
      this.activatedRoute.snapshot.paramMap.get('mode'),
      ['calendar', 'recurring', 'single'],
    );
  }

  ngAfterViewInit() {
    this.segmentView.value = this.tabChoreographer.currentValue;
  }

  /**
   * Resize callback
   *
   * Note: this may not fire when the browser transfers from full screen to windowed
   * (Firefox & Chrome tested)
   */
  @HostListener('window:resize', ['$event'])
  onResize(_: UIEvent) {
    const current = SchedulePageComponent.getDaysToDisplay(
      this.currentWindowWidth,
    );
    const next = SchedulePageComponent.getDaysToDisplay(window.innerWidth);
    this.currentWindowWidth = window.innerWidth;

    if (current.days === next.days) {
      this.layout = next;
    }
  }

  /**
   * When the segment changes
   */
  onSegmentChange() {
    const url = this.router
      .createUrlTree([[], this.segmentView.value])
      .toString();
    this.location.go(url);
    this.tabChoreographer.changeViewForState(this.segmentView.value);
  }

  /**
   * Add event modal sheet
   */
  async showCreateEventModal() {
    this.fabVisible = false;

    // eslint-disable-next-line @typescript-eslint/no-explicit-any,unicorn/consistent-function-scoping
    const enterAnimation = (baseElement: any) => {
      const backdropAnimation = this.animationController
        .create()
        .addElement(baseElement.querySelector('.modal-wrapper'))
        .fromTo('opacity', '0', 'var(--backdrop-opacity)');

      const wrapperAnimation = this.animationController
        .create()
        .addElement(baseElement.querySelector('.modal-wrapper'))
        .keyframes([
          {
            opacity: '0',
            transform: 'translate(30vw, 30vh) scale(0.5)',
          },
          {
            opacity: '1',
            transform: 'translate(0vw, 0vh) scale(1)',
          },
        ]);

      return this.animationController
        .create()
        .addElement(baseElement)
        .easing('ease-out')
        .duration(150)
        .addAnimation([backdropAnimation, wrapperAnimation]);
    };

    // eslint-disable-next-line @typescript-eslint/no-explicit-any,unicorn/consistent-function-scoping
    const leaveAnimation = (baseElement: any) => {
      return enterAnimation(baseElement).direction('reverse');
    };

    const modal = await this.modalController.create({
      component: ModalEventCreatorComponent,
      swipeToClose: true,
      cssClass: 'add-modal',
      componentProps: {
        dismissAction: () => {
          modal.dismiss();
        },
      },
      enterAnimation,
      leaveAnimation,
    });

    await modal.present();
    await modal.onWillDismiss();

    this.fabVisible = true;
  }
}
