using System;
using System.Runtime.InteropServices;

namespace Unosquare.RaspberryIO.Abstractions.Native {
  /// <summary>
  /// Represents a low-level exception, typically thrown when return codes from a
  /// low-level operation is non-zero or in some cases when it is less than zero.
  /// </summary>
  /// <seealso cref="Exception" />
  public class HardwareException : Exception {
    /// <summary>
    /// Initializes a new instance of the <see cref="HardwareException"/> class.
    /// </summary>
    /// <param name="errorCode">The error code.</param>
    /// <param name="component">The component.</param>
    public HardwareException(Int32 errorCode, String component) : base($"A hardware exception occurred. Error Code: {errorCode}") {
      this.ExtendedMessage = null;

      try {
        this.ExtendedMessage = Standard.Strerror(errorCode);
      } catch {
        // Ignore
      }

      this.ErrorCode = errorCode;
      this.Component = component;
    }

    /// <summary>
    /// Gets the error code.
    /// </summary>
    /// <value>
    /// The error code.
    /// </value>
    public Int32 ErrorCode {
      get;
    }

    /// <summary>
    /// Gets the component.
    /// </summary>
    /// <value>
    /// The component.
    /// </value>
    public String Component {
      get;
    }

    /// <summary>
    /// Gets the extended message (could be null).
    /// </summary>
    /// <value>
    /// The extended message.
    /// </value>
    public String? ExtendedMessage {
      get;
    }

    /// <summary>
    /// Throws a new instance of a hardware error by retrieving the last error number (errno).
    /// </summary>
    /// <param name="className">Name of the class.</param>
    /// <param name="methodName">Name of the method.</param>
    /// <exception cref="HardwareException">When an error thrown by an API call occurs.</exception>
    public static void Throw(String className, String methodName) => throw new HardwareException(Marshal.GetLastWin32Error(), $"{className}.{methodName}");

    /// <inheritdoc />
    public override String ToString() => $"{nameof(HardwareException)}{(String.IsNullOrWhiteSpace(this.Component) ? String.Empty : $" on {this.Component}")}: ({this.ErrorCode}) - {this.Message}";
  }
}