using System;
using System.Collections;
using UnityEngine;

namespace OCES.Audio
{
    public class BeatClock
    {
        int m_beatsPerBar;           // 从 TimeSig 解析
        int m_barsPerGrid;           // 从 Grid 字段读取
        double m_startDspTime, m_secondsPerBeat;       // 本次计数周期的起点

        Coroutine m_beatCoroutine, m_barCoroutine, m_gridCoroutine;
        readonly Action<uint> m_onBeat, m_onBar, m_onGrid;

        readonly MonoBehaviour m_host;

        uint m_containerId;
        bool m_stopped, m_blendError;           // 检测到 Blend 多 BPM 情况时置为 true

        internal BeatClock(MonoBehaviour host, Action<uint> onBeat, Action<uint> onBar, Action<uint> onGrid)
        {
            this.m_host = host;
            this.m_onBeat = onBeat;
            this.m_onBar = onBar;
            this.m_onGrid = onGrid;
        }

        public void Restart(MusicContainer container, float inheritedBpm, double dspTime)
        {
            //Debug.Log($"[BeatClock] Restart called, container={container.Id}, bpm={container.Bpm}, inherited={inheritedBpm}");
            
            StopAll();
            this.m_blendError = this.m_stopped = false;
            this.m_containerId = container.Id;
            this.m_startDspTime = dspTime;
            

            // BPM：优先用自己的，为 0 则用传入的继承值，最终回退 120
            float bpm = container.Bpm > 0f ? container.Bpm : inheritedBpm;
            if (bpm <= 30f) bpm = 120f; // 没见过速度小于30bpm的音乐，小于30要么是数填错了，要么是有奇怪的情况。要是真有这么慢的音乐再处理吧。
            this.m_secondsPerBeat = 60 / bpm;

            this.m_beatsPerBar = MusicContainerConfig.GetBeatsPerBar(container.TimeSig);  // 沿用现有的解析逻辑
            this.m_barsPerGrid = container.Grid > 0 ? container.Grid : 4;

            this.m_beatCoroutine = this.m_host.StartCoroutine(BeatCoroutine());
            this.m_barCoroutine  = this.m_host.StartCoroutine(BarCoroutine());
            this.m_gridCoroutine = this.m_host.StartCoroutine(GridCoroutine());
        }

        internal void OnBlendError(MusicContainer container)
        {
            this.m_blendError = true;
            Debug.LogWarning($"[Music System] Blend Container {container.Id} 包含多个不同 BPM 的子 Container，节拍回调已停止。");
        }
        internal void StopAll()
        {
            this.m_stopped = true;
            if (this.m_beatCoroutine != null) this.m_host.StopCoroutine(this.m_beatCoroutine);
            if (this.m_barCoroutine  != null) this.m_host.StopCoroutine(this.m_barCoroutine);
            if (this.m_gridCoroutine != null) this.m_host.StopCoroutine(this.m_gridCoroutine);
            this.m_beatCoroutine = this.m_barCoroutine = this.m_gridCoroutine = null;
        }

        IEnumerator BeatCoroutine()
        {
            double elapsed = AudioSettings.dspTime - this.m_startDspTime;
            long index = elapsed > 0.05 ? (long)(elapsed / this.m_secondsPerBeat) : 0;
            while (true)
            {
                double nextTime = this.m_startDspTime + index * this.m_secondsPerBeat;
                yield return new WaitUntil(() => AudioSettings.dspTime >= nextTime - 0.02);

                while (AudioSettings.dspTime < nextTime)
                    yield return null;

                if (this.m_blendError || this.m_stopped){
                    yield break;
                }
                this.m_onBeat?.Invoke(this.m_containerId);
                index++;
            }
        }

        IEnumerator BarCoroutine()
        {
            double elapsed = AudioSettings.dspTime - this.m_startDspTime;
            double secondsPerBar = this.m_secondsPerBeat * this.m_beatsPerBar;
            long index = elapsed > 0.05 ? (long)(elapsed / secondsPerBar) : 0;
            while (true)
            {
                double nextTime = this.m_startDspTime + index * secondsPerBar;
                yield return new WaitUntil(() => AudioSettings.dspTime >= nextTime - 0.02);

                while (AudioSettings.dspTime < nextTime)
                    yield return null;
                if (this.m_blendError || this.m_stopped){
                    yield break;
                }
                this.m_onBar?.Invoke(this.m_containerId);
                index++;
            }
        }

        IEnumerator GridCoroutine()
        {
            double elapsed = AudioSettings.dspTime - this.m_startDspTime;
            double secondsPerGrid = this.m_secondsPerBeat * this.m_beatsPerBar * this.m_barsPerGrid;
            long index = elapsed > 0.05 ? (long)(elapsed / secondsPerGrid) : 0;
            while (true)
            {
                double nextTime = this.m_startDspTime + index * secondsPerGrid;
                yield return new WaitUntil(() => AudioSettings.dspTime >= nextTime - 0.02);

                while (AudioSettings.dspTime < nextTime)
                    yield return null;
                if (this.m_blendError || this.m_stopped){
                    yield break;
                }
                this.m_onGrid?.Invoke(this.m_containerId);
                index++;
            }
        }
    }
}
