 import AVFoundation
 import UIKit
 
+public struct VideoInfo {
+    public let name: String
+    public let type: String
+}
+
+public enum VideoBackgroundError: Error {
+    case videoNotFound(VideoInfo)
+}
+
 /// Class that plays a video on a UIView.
 public class VideoBackground {
-    private lazy var player = AVPlayer()
+    public static let shared = VideoBackground()
+
+    private lazy var player = AVQueuePlayer()
 
     private lazy var layer = AVPlayerLayer()
 
     private lazy var overlayView = UIView()
 
+    private lazy var playerItems = [AVPlayerItem]()
+
+    private lazy var willLoopVideo = true
+
+    private lazy var playerItemDidPlayToEndObservers = [NSObjectProtocol]()
+
+    private var viewBoundsObserver: NSKeyValueObservation?
+
     private var applicationWillEnterForegroundObserver: NSObjectProtocol?
 
-    private var videoEndedObserver: NSObjectProtocol?
-
-    private var viewBoundsObserver: NSKeyValueObservation?
-
-    private var willLoopVideo = true
+    private var playerItemDidPlayToEndObserver: NSObjectProtocol?
 
     /// Initializes a VideoBackground instance.
     public init() {

    ///     - videoName: String name of video that you have added to your project.
    ///     - videoType: String type of the video. e.g. "mp4"
    ///     - isMuted: Bool indicating whether video is muted. Defaults to true.
    ///     - aplha: CGFloat between 0 and 1. The higher the value, the darker
    ///              the video. Defaults to 0.
    ///     - willLoopVideo: Bool indicating whether video should restart when finished.
    ///                      Defaults to true.
    public func play(view: UIView,
                     videoName: String,
                     videoType: String,
                     isMuted: Bool = true,
                     alpha: CGFloat = 0,
                     willLoopVideo: Bool = true) {
        guard let path = Bundle.main.path(forResource: videoName, ofType: videoType) else {
            print("BackgroundVideo: [ERROR] could not find video")
            return
        }

        let url = URL(fileURLWithPath: path)
        player = AVPlayer(url: url)
        player.actionAtItemEnd = .none
        player.isMuted = isMuted
        player.play()



        if alpha > 0 && alpha <= 1 {
            overlayView = UIView(frame: view.frame)
            overlayView.alpha = alpha
            overlayView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
            overlayView.backgroundColor = .black
            view.addSubview(overlayView)
            view.sendSubview(toBack: overlayView)
        }

        // Adjust frames upon device rotation
        viewBoundsObserver = view.layer.observe(\.bounds) { [weak self] view, _ in
            DispatchQueue.main.async {
                self?.layer.frame = view.frame
            }
        }
    }

    private func cleanUp() {
        overlayView.removeFromSuperview()

        player.removeAllItems()

        removePlayerItemDidPlayToEndObservers()

        playerItemDidPlayToEndObservers.removeAll()
    }

    private func removePlayerItemDidPlayToEndObservers() {
        playerItemDidPlayToEndObservers.forEach {
            NotificationCenter.default.removeObserver($0)
        }
    }

    private func makeAVPlayerItems(_ videos: [VideoInfo]) throws -> [AVPlayerItem] {
        return try videos.map {
            return try makeAVPlayerItem($0)
        }
    }

    private func makeAVPlayerItem(_ videoInfo: VideoInfo) throws -> AVPlayerItem {
        guard let path = Bundle.main.path(forResource: videoInfo.name, ofType: videoInfo.type) else {
            throw VideoBackgroundError.videoNotFound(videoInfo)
        }
        let url = URL(fileURLWithPath: path)
        return AVPlayerItem(url: url)
    }

    deinit {
        if let applicationWillEnterForegroundObserver = applicationWillEnterForegroundObserver {
            NotificationCenter.default.removeObserver(applicationWillEnterForegroundObserver)
        }
        if let playerItemDidPlayToEndObserver = playerItemDidPlayToEndObserver {
            NotificationCenter.default.removeObserver(playerItemDidPlayToEndObserver)
        }
        removePlayerItemDidPlayToEndObservers()
        viewBoundsObserver?.invalidate()
    }
}
