//
//  MP_PlayerManager.swift
//  MusicPlayer
//
//  Created by Mr.Zhou on 2024/5/10.
//

import UIKit
import AVFoundation
import MediaPlayer
import AVKit
///播放器播放状态
enum MP_PlayerStateType:Int {
    ///未启动
    case Null = 0
    ///运行中
    case Playing = 1
    ///暂停中
    case Pause = 2
}
///播放器播放方式
enum MP_PlayerPlayType:Int {
    ///列表顺序播放
    case normal = 0
    ///列表随机播放
    case random = 1
    ///单曲播放（当前音乐无限循环）
    case single = 2
}

///播放器启动时执行事件（播放的音乐）
typealias MP_PlayTimerStartAction = () -> Void
///播放器运行时执行事件(当前时间值，最大时间值)
typealias MP_PlayTimerRunAction = (_ currentTime:TimeInterval, _ duration:TimeInterval) -> Void
///播放器结束时执行事件
typealias MP_PlayTimerEndAction = () -> Void
///播放器暂停时执行事件
typealias MP_PlayTimerPauseAction = () -> Void
///播放器继续时执行事件
typealias MP_PlayTimerResumeAction = () -> Void
///播放器终止时执行事件
typealias MP_PlayTimerStopAction = () -> Void
///播放器调整进度时执行事件
typealias MP_PlayTimerEditEndAction = () -> Void
///播放器缓存值执行事件
typealias MP_PlayCacheValueAction = (_ currentValue:TimeInterval, _ duration:TimeInterval) -> Void
///播放器
class MP_PlayerManager:NSObject{
    ///控制器单例
    static let shared = MP_PlayerManager()
    ///播放器
    private var player:AVPlayer = AVPlayer()
    ///load模块
    var loadPlayer:MPPositive_PlayerLoadViewModel!{
        didSet{
            if loadPlayer != nil {
                //当load模块接受到新值的时候，发出通知，提醒底部模块状态切换
                NotificationCenter.notificationKey.post(notificationName: .pup_bottom_show)
            }else {
                //用户清空了load模块，隐藏播放器
                NotificationCenter.notificationKey.post(notificationName: .player_delete_list)
                playState = .Null
            }
        }
    }
    //当前播放器状态
    private var playState:MP_PlayerStateType = .Null{
        didSet{
            //当播放器状态发生变化时，对播放器按钮状态进行切换
            NotificationCenter.notificationKey.post(notificationName: .switch_player_status, object: playState)
        }
    }
    ///获取播放器播放状态
    func getPlayState() -> MP_PlayerStateType {
        return playState
    }
    ///当前播放器播放方法
    private var playType:MP_PlayerPlayType = .normal{
        didSet{
            //当播放器播放方式变化后，发出通知
            NotificationCenter.notificationKey.post(notificationName: .player_type_switch)
        }
    }
    ///获取播放器播放方法
    func getPlayType() -> MP_PlayerPlayType {
        return playType
    }
    /// 设置播放器播放方式
    /// - Parameter type: 新的类型
    func setPlayType(_ type:MP_PlayerPlayType) {
        playType = type
        if playType == .random {
            
        }
    }
    ///播放器启动时执行事件记录
    private var startActionBlock:MP_PlayTimerStartAction!
    ///播放器运行时执行事件记录
    var runActionBlock:MP_PlayTimerRunAction!
    ///播放器缓存值闭包
    var cacheValueBlock:MP_PlayCacheValueAction!
    private override init() {
        super.init()
        // 添加观察者，监听播放结束事件
        NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(_ :)), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
        //监听用户切换了音乐
        NotificationCenter.notificationKey.add(observer: self, selector: #selector(userSwitchCurrentVideoAction(_ :)), notificationName: .positive_player_reload)
        //监听网络状态恢复可用
        NotificationCenter.notificationKey.add(observer: self, selector: #selector(netWorkReachableAction(_ :)), notificationName: .net_switch_reachable)
    }
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    /// 开始播放音乐
    /// - Parameters:
    ///   - startAction: 开始播放时需要执行的事件
    ///   - runAction: 播放途中需要执行的事件
    ///   - endAction: 结束播放时需要执行的事件
    func play(startAction:MP_PlayTimerStartAction? = nil) {
        guard loadPlayer != nil, loadPlayer.currentVideo != nil else {
            //当两项数据皆为空时，播放器无法播放
            print("Player No Data")
            return
        }
        //检索播放器状态
        switch playState {
        case .Null://未启动
            break
        case .Playing://启动中
            player.pause()
        case .Pause://暂停中
            break
        }
        //记录事件
        if startAction != nil {
            startActionBlock = startAction
        }
        //覆盖播放器原有的playerItem
        player.replaceCurrentItem(with: loadPlayer.currentVideo.resourcePlayerItem)
        //将进度回归为0
        player.seek(to: .zero)
        //设置一个秒为刻度的时间值
        let interval:CMTime = .init(seconds: 1, preferredTimescale: .init(1))
        //为播放器添加运行时主线程每秒触发事件
        player.addPeriodicTimeObserver(forInterval: interval, queue: .main, using: { [weak self] (time) in
            guard let self = self else { return }
            //转化为当前播放进度秒值
            let currentDuration = CMTimeGetSeconds(time)
            //获取当前播放音乐资源的最大时间值
            let maxDuration = getMusicDuration()
            if maxDuration.isNaN == false {
                //判断当值进度是否超越最大时间值
                if currentDuration <= maxDuration {
                    //没有，执行运行时时间
                    if runActionBlock != nil {
                        runActionBlock!(currentDuration, maxDuration)
                    }
                }
            }
        })
        //对当前播放PlayerItem设置监听状态
        //准备状态
        loadPlayer.currentVideo?.resourcePlayerItem?.addObserver(self, forKeyPath: "status", options: [.old,.new], context: nil)
        //当前缓冲值
        loadPlayer.currentVideo?.resourcePlayerItem.addObserver(self, forKeyPath: "loadedTimeRanges", options: [.old,.new], context: nil)
        //是否具备足够播放的能力
        loadPlayer.currentVideo?.resourcePlayerItem.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: [.old,.new], context: nil)
        //启动全部预加载
        loadPlayer.listViewVideos.forEach({$0.preloadAsset($0.resourceAsset)})
    }
    ///网络状态恢复正常
    @objc private func netWorkReachableAction(_ sender:Notification) {
        //监听到网络状态恢复，检索当前播放器是否正在播放
        if loadPlayer?.currentVideo != nil {
            //有音乐播放，获取当前播放进度
            let currentTime = loadPlayer.currentVideo!.resourcePlayerItem.currentTime()
            //手动调整播放时间点，以此重启播放器缓存
            player.seek(to: currentTime)
            player.play()
            playState = .Playing
        }
    }
    
