using System.Globalization;
using UnityEngine;

namespace ServiceSim
{
    /// <summary>
    /// 実物の動画と秒数を比べられる画面（依頼 v13 の 依頼2。2026-10-26）。
    ///
    /// 録画はエイイチが Mac の画面録画で撮るので、Unity Recorder は入れない。
    /// この script がするのは2つだけ:
    ///   1. 画面の左上に「店の時計」と「いまの倍率」を出す
    ///   2. 1倍で回す（1コマ 0.05 秒のまま、実時間に合わせて待つ）
    ///
    /// 店の時計 … 回し始めからのシミュレーションの中の経過（時:分:秒）。ServiceOps.NowS。
    /// 倍率     … 直近 measureWindowSec の「シミュレーションの時間 ÷ 実時間」。
    ///            決め打ちの値ではなく **測った値** を出す（コマが重いと下がる）。
    ///
    /// 測る回に影響を出さないための決まり:
    ///   ・show・realTimeSpeed の初期値は **どちらも切**
    ///   ・画面なしの実行（-batchmode / -nographics）では描かない
    ///   ・1倍で回した回は **見る専用**。ExperimentRunner が記録を書き出さない
    ///     （出力フォルダも作らない）。段階Bなどの測る回は、1倍が入っていると
    ///     「押す前の確かめ」で止まる
    ///   ・描くのは OnGUI だけで、店の回し方・ロボット・人には1つも触らない
    /// </summary>
    public class SimHud : MonoBehaviour
    {
        /// <summary>Setup が作るときの名前</summary>
        public const string RootName = "SimHud";

        [Header("参照（Setup が入れる）")]
        [Tooltip("店の時計を読む先。空なら回し始めに自分で探す")]
        public ServiceOps ops;

        [Header("出し方（録画のときだけ入れる）")]
        [Tooltip("画面の左上に、店の時計と倍率を出す。" +
                 "測る回は切のままにする（初期値は切）")]
        public bool show = false;

        [Tooltip("1倍で回す。1コマ 0.05 秒のまま、実時間に合わせて待つので" +
                 "中身の進み方は変わらない。" +
                 "入れた回は **見る専用** で、記録を書き出さない（初期値は切）")]
        public bool realTimeSpeed = false;

        [Header("細かい決まり")]
        [Tooltip("倍率を測る窓（実時間の秒）。短いとちらつき、長いと反応が鈍い")]
        public float measureWindowSec = 2f;

        [Tooltip("字の大きさ（画面の高さに対する割合）。録画の見やすさで決める")]
        public float fontScale = 0.028f;

        [Tooltip("画面の左上からの余白（px）")]
        public Vector2 margin = new Vector2(16f, 16f);

        // ---- 測るための覚え（描くだけに使う。回し方には入らない）----
        float _winRealS, _winSimS, _lastSimS = -1f, _lastRealS = -1f;
        float _rate;                 // 測った倍率
        bool _hasRate;

        GUIStyle _style;
        Texture2D _back;

        /// <summary>
        /// 画面に描けるか（画面なしの実行では描かない）。
        /// -batchmode でも -nographics なら graphicsDeviceType が Null になる
        /// </summary>
        public static bool CanDraw
        {
            get
            {
                return !Application.isBatchMode &&
                       SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Null;
            }
        }

        /// <summary>
        /// いま1倍で回しているか（ExperimentRunner が「見る専用」の判定に読む）。
        /// 画面なしの実行では、入れてあっても1倍にしない（夜に待たせないため）
        /// </summary>
        public bool RealTimeNow { get { return realTimeSpeed && CanDraw; } }

        /// <summary>測った倍率（まだ測れていなければ -1）</summary>
        public float MeasuredRate { get { return _hasRate ? _rate : -1f; } }

        void OnEnable()
        {
            _frameRealStart = Time.realtimeSinceStartup;
            _winRealS = 0f;
            _winSimS = 0f;
            _lastSimS = -1f;
            _lastRealS = -1f;
            _hasRate = false;
        }

