/*
 * Copyright (C) 2024 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 type {AnimationBuilder} from '@ionic/angular/standalone';
import {AnimationController} from '@ionic/angular/standalone';
import type {AnimationOptions} from '@ionic/angular/common/providers/nav-controller';
import {iosDuration, iosEasing, mdDuration, mdEasing} from 'src/app/animation/easings';

/**
 * Get the center of an element
 */
function center(element: HTMLElement): {x: number; y: number} {
  const bounds = element.getBoundingClientRect();
  return {
    x: bounds.left + bounds.width / 2,
    y: bounds.top + bounds.height / 2,
  };
}

/**
 * Get the flip transform for an element
 */
function flipTransform(from: HTMLElement, to: HTMLElement): string {
  const fromCenter = center(from);
  const toCenter = center(to);
  const dx = fromCenter.x - toCenter.x;
  const dy = fromCenter.y - toCenter.y;
  return `translate(${dx}px, ${dy}px)`;
}

/**
 * Get the flip clip path for an element
 */
function flipClipPath(from: HTMLElement, to: HTMLElement): string {
  const fromBounds = from.getBoundingClientRect();
  const toBounds = to.getBoundingClientRect();
  const y = Math.max(0, (toBounds.height - fromBounds.height) / 2);
  const x = Math.max(0, (toBounds.width - fromBounds.width) / 2);
  return `inset(${y}px ${x}px)`;
}

/**
 * Animation of the map maximize
 */
export function mapMaximizeAnimation(animationController: AnimationController): AnimationBuilder {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return (_baseElement: HTMLElement, options: AnimationOptions | any) => {
    const rootTransition = animationController
      .create()
      .duration(options.duration ?? (options.mode === 'ios' ? iosDuration * 1.5 : mdDuration * 2.5))
      .easing(options.mode === 'ios' ? iosEasing : mdEasing);

    const enteringMap = options.enteringEl.querySelector('mgl-map') as HTMLElement;
    const leavingMap = options.leavingEl.querySelector('mgl-map') as HTMLElement;

    if (!enteringMap.classList.contains('ready')) {
      rootTransition.delay(2000);
      enteringMap.addEventListener(
        'ready',
        event => {
          event.preventDefault();
          setTimeout(() => {
            if (rootTransition.isRunning()) {
              rootTransition.stop();
              rootTransition.delay(0);
              rootTransition.play();
            }
          });
        },
        {once: true},
      );
    }

    const mapEnterTransition = animationController
      .create()
      .fromTo('transform', flipTransform(leavingMap, enteringMap), 'translate(0, 0)')
      .fromTo('clipPath', flipClipPath(leavingMap, enteringMap), 'inset(0px 0px)')
      .addElement(enteringMap);
    const mapExitTransition = animationController
      .create()
      .fromTo('transform', 'translate(0, 0)', flipTransform(enteringMap, leavingMap))
      .fromTo('clipPath', 'inset(0px 0px)', flipClipPath(enteringMap, leavingMap))
      .addElement(leavingMap);

    const enterTransition = animationController
      .create()
      .fromTo('opacity', options.direction === 'back' ? '1' : '0', '1')

      .addElement(options.enteringEl);
    const exitTransition = animationController
      .create()
      .fromTo('opacity', '1', options.direction === 'back' ? '0' : '1')
      .addElement(options.leavingEl);

    rootTransition.addAnimation([enterTransition, exitTransition, mapEnterTransition, mapExitTransition]);
    return rootTransition;
  };
}
