// Elfentier Screen Transitions (free download from elfentier.com)
// 使い方:
//   1. Canvas(Screen Space - Overlay) に全画面の RawImage を置く
//   2. Elfentier/ScreenTransition シェーダーを使ったマテリアルを割り当てる
//   3. この ScreenTransition コンポーネントを同じ GameObject に追加する
//   4. ScreenTransition.Play(TransitionMode.CircleWipe, 0.6f, onComplete) を呼ぶ
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;

namespace Elfentier.Transitions
{
    public enum TransitionMode
    {
        Fade = 0,
        CircleWipe = 1,
        HorizontalWipe = 2,
        DiagonalBlinds = 3,
    }

    [RequireComponent(typeof(Graphic))]
    public sealed class ScreenTransition : MonoBehaviour
    {
        static readonly int ProgressId = Shader.PropertyToID("_Progress");
        static readonly int ModeId = Shader.PropertyToID("_Mode");

        [SerializeField] TransitionMode mode = TransitionMode.Fade;
        [SerializeField, Range(0.05f, 5f)] float duration = 0.6f;

        Graphic graphic;
        Material material;
        Coroutine running;

        void Awake()
        {
            graphic = GetComponent<Graphic>();
            // 共有マテリアルを汚さないよう複製する
            material = new Material(graphic.material);
            graphic.material = material;
            material.SetFloat(ProgressId, 0f);
            graphic.raycastTarget = false;
        }

        /// <summary>画面を覆う(シーン遷移の直前に呼ぶ)</summary>
        public void Play(Action onCovered = null) => Play(mode, duration, onCovered);

        public void Play(TransitionMode transitionMode, float seconds, Action onCovered = null)
        {
            if (running != null) StopCoroutine(running);
            material.SetFloat(ModeId, (float)transitionMode);
            running = StartCoroutine(Animate(0f, 1f, seconds, onCovered));
        }

        /// <summary>覆いを開ける(シーン読み込み後に呼ぶ)</summary>
        public void Reveal(Action onRevealed = null) => Reveal(mode, duration, onRevealed);

        public void Reveal(TransitionMode transitionMode, float seconds, Action onRevealed = null)
        {
            if (running != null) StopCoroutine(running);
            material.SetFloat(ModeId, (float)transitionMode);
            running = StartCoroutine(Animate(1f, 0f, seconds, onRevealed));
        }

        IEnumerator Animate(float from, float to, float seconds, Action onComplete)
        {
            graphic.raycastTarget = true;
            float elapsed = 0f;
            while (elapsed < seconds)
            {
                elapsed += Time.unscaledDeltaTime;
                float t = Mathf.Clamp01(elapsed / seconds);
                // smoothstep で緩急をつける
                float eased = t * t * (3f - 2f * t);
                material.SetFloat(ProgressId, Mathf.Lerp(from, to, eased));
                yield return null;
            }
            material.SetFloat(ProgressId, to);
            graphic.raycastTarget = to > 0.5f;
            running = null;
            onComplete?.Invoke();
        }
    }
}
