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

/**
 *
 */
export function fabExpand(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 : mdDuration * 1.4))
      .easing(options.mode === 'ios' ? iosEasing : mdEasing);
    const back = options.direction === 'back';
    const fabView = back ? options.enteringEl! : options.leavingEl!;
    const otherView = back ? options.leavingEl! : options.enteringEl!;

    const fab = fabView.querySelector('ion-fab-button').shadowRoot.querySelector('.button-native');
    const fabBounds = fab.getBoundingClientRect();
    const viewBounds = otherView.getBoundingClientRect();

    const useReducedMotion = viewBounds.width > 500;
    const reducedMotionTransform = `${Math.min(viewBounds.width * 0.3, 200)}px`;
    const reducedMotionViewBorderRadius = '128px';
    const reducedMotionFabGrow = '2';
    const reducedMotionViewShrink = '0.9';

    const viewCenterX = (viewBounds.width - viewBounds.x) / 2;
    const viewCenterY = (viewBounds.height - viewBounds.y) / 2;

    const viewOnFab = useReducedMotion
      ? `translate(${reducedMotionTransform}, ${reducedMotionTransform}) scale(${reducedMotionViewShrink})`
      : `translate(${(fabBounds.x - viewBounds.x) / 2}px, ${(fabBounds.y - viewBounds.y) / 2}px) scale(${
          fabBounds.width / viewBounds.width
        }, ${fabBounds.height / viewBounds.height})`;
    const fabOnView = useReducedMotion
      ? `translate(-${reducedMotionTransform}, -${reducedMotionTransform}) scale(${reducedMotionFabGrow})`
      : `translate(${viewCenterX - fabBounds.x}px, ${viewCenterY - fabBounds.y}px) scale(${
          viewBounds.width / fabBounds.width
        }, ${viewBounds.height / fabBounds.height})`;
    const transformNormal = `translate(0px, 0px) scale(1, 1)`;

    const viewBorderRadius = useReducedMotion ? reducedMotionViewBorderRadius : '50%';

    const fabViewFade = animationController
      .create()
      .beforeStyles({zIndex: -1})
      .fromTo('opacity', '1', '1')
      .addElement(fabView);
    const fabGrow = animationController
      .create()
      .beforeStyles({transformOrigin: 'center'})
      .fromTo('transform', back ? fabOnView : transformNormal, back ? transformNormal : fabOnView)
      .fromTo('opacity', back ? '0' : '1', back ? '1' : '0')
      .fromTo('borderRadius', back ? '0' : '50%', back ? '50%' : '0')
      .addElement(fab);
    const viewGrow = animationController
      .create()
      .beforeStyles({zIndex: 200, overflow: 'hidden', transformOrigin: 'center'})
      .fromTo('transform', back ? transformNormal : viewOnFab, back ? viewOnFab : transformNormal)
      .fromTo('opacity', back ? '1' : '0', back ? '0' : '1')
      .fromTo('borderRadius', back ? '0' : viewBorderRadius, back ? viewBorderRadius : '0')
      .addElement(otherView);

    return rootTransition.addAnimation(fabGrow).addAnimation(viewGrow).addAnimation(fabViewFade);
  };
}
