import { MathUtils } from "three";

/**
 * ◀︎ ■ ▶︎ のメニューの初期化処理
 * Initialization process for the ◀︎ ■ ▶︎ menu
 * @returns
 */
export function setupMenu({ world, hook }) {
  /**
   * エフェクトの取得. Get the effect.
   */
  const menuBox = world.getObByEl(".menu-box"),
    menuTriangles = world.getObByElAll(".menu-triangle"),
    hoverScale = 1.3,
    lerpLevel = 0.1;

  /**
   * 三角柱が初期表示時は上向き（▲）のため、三角形の向きを回転して表示
   * Since the triangular prism is facing up (▲) on initial display, rotate the orientation of the triangles for display
   */
  if (menuTriangles.length > 0) {
    menuTriangles[0].mesh.rotation.z = Math.PI / 2;
    menuTriangles[1].mesh.rotation.z = -Math.PI / 2;
  }

  /**
   * ホバーした際にスケールを変更する処理をメッシュに追加
   * Add a process to change the scale when hovered over to the mesh
   */
  const loadBox = changeScaleMesh(hook, {
    ob: menuBox,
    lerpLevel,
    hoverScale,
  });

  /**
   * ホバーした際にスケールを変更する処理をメッシュに追加
   * Add a process to change the scale when hovered over to the mesh
   */
  const [loadSphere1, loadSphere2] = menuTriangles.map((menuSphere) => {
    return changeScaleMesh(hook, {
      ob: menuSphere,
      lerpLevel,
      hoverScale,
    });
  });

  /**
   * 画面初期表示時
   * For initial screen display
   */
  return [loadBox, loadSphere1, loadSphere2];
}

/**
 * ホバーした際にスケールを変更する処理をメッシュに追加します。
 * Add a process to change the scale when hovered over to the mesh.
 * @param {*} hook
 * @param {*} param1
 * @returns
 */
function changeScaleMesh(hook, { ob, lerpLevel, hoverScale }) {
  if (!ob) return;
  let currentScale = 0,
    targetScale = ob.scale.width,
    ckickTimeoutId;

  // メニューにホバーした時のスケール
  // Scale when hovering over the menu
  function _menuEnter({ mesh }) {
    if (mesh !== ob.mesh) return;
    clearTimeout(ckickTimeoutId);
    targetScale = ob.scale.width * hoverScale;
    currentScale = ob.scale.width;

    // rAF（requestAnimationFrame）で targetScale に向けて連続的にスケールを変更する処理を開始
    // Start the process of continuously changing the scale towards targetScale with rAF (requestAnimationFrame)
    hook.on(hook.RENDER, _changeScale);
  }

  // メニューからホバーが外れた時のスケール
  // Scale when hovering away from the menu
  function _menuLeave({ mesh }) {
    if (mesh !== ob.mesh) return;
    clearTimeout(ckickTimeoutId);
    targetScale = ob.scale.width;

    // rAFで targetScale（元のスケール） に向けて連続的にスケールを変更
    // Continuously change the scale towards targetScale (original scale) with rAF
    hook.on(hook.RENDER, _changeScale);
  }

  // ページトランジションする際にスケールを 0 にする
  // Set the scale to 0 when transitioning the page
  function _pageTransition() {
    targetScale = 0;

    // rAFでtargetScaleに向けて連続的にスケールを変更
    // Continuously change the scale towards targetScale with rAF
    hook.on(hook.RENDER, _changeScale);
  }

  /**
   * フックを削除します。
   * Remove the hook.
   */
  function _destroyHooks() {
    // メッシュにマウスが入った際の処理を解除
    // Remove the process when the mouse enters the mesh
    hook.off(hook.MOUSE_MESH_ENTER, _menuEnter);
    hook.off(hook.MOUSE_MESH_LEAVE, _menuLeave);
  }

  // ホバー制御を追加
  // Add hover control
  hook.on(hook.MOUSE_MESH_ENTER, _menuEnter);
  hook.on(hook.MOUSE_MESH_LEAVE, _menuLeave);
  /**
   * ページ遷移処理を追加
   * Add page transition process
   */
  hook.on(hook.T_BEGIN, _pageTransition, { once: true, priority: 10 });
  /**
   * 画面遷移時にホバー時のスケール変更のフックを削除します。
   * Remove the hooks for scale change on hover during screen transitions.
   */
  hook.on(hook.T_BEGIN, _destroyHooks, { once: true, priority: 20 });

  // スケールを変更するための関数
  // Function to change the scale
  function _changeScale() {
    // targetScaleに十分近づいたら処理を終了
    // End the process once close enough to targetScale
    if (Math.abs(ob.mesh.scale.x - targetScale) < 1e-3) {
      // フックを解除する
      // Remove the hook
      hook.off(hook.RENDER, _changeScale);
    }

    // スケールを変更
    // Change the scale
    currentScale = MathUtils.lerp(currentScale, targetScale, lerpLevel);
    // x,y,zに一律に値をセット
    // Set the same value to x, y, z uniformly
    ob.mesh.scale.setScalar(currentScale);
  }

  // 初期状態は scale を 0 とする
  // The initial state sets the scale to 0
  ob.mesh.scale.setScalar(currentScale);

  // 実行することでメッシュが表示される
  // Executing will display the mesh
  return function loadAnimation() {
    hook.on(hook.RENDER, _changeScale);
  };
}
