 #if canImport(UIKit)
 import UIKit
 
+/// A simple **H**eads **U**p **D**isplay implementation.
+///
+/// HUDs are a common component of app design. Typically used to indicated
+/// to a user that action is in progress, or something has been completed.
 public class HUD: UIView {
-    
+
+    public static var animationDuration: TimeInterval = 0.15
+
+    /// Available presentation methods
     public enum Presentation {
+        /// Presents a `UIActivityIndicatorView` as the central component along
+        /// with an optional short text.
         case activity(text: String?)
+        /// Presents an image along with optional text.
         case image(image: UIImage, text: String?)
-        
+        /// Presents a generic view along with optional text.
+        case view(view: UIView, text: String?)
+
         public var text: String? {
             switch self {
             case .activity(let text):
                 return text
             case .image(_, let text):
                 return text
-            }
-        }
-    }
-    
-    /// The tint/highlight color of the text and visual components
-    public static var tintColor: UIColor = .white {
-        didSet {
-            hud.tintColor = tintColor
-        }
-    }
-    
-    /// The color of the hud area
-    public static var color: UIColor = UIColor.gray.withAlphaComponent(0.5) {
-        didSet {
-            hud.layer.backgroundColor = color.cgColor
-        }
-    }
-    
-    /// The color of the hud area
-    public static var borderColor: UIColor = UIColor.gray.withAlphaComponent(0.8) {
-        didSet {
-            hud.layer.borderColor = borderColor.cgColor
-        }
-    }
-    
+            case .view(_, let text):
+                return text
+            }
+        }
+    }
+
     /// The color used to fill the rest of the window
+    ///
+    /// This is a proxy to the `container.backgroundColor` and `hud.backgroundColor`.
     public static var backgroundColor: UIColor = UIColor.black.withAlphaComponent(0.3) {
         didSet {
             container.backgroundColor = backgroundColor
             hud.backgroundColor = backgroundColor
         }
     }
-    
+
+    /// The color of the hud area
+    ///
+    /// This is a proxy to the `hud.layer.backgroundColor`.
+    public static var color: UIColor = .gray {
+        didSet {
+            hud.layer.backgroundColor = color.cgColor
+        }
+    }
+
+    /// The tint/highlight color of the text and visual components
+    ///
+    /// This is a proxy to the `hud.tintColor` and `hud.label.textColor`
+    public static var tintColor: UIColor = .white {
+        didSet {
+            hud.tintColor = tintColor
+            hud.label.textColor = tintColor
+        }
+    }
+
+    /// The color of the hud area
+    ///
+    /// This is a proxy to the `hud.layer.borderColor`.
+    public static var borderColor: UIColor = UIColor.black.withAlphaComponent(0.8) {
+        didSet {
+            hud.layer.borderColor = borderColor.cgColor
+        }
+    }
+
+    /// The width of the layer border around the hud.
+    ///
+    /// This is a proxy to `hud.layer.borderWidth`
+    public static var borderWidth: CGFloat = 1.5 {
+        didSet {
+            hud.layer.borderWidth = borderWidth
+        }
+    }
+
+    /// The corner radius of the hud view (and centering container in shared context).
+    ///
+    /// This is a proxy to the `container.cornerRadius` and `hud.layer.cornerRadius`
+    public static var cornerRadius: CGFloat = 8.0 {
+        didSet {
+            hud.layer.cornerRadius = cornerRadius
+            container.cornerRadius = cornerRadius
+        }
+    }
+
+    /// The corners masked to use the 'cornerRadius'.
+    ///
+    /// This is a proxy to the `container.cornerRadius` and `hud.layer.cornerRadius`
+    @available(iOS 11.0, *)
+    public static var maskedCorners: CACornerMask = [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner] {
+        didSet {
+            hud.layer.maskedCorners = maskedCorners
+            container.maskedCorners = maskedCorners
+        }
+    }
+
+    /// The current presentation/visibility status of the hud.
     private static var isPresented: Bool = false
+    /// Determines if the presentation was canceled before being presented.
     private static var isPresentationCanceled: Bool = false
-    
-    private static var container: CenteringContainerView = {
-        let view = CenteringContainerView(frame: .zero)
+
+    private static var container: CenteringView = {
+        let view = CenteringView(frame: .zero)
         view.translatesAutoresizingMaskIntoConstraints = false
         view.backgroundColor = HUD.backgroundColor
         view.contentView = hud
-        return view
-    }()
+        view.cornerRadius = HUD.cornerRadius
+        if #available(iOS 11.0, *) {
+            view.maskedCorners = HUD.maskedCorners
+        }
+        return view
+    }()
+
+    /// A singular shared hud instance.
     private static var hud = HUD(frame: .zero)
     private static var constraints: [NSLayoutConstraint] = []