        void Update()
        {
            if (ops == null) ops = FindObjectOfType<ServiceOps>();
            if (ops == null) return;

            // 倍率を測る（店の中の時間の進み ÷ 実時間の進み）。
            //
            // 実時間は **realtimeSinceStartup** で測る。
            // Time.captureDeltaTime を入れて回していると、Unity は deltaTime も
            // unscaledDeltaTime もその値（0.05）に固定するので、
            // そちらを使うと倍率が何倍で回していても 1.0 に見えてしまう
            float sim = ops.NowS;
            float real = Time.realtimeSinceStartup;
            if (_lastSimS >= 0f && _lastRealS >= 0f)
            {
                float dSim = sim - _lastSimS;
                float dReal = real - _lastRealS;
                if (dSim >= 0f && dReal > 0f)
                {
                    _winSimS += dSim;
                    _winRealS += dReal;
                    if (_winRealS >= Mathf.Max(0.2f, measureWindowSec))
                    {
                        _rate = _winSimS / _winRealS;
                        _hasRate = true;
                        _winSimS = 0f;
                        _winRealS = 0f;
                    }
                }
            }
            _lastSimS = sim;
            _lastRealS = real;
        }

        /// <summary>
        /// 1倍にするための待ち（依頼 v13 の 依頼2）。
        ///
        /// Time.captureDeltaTime は触らない（1コマ 0.05 秒のまま）。
        /// 1コマの実時間が 0.05 秒になるまで待つだけなので、
        /// 中身の進み方（どの順で何が起きるか）は1つも変わらない。
        /// LateUpdate なので、その回のコマの計算が終わったあとに待つ。
        /// 画面なしの実行では待たない
        /// </summary>
        void LateUpdate()
        {
            if (!RealTimeNow) return;

            float step = Time.captureDeltaTime;
            if (step <= 0f) return;                 // 1コマ固定で回していないときは何もしない

            float used = Time.realtimeSinceStartup - _frameRealStart;
            float wait = step - used;
            if (wait > 0f && wait < 1f)
            {
                // ミリ秒まででよい（1コマ 50 ミリ秒に対して十分細かい）
                System.Threading.Thread.Sleep((int)(wait * 1000f));
            }
            _frameRealStart = Time.realtimeSinceStartup;
        }

        float _frameRealStart;

        void Start() { _frameRealStart = Time.realtimeSinceStartup; }

        void OnGUI()
        {
            if (!show || !CanDraw || ops == null) return;

            if (_style == null)
            {
                _style = new GUIStyle(GUI.skin.label);
                _style.alignment = TextAnchor.UpperLeft;
                _style.richText = false;
            }
            _style.fontSize = Mathf.Max(11, Mathf.RoundToInt(Screen.height * fontScale));
            _style.normal.textColor = Color.white;

            string text = Clock(ops.NowS) + "   " + RateText();

            Vector2 size = _style.CalcSize(new GUIContent(text));
            Rect box = new Rect(margin.x, margin.y, size.x + 20f, size.y + 12f);

            if (_back == null)
            {
                _back = new Texture2D(1, 1);
                _back.SetPixel(0, 0, new Color(0f, 0f, 0f, 0.55f));
                _back.Apply();
            }
            GUI.DrawTexture(box, _back);
            GUI.Label(new Rect(box.x + 10f, box.y + 6f, size.x, size.y), text, _style);
        }

        /// <summary>店の時計（時:分:秒）</summary>
        public static string Clock(float seconds)
        {
            if (seconds < 0f) seconds = 0f;
            int t = Mathf.FloorToInt(seconds);
            int h = t / 3600;
            int m = (t / 60) % 60;
            int s = t % 60;
            return h.ToString(CultureInfo.InvariantCulture) + ":" +
                   m.ToString("00", CultureInfo.InvariantCulture) + ":" +
                   s.ToString("00", CultureInfo.InvariantCulture);
        }

        string RateText()
        {
            if (RealTimeNow && !_hasRate) return "1.0 倍（実時間）";
            if (!_hasRate) return "— 倍";
            string n = _rate >= 10f ? _rate.ToString("0", CultureInfo.InvariantCulture)
                                    : _rate.ToString("0.0", CultureInfo.InvariantCulture);
            return n + " 倍" + (RealTimeNow ? "（実時間）" : "");
        }

        void OnDisable()
        {
            if (_back != null) { Destroy(_back); _back = null; }
        }
    }
}
