//
//  Video.swift
//  MVMCoreUI
//
//  Created by Scott Pfeil on 1/26/21.
//  Copyright © 2021 Verizon Wireless. All rights reserved.
//
import AVKit

open class Video: View {
    public let videoViewController = AVPlayerViewController()
    private weak var containingView: UIView?
    
    /// Used to track the state and respond..
    private var stateKVOToken: NSKeyValueObservation?

    open override func setupView() {
        super.setupView()
        videoViewController.view.translatesAutoresizingMaskIntoConstraints = false
        addSubview(videoViewController.view)
        NSLayoutConstraint.constraintPinSubview(toSuperview: videoViewController.view)
        videoViewController.videoGravity = .resizeAspectFill
    }
    
    /// Checks if the video is visible in the molecule delegate
    open func isVisibleInDelegate() -> Bool {
        guard let containingView = containingView else { return true }
        return isVisible(in: containingView)
    }
    
    /// Checks if the video is visible in the passed in view
    open func isVisible(in view: UIView) -> Bool {
        return MVMCoreUIUtility.isView(self, visibleIn: view)
    }
    
    open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
        /// Detach the view from it's previous model before setting.
        (self.model as? VideoModel)?.view = nil
        containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view
        super.set(with: model, delegateObject, additionalData)
        
        guard let model = model as? VideoModel else { return }
        if let controller = delegateObject?.moleculeDelegate as? UIViewController {
            controller.addChild(videoViewController)
            videoViewController.didMove(toParent: controller)
        }
        videoViewController.showsPlaybackControls = model.showControls
        videoViewController.player = model.videoDataManager.player
        addStateObserver()
        model.addVisibilityHalting(for: self, delegateObject: delegateObject)
        
        switch (model.videoDataManager.videoState) {
        case .none:
            // Begin loading the video
            model.videoDataManager.loadVideo()
        case .loaded:
            guard isVisibleInDelegate() else { return }
            // Video loaded, unhalt it if necessary.
            model.halted = false
        default:
            break
        }
    }
    
    /// Listens and responds to video loading state changes.
    private func addStateObserver() {
        removeStateObserver()

        guard stateKVOToken == nil,
              let model = model as? VideoModel else { return }
        
        // To know when the video player item is done loading.
        stateKVOToken =
            model.videoDataManager.observe(\.videoState) { [weak self] (item, change) in
            guard let self = self,
                  let model = self.model as? VideoModel,
                  item == model.videoDataManager else { return }
                
            switch item.videoState {
            case .loaded:
                // Setting videoController's player must be in the main thread
                MVMCoreDispatchUtility.performSyncBlock(onMainThread: {
                    // Play the video
                    self.videoViewController.player = item.player
                    if !model.halted && model.autoPlay && self.isVisibleInDelegate() {
                        item.player?.play()
                        UIAccessibility.post(notification: .screenChanged, argument: self)
                    }
                })
            case .failed:
                if let errorObject = item.loadFailedError {
                    MVMCoreLoggingHandler.addError(toLog: errorObject)
                }
            default:
                break
            }
        }
    }

    private func removeStateObserver() {
        stateKVOToken?.invalidate()
        stateKVOToken = nil
    }
    
    deinit {
        removeStateObserver()
    }
}