-    
+
+    /// Shows the HUD with the provided configuration options.
+    ///
+    /// Typically you want the hud to be presented at a `UIWindow` level, so passing
+    /// `view.window ?? view` (in the context of a `UIViewController` subclass) or
+    /// `window ?? self` (in the contexted of a `UIView` subclass).
+    ///
+    /// No animations are currently enabled, the hud is simpled added to the view.
+    ///
+    /// The `delayPresentation` parameter helps to mitigate the undesired effect of
+    /// 'flashing' huds onto the screen. It is typically to use a hud during asyncronous
+    /// methods. Using a slight delay allows for quick methods to still call show/hide
+    /// but not actually display the hud if the action is *quick enough*.
+    ///
+    /// - parameter presentation: The *style* of HUD to present
+    /// - parameter view: The view instance in which to append the hud container
+    /// - parameter animated: Determines if any animation is used in the presentation
+    /// - parameter delayPresentation: The time interval in which to wait to display the hud.
+    /// - parameter autoHideAfter: The time interval in which to automatically dismiss the hud.
     public static func show(presentation: Presentation, in view: UIView, animated: Bool = true, delayPresentation: TimeInterval = 0.25, autoHideAfter: TimeInterval? = -1.0) {
         isPresentationCanceled = false
-        
+
         DispatchQueue.main.asyncAfter(deadline: .now() + max(delayPresentation, 0.0)) {
             guard isPresentationCanceled == false else {
                 isPresented = false
                 return
             }
-            
+
             isPresented = true
-            
-            if constraints.count > 0 {
+
+            if !constraints.isEmpty {
                 NSLayoutConstraint.deactivate(constraints)
                 container.removeFromSuperview()
             }
-            
+
             hud.presentation = presentation
             view.addSubview(container)
-            
+
             constraints = [
                 NSLayoutConstraint(item: container, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0.0),
                 NSLayoutConstraint(item: container, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: 0.0),
                 NSLayoutConstraint(item: container, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0.0),
                 NSLayoutConstraint(item: container, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1.0, constant: 0.0),
             ]
-            
+
             NSLayoutConstraint.activate(constraints)
-            
+
+            if animated {
+                container.alpha = 0
+                animator({ container.alpha = 1 }).startAnimation()
+            }
+
             guard let interval = autoHideAfter, interval > 0.0 else {
                 return
             }
-            
+
             DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + interval, execute: {
                 self.hide(animated: animated)
             })
         }
     }
-    
+
     public static func hide(animated: Bool = true) {
         DispatchQueue.main.async {
             guard isPresented else {
                 isPresentationCanceled = true
                 return
             }
-            
-            isPresented = false
-            
-            NSLayoutConstraint.deactivate(constraints)
-            container.removeFromSuperview()
-            constraints.removeAll()
-        }
-    }
-    
-    private var presentation: Presentation = .activity(text: nil) {
+
+            let remove: (UIViewAnimatingPosition) -> Void = { _ in
+                self.isPresented = false
+
+                NSLayoutConstraint.deactivate(self.constraints)
+                self.container.removeFromSuperview()
+                self.constraints.removeAll()
+            }
+
+            if animated {
+                let animator = self.animator { container.alpha = 0 }
+                animator.addCompletion(remove)
+                animator.startAnimation()
+            } else {
+                remove(.end)
+            }
+        }
+    }
+
+    public var presentation: Presentation = .activity(text: nil) {
         didSet {
             switch presentation {
             case .activity(let text):
+                supplementalView = nil
                 activity.isHidden = false
                 imageView.isHidden = true
                 label.isHidden = (text == nil)
                 label.text = text
             case .image(let image, let text):
+                supplementalView = nil
                 activity.isHidden = true
                 imageView.isHidden = false
                 imageView.image = image
                 label.isHidden = (text == nil)
                 label.text = text
-            }
-            
+            case .view(let view, let text):
+                supplementalView = view
+                activity.isHidden = true
+                imageView.isHidden = true
+                label.isHidden = (text == nil)
+                label.text = text
+            }
+
             updateSubviews()
         }
     }
-    
-    private override init(frame: CGRect) {
+
+    public override init(frame: CGRect) {
         super.init(frame: frame)
         initializeSubviews()
     }
-    
+
     required init?(coder aDecoder: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }
-    
-    private lazy var imageView: UIImageView = {
+
+    private static func animator(_ animations: @escaping () -> Void) -> UIViewPropertyAnimator {
+        return UIViewPropertyAnimator(duration: animationDuration, curve: .easeInOut, animations: animations)
+    }
+
+    public private(set) lazy var imageView: UIImageView = {
         let view = UIImageView(frame: .zero)
         view.translatesAutoresizingMaskIntoConstraints = false
         view.contentMode = .scaleAspectFit
         view.setContentHuggingPriority(.defaultHigh, for: .vertical)
         return view
     }()
-    
-    private lazy var activity: UIActivityIndicatorView = {
+
+    public private(set) lazy var activity: UIActivityIndicatorView = {
         let view: UIActivityIndicatorView
         if #available(iOS 13.0, *) {
             view = UIActivityIndicatorView(style: .large)

                NSLayoutConstraint(item: contentView, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1.0, constant: 0.0),
                NSLayoutConstraint(item: contentView, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1.0, constant: 0.0),
                NSLayoutConstraint(item: contentView, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1.0, constant: 0.0),
            ])

            NSLayoutConstraint.activate(additionalConstraints)
        }
    }

    override internal var backgroundColor: UIColor? {
        didSet {
            container.backgroundColor = backgroundColor
        }
    }


            trailingLessThan,
        ])
    }

}

#endif
