using System;
using System.Threading;
 
namespace PostSharp.Samples.Profiling
{
  internal class MetricAccessor
  {
    public static MetricAccessor Empty = new MetricAccessor(null);
    private volatile int _version;
    private MetricData _data;
 
    public MetricAccessor(MetricMetadata metadata)
    {
      this.Metadata = metadata;
    }
 
    public MetricMetadata Metadata { get; }
 
 
    public int Version
    {
      get
      {
        return this._version;
      }
      set
      {
        this._version = value;
      }
    }
 
    private bool IsWriting => this._version % 2 == 1;
 
    public void AddData(in MetricData data)
    {
#if DEBUG
      if (this.IsWriting)
      {
        throw new InvalidOperationException();
      }
#endif
      var localVersion = this._version;
      this._version = localVersion + 1;
      Thread.MemoryBarrier();
 
      this._data.AddData(data);
 
      Thread.MemoryBarrier();
      this._version = localVersion + 2;
 
    }
 
    public void AddExclusion(in ExcludedTimeData data)
    {
#if DEBUG
      if (this.IsWriting)
      {
        throw new InvalidOperationException();
      }
#endif
      var localVersion = this._version;
      this._version = localVersion + 1;
      Thread.MemoryBarrier();
 
      this._data.AddExclusion(data);
 
      Thread.MemoryBarrier();
      this._version = localVersion + 2;
 
    }
 
    public void GetData(out MetricData data)
    {
      var spinWait = new SpinWait();
 
      while (true)
      {
        var versionBefore = this._version;
 
        while (this.IsWriting)
        {
          spinWait.SpinOnce();
        }
 
 
        data = this._data;
 
        if (this._version == versionBefore)
        {
          return;
        }
 
        spinWait.SpinOnce();
      }
 
    }
 
  }
}