// TimeSample from Mirror II.
// simple profiling sample, averaged for display in statistics.
// usable in builds without unitiy profiler overhead etc.
//
// .average may safely be called from main thread while Begin/End is in another.
// i.e. worker threads, transport, etc.
using System.Diagnostics;
using System.Threading;

namespace Mirror
{
    public struct TimeSample
    {
        // UnityEngine.Time isn't thread safe. use stopwatch instead.
        readonly Stopwatch watch;

        // remember when Begin was called
        double beginTime;

        // keep accumulating times over the given interval.
        // (not readonly. we modify its contents.)
        ExponentialMovingAverage ema;

        // average in seconds.
        // code often runs in sub-millisecond time. float is more precise.
        //
        // set with Interlocked for thread safety.
        // can be read from main thread while sampling happens in other thread.
        public double average; // THREAD SAFE

        // average over N begin/end captures
        public TimeSample(int n)
        {
            watch     = new Stopwatch();
            watch.Start();
            ema       = new ExponentialMovingAverage(n);
            beginTime = 0;
            average   = 0;
        }

        // begin is called before the code to be sampled
        public void Begin()
        {
            // remember when Begin was called.
            // keep StopWatch running so we can average over the given interval.
            beginTime = watch.Elapsed.TotalSeconds;
            // Debug.Log($"Begin @ {beginTime:F4}");
        }

        // end is called after the code to be sampled
        public void End()
        {
            // add duration in seconds to accumulated durations
            double elapsed = watch.Elapsed.TotalSeconds - beginTime;
            ema.Add(elapsed);

            // expose new average thread safely
            Interlocked.Exchange(ref average, ema.Value);
        }
    }
}