namespace PostSharp.Samples.Profiling
{
  public struct MethodCallData
  {
    internal MetricData MetricData;
 
    private long _kernelTimestamp;
    private long _threadTimestamp;
    private long _userTimestamp;
    private long _asyncTimestamp;
    ThreadLocalSampleCollector _threadLocalCollector;
 
    internal void Start(MetricMetadata metadata)
    {
      this._asyncTimestamp = ProfilingServices.GetTimestamp();
      this.Metadata = metadata;
      this.MetricData.ExecutionCount = 1;
 
      this.Resume();
    }
 
    internal bool IsNull => this._asyncTimestamp == 0;
 
    internal MetricMetadata Metadata { get; private set; }
 
 
    internal void Resume()
    {
      this._threadTimestamp = ProfilingServices.GetTimestamp();
 
      Win32.GetThreadTimes(Win32.GetCurrentThread(), out _, out _, out this._kernelTimestamp, out this._userTimestamp);
 
      this._threadLocalCollector = ProfilingServices.Collector.GetThreadLocalCollector();
      this._threadLocalCollector.EnterMethod(this.Metadata);
    }
 
    internal void Stop()
    {
      this.Pause();
 
      this.MetricData.AsyncTime = ProfilingServices.GetTimestamp() - this._asyncTimestamp;
    }
 
    internal void Pause()
    {
      Win32.GetThreadTimes(Win32.GetCurrentThread(), out _, out _, out var kernelTime, out var userTime);
 
 
      var cpuTime = (kernelTime - this._kernelTimestamp) + (userTime - this._userTimestamp);
 
      var threadTime = ProfilingServices.GetTimestamp() - this._threadTimestamp;
 
      this.MetricData.CpuTime += cpuTime;
      this.MetricData.ThreadTime += threadTime;
 
      this._threadLocalCollector.ExitMethod(this.Metadata, new ExcludedTimeData(cpuTime, threadTime));
    }
 
    internal void AddException() => this.MetricData.ExceptionCount++;
  }
}