using System;

namespace Unosquare.Swan.Abstractions {
  /// <summary>
  /// Represents a singleton pattern abstract class.
  /// </summary>
  /// <typeparam name="T">The type of class.</typeparam>
  public abstract class SingletonBase<T> : IDisposable
      where T : class {
    /// <summary>
    /// The static, singleton instance reference.
    /// </summary>
    protected static readonly Lazy<T> LazyInstance = new Lazy<T>(
        valueFactory: () => Activator.CreateInstance(typeof(T), true) as T,
        isThreadSafe: true);

    private Boolean _isDisposing; // To detect redundant calls

    /// <summary>
    /// Gets the instance that this singleton represents.
    /// If the instance is null, it is constructed and assigned when this member is accessed.
    /// </summary>
    /// <value>
    /// The instance.
    /// </value>
    public static T Instance => LazyInstance.Value;

    /// <inheritdoc />
    public void Dispose() => this.Dispose(true);

    /// <summary>
    /// Releases unmanaged and - optionally - managed resources.
    /// Call the GC.SuppressFinalize if you override this method and use
    /// a non-default class finalizer (destructor).
    /// </summary>
    /// <param name="disposeManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
    protected virtual void Dispose(Boolean disposeManaged) {
      if(this._isDisposing) {
        return;
      }

      this._isDisposing = true;

      // free managed resources
      if(LazyInstance == null) {
        return;
      }

      try {
        IDisposable disposableInstance = LazyInstance.Value as IDisposable;
        disposableInstance?.Dispose();
      } catch {
        // swallow
      }
    }
  }
}