    //实现KVO监听
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        guard let keyPath = keyPath else {
            return
        }
        //根据keyPath检索
        switch keyPath {
        case "status"://playerItem状态
            if let statuValue = change?[.newKey] as? Int, statuValue == AVPlayerItem.Status.readyToPlay.rawValue {
                //判断当前播放器是否在播放当前音乐中
                if playState != .Playing {
                    //当statuVlaue值等于playerItem准备播放的值，说明已经准备好播放
                    print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 已经准备好播放")
                }
            }else {
                print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 未做好准备播放，失败原因是\(loadPlayer.currentVideo?.resourcePlayerItem.error?.localizedDescription ?? "")")
                //资源更新，重新配置一下相关内容
                loadPlayer.remakeImproveData {
                    [weak self] in
                    guard let self = self else {return}
                    //重新播放
                    play()
                }
            }
        case "loadedTimeRanges"://当前缓冲进度
            //获取当前播放Item的缓冲值组
            if let timeRanges = loadPlayer.currentVideo?.resourcePlayerItem.loadedTimeRanges.map({$0.timeRangeValue}), let first = timeRanges.first {
                //获取开始时间的秒数
                let startSeconds = first.start.seconds
                //获取缓冲区的持续时间
                let durationSeconds = first.duration.seconds
                //计算当前缓冲总时间
                let bufferedSeconds = startSeconds + durationSeconds
                //获取当前播放音乐资源的最大时间值
                let maxDuration = getMusicDuration()
                //传递缓存值
                if cacheValueBlock != nil {
                    cacheValueBlock!(bufferedSeconds, maxDuration)
                }
            }
            
        case "playbackLikelyToKeepUp"://是否存在足够的数据开始播放
            if let playbackLikelyToKeepUp = change?[.newKey] as? Bool, playbackLikelyToKeepUp == true {
                if playState != .Playing {
                    //还未播放当前音乐，启动播放
                    player.play()
                    playState = .Playing
                    //执行开始播放闭包
                    if startActionBlock != nil {
                        startActionBlock!()
                    }
                }
            }else {
                //没有足够的数据支持播放
                player.pause()
                playState = .Null
            }
        default:
            break
        }
    }
    //MARK: - 获取当前音乐总长度
    ///获取音乐资源总时长
    private func getMusicDuration() -> TimeInterval {
        return CMTimeGetSeconds(player.currentItem?.duration ?? .zero)
    }
    
    //MARK: - 音乐播放结束
    //当前音乐播放结束时
    @objc private func playerDidFinishPlaying(_ sender:Notification) {
        //检索播放器对象
        guard playState == .Playing else {
            return
        }
        switch playType {
        case .single:
            //重播
            player.seek(to: CMTime.zero)
            player.play()
        default:
            //当前音乐播放器正在播放中，下一首
            nextEvent()
        }
    }
    //MARK: - 暂停播放
    ///内部暂停播放
    private func pause() {
        //检索播放状态，是否进行中
        guard playState == .Playing else {
            //未处于播放中
            print("Player is not in playing")
            return
        }
        //暂停播放器
        player.pause()
        //切换播放器状态
        playState = .Pause
    }
    /// 暂停播放
    /// - Parameter pauseAction: 暂停时要执行的事件
    func pause(_ pauseAction:MP_PlayTimerPauseAction? = nil) {
        //检索播放状态，是否进行中
        guard playState == .Playing else {
            //未处于播放中
            print("Player is not in playing")
            return
        }
        if pauseAction != nil {
            pauseAction!()
        }
        //暂停播放器
        player.pause()
        //切换播放器状态
        playState = .Pause
    }
    //MARK: - 继续播放
    /// 继续播放
    /// - Parameter resumeAction: 继续时要执行的事件
    func resume(_ resumeAction:MP_PlayTimerResumeAction? = nil) {
        //检索播放状态，是否暂停中
        guard playState == .Pause else {
            //未处于暂停中
            print("Player is not paused")
            return
        }
        if resumeAction != nil {
            resumeAction!()
        }
        //继续播放器
        player.play()
        //切换播放器状态
        playState = .Playing
    }
    ///内部继续播放
    private func resume() {
        //检索播放状态，是否暂停中
        guard playState == .Pause else {
            //未处于暂停中
            print("Player is not paused")
            return
        }
        //继续播放器
        player.play()
        //切换播放器状态
        playState = .Playing
    }
    
    //MARK: - 停止播放
    //停止播放
    func stop() {
        //检索播放状态，是否已启动
        guard playState != .Null else {
            //未启动
            print("Player is not started")
            return
        }
        player.pause()
        playState = .Null
    }
    //MARK: - 切歌（上一首/下一首）
    ///上一首歌事件
    func previousEvent() {
        //将播放器状态调整未播放
        playState = .Null
        var nextIndex:Int = 0
        //判断当前音乐播放方式
        switch playType {
        case .random://随机，播放随机列表内容
            for (index, item) in loadPlayer.randomVideos.enumerated() {
                if item.videoId == loadPlayer.currentVideo?.song.videoId {
                    //找到播放音乐的索引
                    nextIndex = index - 1
                }
            }
            //假如next为负数，则直接播放列表最后一首
            if nextIndex < 0 {
                //播放列表最后一首
                let last = loadPlayer.randomVideos.last
                loadPlayer.improveData(last?.videoId ?? "", isRandom: true)
            }else {
                //查询列表对应单曲
                let song = loadPlayer.randomVideos[nextIndex]
                loadPlayer.improveData(song.videoId ?? "", isRandom: true)
            }
        default://常规播放或者单曲播放
            for (index, item) in loadPlayer.songVideos.enumerated() {
                if item.videoId == loadPlayer.currentVideo?.song.videoId {
                    //找到播放音乐的索引
                    nextIndex = index - 1
                }
            }
            //假如next为负数，则直接播放列表最后一首
            if nextIndex < 0 {
                //播放列表最后一首
                let last = loadPlayer.songVideos.last
                loadPlayer.improveData(last?.videoId ?? "")
            }else {
                //查询列表对应单曲
                let song = loadPlayer.songVideos[nextIndex]
                loadPlayer.improveData(song.videoId ?? "")
            }
        }
    }
    ///下一首歌事件
    func nextEvent() {
        //将播放器状态调整未播放
        playState = .Null
        var nextIndex:Int = 0
        switch playType {
        case .random:
            for (index, item) in loadPlayer.randomVideos.enumerated() {
                if item.videoId == loadPlayer.currentVideo?.song.videoId {
                    //找到播放音乐的索引
                    nextIndex = index + 1
                }
            }
            //超出播放列表数
            if nextIndex > (loadPlayer.randomVideos.count-1) {
                //播放列表第一首
                let first = loadPlayer.randomVideos.first
                loadPlayer.improveData(first?.videoId ?? "", isRandom: true)
            }else {
                //存在下一首,获取下一首ID，并播放
                let song = loadPlayer.randomVideos[nextIndex]
                loadPlayer.improveData(song.videoId ?? "", isRandom: true)
            }
        default:
            for (index, item) in loadPlayer.songVideos.enumerated() {
                if item.videoId == loadPlayer.currentVideo?.song.videoId {
                    //找到播放音乐的索引
                    nextIndex = index + 1
                }
            }
            //超出播放列表数
            if nextIndex > (loadPlayer.songVideos.count-1) {
                //播放列表第一首
                let first = loadPlayer.songVideos.first
                loadPlayer.improveData(first?.videoId ?? "")
            }else {
                //存在下一首,获取下一首ID，并播放
                let song = loadPlayer.songVideos[nextIndex]
                loadPlayer.improveData(song.videoId ?? "")
            }
        }
    }
    ///监听到用户切换当前音乐
    @objc private func userSwitchCurrentVideoAction(_ sender:Notification) {
        //将播放器状态调整未播放
        playState = .Null
        //暂停播放
        player.pause()
        //优先获取传递的值
        if let video = sender.object as? MPPositive_SongViewModel {
            //切歌时移除KVO监听
            video.resourcePlayerItem.removeObserver(self, forKeyPath: "status")
            video.resourcePlayerItem.removeObserver(self, forKeyPath: "loadedTimeRanges")
            video.resourcePlayerItem.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
        }
        if cacheValueBlock != nil {
            cacheValueBlock!(0, 1)
        }
        if loadPlayer.currentVideo != nil {
            //开始播放
            play(startAction: startActionBlock)
        }
    }
    
    ///播放器进度调整状态
    func setEditPorgressStatu() {
        guard playState != .Null else {
            return
        }
        //播放器进入暂停状态
        pause()
    }
    /// 调整播放器进度值，必须和 setEditPorgressStatu()搭配使用
    /// - Parameters:
    ///   - progress: 要调整进度值（保证在0-1范围内，超出该方法不会响应）
    func setEditProgressEnd(_ progress:Float, endAction:MP_PlayTimerEditEndAction? = nil) {
        guard playState != .Null else {
            return
        }
        guard progress >= 0, progress <= 1 else {
            return
        }
        //根据当前进度值设置时间节点
        let timePoint:Double = Double(progress)*getMusicDuration()
        //设置对应的时间值
        let time:CMTime = .init(seconds: timePoint, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
        //调整播放器时间
        player.seek(to: time)
        //恢复播放
        resume()
        if endAction != nil {
            endAction!()
        }
    }
}
