//
//  PQStuckPointEditerController.swift
//  PQSpeed
//
//  Created by ak on 2021/4/26.
//  Copyright © 2021 BytesFlow. All rights reserved.
//  功能：卡点音乐编辑界面

// 创建不同玩法的类型 （1：跳跃卡点，2：快慢速，3：仅配乐）
public enum createStickersModel: Int {
    case createStickersModelPoint = 1 // 跳跃卡点
    case createStickersModelSpeed = 2 // 快慢速
    case createStickersModelOnlyMusic = 3 // 仅配乐
}

import BFCommonKit
import Foundation
import ObjectMapper
import Photos
import RealmSwift
import UIKit

class PQStuckPointEditerController: PQBaseViewController {
    // 是否导出视频成功
    var isExportVideosSuccess: Bool = false
    // 是否请求卡点数据成功
    var isStuckPointDataSuccess: Bool = false
    // 是否同步音乐成功
    var isSynchroMusicInfoSuccess: Bool = false
    /// 当前所有的filter
    var filters: Array = Array<ImageProcessingOperation>.init()
    // 选中所有素材的的总时长 再进入编辑界面时已经不包括图片的时长
    var selectedTotalDuration: Float64 = 0
    // 选择的总数
    var selectedDataCount: Int = 0
    // 选择的图片总数
    var selectedImageDataCount: Int = 0
    // 选中的素材数据
    var selectedPhotoData: [PHAsset]? {
        didSet {
            if selectedPhotoData != nil, (selectedPhotoData?.count ?? 0) > 0 {
                selectedMetarialData = Array<PQEditVisionTrackMaterialsModel>.init()
                selectedPhotoData?.forEach { phAsset in
                    let metarialData = PQEditVisionTrackMaterialsModel()
                    metarialData.asset = phAsset
                    metarialData.width = Float(phAsset.pixelWidth)
                    metarialData.itemWidth = Float(phAsset.pixelWidth)
                    metarialData.height = Float(phAsset.pixelHeight)
                    metarialData.itemHeight = Float(phAsset.pixelHeight)
                    if phAsset.mediaType == .image {
                        metarialData.type = "image"
                    } else if phAsset.mediaType == .video {
                        metarialData.type = "video"
                        metarialData.duration = Float64(phAsset.duration)
                    }
                    metarialData.canvasFillType = phAsset.canvasFillType ?? ""
                    metarialData.locationPath = phAsset.localPath ?? ""
                    metarialData.selectedIndex = phAsset.selectedIndex ?? 1
                    metarialData.originalData = phAsset.originalData
//                    metarialData.coverImageUI = phAsset.image
                    selectedMetarialData?.append(metarialData)
                }
            }
        }
    }

    var selectedMetarialData: [PQEditVisionTrackMaterialsModel]?
    // 选中的音乐数据
    var stuckPointMusicData: PQVoiceModel?
    // 保存所有段的所有贴纸，音频信息,用于播放器的渲染使用
    var projectModel: PQEditProjectModel = PQEditProjectModel()
    // 从草稿箱进入的项目数据
    var draftProjectModel: PQEditProjectModel?
    var mStickers: [PQEditVisionTrackMaterialsModel]?
    // 播放器的开始和结束时间,1，刚进界面使用推荐的开始结束时间，2，用户修改起结点时修改
    var playeTimeRange: CMTimeRange = CMTimeRange()

    // 首帧图片
    var firstFrameImage: UIImage?{
        didSet{
            if firstFrameImage != nil {
                self.playerView.layer.contents = firstFrameImage?.cgImage
            }
        }
    }

    // add by ak 是否是再创作模式
    var isReCreate: Bool = false
    public var reCreateVideoData: PQReCreateModel? // 再创作数据
    // 最后一个选择的模式 BTN 用于还原选中状态
    var lastEditModelBtn: UIButton?

    // add by ak  最大、最小速度 有固定值和自定义，当快慢速下两个值都有效，当跳跃卡点只有maxSpeed有效
    // 快慢速模式的 速度设置，快/慢速
    var modelSpeed_maxSpeed: Float = 1.0
    var modelSpeed_minSpeed: Float = 1.0
    //跳跃模式的速度
    var modelPoint_speed:Float = 1.0

    // 快慢速最后一次选择的速度位置
    var lastSpeedSelectIndex: Int = 0
    // 跳跃卡点最后一次选择的速度位置
    var lastJumpSpeedSelectIndex: Int = 0
    // 循环次数设置最后一次选择的位置
    var lastCyclesSelectIndex: Int = -1

    // 当前选择的玩法模式
    var currentCreateStickersModel: createStickersModel = .createStickersModelSpeed

    // 最终使用的卡点数据
    var finallyStuckPoints: Array = Array<Float>.init()
    var finallyStuckPointsInt64: Array = Array<Int64>.init()
    // 最终使用的音频时长
    var finallyUserAudioTime: Float = 0.0

    // 注意推荐时间位置和后面最近的卡点时间与0.3的关系
    // 保存丢卡点处理后的卡点信息推荐开始到最后倒数第二个
    // 经过档位处理后的卡点信息
    var stuckPointsTemp: Array = Array<Float>.init()
    var stuckPointsTempInt64: Array = Array<Int64>.init()


    // 下一步
    lazy var nextBtn: UIButton = {
        let nextBtn = UIButton(type: .custom)
        nextBtn.frame = CGRect(x: cScreenWidth - 16 - cDefaultMargin * 6, y: cDevice_iPhoneStatusBarHei + (cDevice_iPhoneNavBarHei - cDefaultMargin * 3) / 2, width: cDefaultMargin * 6, height: cDefaultMargin * 3)
        nextBtn.setTitle("合成", for: .normal)
        nextBtn.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: .medium)
        nextBtn.addTarget(self, action: #selector(nextBtnClick(sender:)), for: .touchUpInside)
        nextBtn.backgroundColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
        nextBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#FFFFFF"), for: .normal)
        nextBtn.uxy_acceptEventInterval = 0.5
        nextBtn.addCorner(corner: 3)
        return nextBtn
    }()

    // 播放器显示 view
    lazy var playerView: PQGPUImagePlayerView = {
        let playerView = PQGPUImagePlayerView(frame: .zero)
        playerView.backgroundColor = PQBFConfig.shared.styleBackGroundColor
        playerView.isShowLine = false
        playerView.showGaussianBlur = true
        playerView.pause()
        playerView.renderViewOnClickHandle = { [weak self] in
            self?.musicEditBGView.pausePlayer()
        }
        playerView.playerEmptyView.isHidden = true
        return playerView
    }()

    /// 节奏选择视图
    lazy var sustomSwitchView: PQCustomSwitchView = {
        let sustomSwitchView = PQCustomSwitchView(frame: CGRect(x: 16, y: 0, width: 180, height: 30), titles: ["慢节奏", "适中", "快节奏"], defaultIndex: stuckPointMusicData?.speed ?? 2)
        sustomSwitchView.switchChangeHandle = { [weak self] sender in
            // 改变速率,.只有快慢速且非只有图片素材时自动+1处理
            self?.stuckPointMusicData?.speed = sender.tag
            self?.musicEditBGView.pausePlayer()

            self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.bgmInfo?.rhythmMusicSpeed = sender.tag
            // 播放前先暂停
//            self?.playerView.stop()
            // 开始播放
            self?.settingPlayerView()

            // 下面都是统计
            if self?.currentCreateStickersModel == .createStickersModelPoint {
                PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_selectMusicVideoRhythm, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, remindmsg: "点击上报：选择节奏")
            } else if self?.currentCreateStickersModel == .createStickersModelSpeed {
                PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_selectSpeedRhythm, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, remindmsg: "点击上报：选择节奏")
            } else if self?.currentCreateStickersModel == .createStickersModelOnlyMusic {
                PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_selectMusicVideoRepeatRhythm, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, remindmsg: "点击上报：选择节奏")
            }
        }
        return sustomSwitchView
    }()

    /// 裁剪视图
    lazy var stuckPointCuttingView: PQStuckPointCuttingView = {
        let stuckPointCuttingView = PQStuckPointCuttingView(frame: CGRect(x: 0, y: optionlineView.frame.minY - 85 - 28, width: view.frame.width, height: 80), duration: CGFloat(Float(stuckPointMusicData?.duration ?? "0") ?? 0), suggestRhythmStartTime: CGFloat(stuckPointMusicData?.suggestRhythmStartTime ?? 0))
        /// 裁剪进度回调
        stuckPointCuttingView.videoDidBeginDrag = { [weak self] in
            BFLog(message: "开始划动")
            self?.playerView.pause()
        }
        /// 播放进度回调
        stuckPointCuttingView.videoProgressDidChanged = { [weak self] progress in
            BFLog(message: "进度更新返回--progress = \(progress) \(String(describing: self?.playerView.mPlayeTimeRange))")
        }
        /// 拖缀结束的回调 type - 1-拖动左边裁剪结束 2--拖动右边裁剪结束 3-进度条拖动结束 4-滑动结束
        stuckPointCuttingView.videoDidEndDragging = { [weak self] type, startTime, endTime, progress in
            BFLog(1, message: "拖拽结束返回--type = \(type),startTime = \(startTime),endTime = \(endTime),progress = \(progress)")
            self?.playerView.pause()
            self?.musicEditBGView.pausePlayer()

            // 修改最新值
            self?.stuckPointMusicData?.startTime = Float64(startTime)
            self?.stuckPointMusicData?.endTime = Float64(endTime)
            // 红的指针完成
            if type == 3 {
                if CMTimeGetSeconds(self?.playerView.mPlayeTimeRange?.end ?? .zero) == 0 {
                    BFLog(message: "mPlayeTimeRange is error")
                    return
                }
                let newBeginSconds = (Double(startTime) + (Double(endTime) - Double(startTime)) * Double(progress)) * 600
                BFLog(message: " newBeginSconds is \(newBeginSconds)")
                let seekTimeRange: CMTimeRange = CMTimeRange(start: CMTime(value: CMTimeValue(Int64(newBeginSconds)), timescale: 600), end:
                    CMTime(value: CMTimeValue(Int64(endTime * 600)), timescale: 600))
                BFLog(message: "修改的开始 \(CMTimeGetSeconds(seekTimeRange.start)) 结束  \(CMTimeGetSeconds(seekTimeRange.end))")
                // 重新设置有效缓存
                self?.playerView.configCache(beginTime: CMTimeGetSeconds(seekTimeRange.start))
                self?.playerView.play(pauseFirstFrame: false, playeTimeRange: seekTimeRange)

            } else {
                // 更改素材开始时间及结束时间
                self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.out = Float64(endTime)
                self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.model_in = Float64(startTime)
                self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.timelineIn = Float64(startTime)
                self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.timelineOut = Float64(endTime)
                BFLog(message: "调整后总时长: \(endTime - startTime) startTime:\(startTime) endTime:\(endTime)")
                // 初始化音频的开始和结束时间
                self?.playeTimeRange = CMTimeRange(start: CMTimeMakeWithSeconds(Float64(startTime), preferredTimescale: BASE_FILTER_TIMESCALE), end: CMTimeMakeWithSeconds(Float64(endTime), preferredTimescale: BASE_FILTER_TIMESCALE))

                self?.dealParameter(model: self?.currentCreateStickersModel ?? .createStickersModelSpeed)
                if((self?.finallyStuckPoints.count ?? 0) < 1){
                    BFLog(message: "finallyStuckPoints data is error!!!!")
                    return
                }

                DispatchQueue.global().async { // 并行、异步
                    let beginTime = Date()

                    self?.mStickers = self?.createStickers(sections: self?.projectModel.sData?.sections ?? List(), inputSize: CGSize(width: CGFloat(self?.projectModel.sData?.videoMetaData?.videoWidth ?? 0), height: CGFloat(self?.projectModel.sData?.videoMetaData?.videoHeight ?? 0)), model: self?.currentCreateStickersModel ?? .createStickersModelSpeed)

                    DispatchQueue.main.async { // 串行、异步
                        self?.playerView.mStickers = self?.mStickers
                        BFLog(message: "endTime is endTimeendTime \(Date().timeIntervalSince(beginTime))")
                        self?.playerView.play(pauseFirstFrame: false, playeTimeRange: self!.playeTimeRange)

                        // 更新一下时间条的UI总时间 及数据
                        self?.stuckPointCuttingView.videoDuration = CGFloat(self?.finallyUserAudioTime ?? 0)

                        self?.stuckPointCuttingView.stuckPointStartTime = CGFloat(CMTimeGetSeconds(self?.playeTimeRange.start ?? .zero))
                        self?.stuckPointCuttingView.stuckPointEndTime = CGFloat(CMTimeGetSeconds(self?.playeTimeRange.end ?? .zero))
                        self?.stuckPointCuttingView.tatalTimeLabel.text = "\(Float64(CMTimeGetSeconds(self?.playeTimeRange.end ?? .zero) - CMTimeGetSeconds(self?.playeTimeRange.start ?? .zero)).formatDurationToHMS())"
                    }
                }
            }
        }
        return stuckPointCuttingView
    }()

    /// 卡点模式标题
    lazy var pointEditRemindLab: UILabel = {
        let pointEditRemindLab = UILabel()
        pointEditRemindLab.backgroundColor = .clear
        pointEditRemindLab.textAlignment = .left
        pointEditRemindLab.font = UIFont.boldSystemFont(ofSize: 14)
        pointEditRemindLab.textColor = PQBFConfig.shared.styleTitleColor
        pointEditRemindLab.text = "卡点模式"
        return pointEditRemindLab
    }()

    /// 卡点模式标题
    lazy var speedTitleLab: UILabel = {
        let speedTitleLab = UILabel()
        speedTitleLab.backgroundColor = .clear
        speedTitleLab.textAlignment = .left
        speedTitleLab.font = UIFont.boldSystemFont(ofSize: 14)
        speedTitleLab.textColor = PQBFConfig.shared.styleTitleColor
        speedTitleLab.text = "节奏变化"
        return speedTitleLab
    }()

    /// 卡点模式下方操作区背景
    lazy var pointEditBGView: UIView = {
        let pointEditBGView = UIView()
        pointEditBGView.backgroundColor = .clear
        return pointEditBGView
    }()

    /// 下方音乐编辑操作区背景
    lazy var musicEditBGView: PQSelecteMusicView = {
        let musicEditBGView = PQSelecteMusicView()
        musicEditBGView.backgroundColor = .clear
        musicEditBGView.isUserInteractionEnabled = true
        musicEditBGView.isHidden = true
        musicEditBGView.musicSearchBtn.addTarget(self, action: #selector(musicSearchBtnClick(sender:)), for: .touchUpInside)

        musicEditBGView.didSelectItemHandle = { [weak self] status in
            if status == .isSelected{
                if self?.playerView.status != .playing{
                    self?.playerView.RenderViewOnclick()
                }
            }else{
                self?.playerView.pause()
            }
        }
        musicEditBGView.btnClickHandle = { [weak self] _, bgmData in
            // 使用音乐
            self?.userstuckPointMusic(musicData: bgmData as? PQVoiceModel)
        }

        return musicEditBGView
    }()

    // 卡点编辑 btn
    lazy var pointEditerBtn: UIButton = {
        let pointEdterBtn = UIButton(type: .custom)
       
        pointEdterBtn.setImage(UIImage.moduleImage(named: "pointEditerBtn_n", moduleName: "BFFramework", isAssets: false), for: .normal)
         
        pointEdterBtn.setImage(UIImage.moduleImage(named: "pointEditerBtn_h", moduleName: "BFFramework", isAssets: false)?.withRenderingMode(.alwaysTemplate), for: .selected)
        pointEdterBtn.tintColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
        pointEdterBtn.addTarget(self, action: #selector(pointEditerBtnClick(sender:)), for: .touchUpInside)
        pointEdterBtn.isSelected = true
        pointEdterBtn.adjustsImageWhenHighlighted = false
        return pointEdterBtn
    }()

    // 音乐编辑 btn
    lazy var musicEditerBtn: UIButton = { 
        let musicEditerBtn = UIButton(type: .custom)
        musicEditerBtn.setImage(UIImage.moduleImage(named: "musicEditerBtn_n", moduleName: "BFFramework", isAssets: false), for: .normal)
        musicEditerBtn.setImage(UIImage.moduleImage(named: "musicEditerBtn_h", moduleName: "BFFramework", isAssets: false)?.withRenderingMode(.alwaysTemplate), for: .selected)
        musicEditerBtn.tintColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
        musicEditerBtn.addTarget(self, action: #selector(musicEditerBtnClick(sender:)), for: .touchUpInside)
        musicEditerBtn.adjustsImageWhenHighlighted = false
        return musicEditerBtn
    }()

    // 快慢速卡点模式 btn
    lazy var speedStuckBtn: UIButton = {
        let speedStuckBtn = UIButton(type: .custom)
        speedStuckBtn.addTarget(self, action: #selector(editModelClick1(sender:)), for: .touchUpInside)
        speedStuckBtn.setTitle("快慢速卡点", for: .normal)
        speedStuckBtn.titleLabel?.font = UIFont.systemFont(ofSize: 13, weight: .regular)
        jumpPointBtn.backgroundColor =  PQBFConfig.shared.pointEditNamalBackgroundColor
        speedStuckBtn.setTitleColor(UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue), for: .selected)
        speedStuckBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#959595"), for: .normal)
        speedStuckBtn.addCorner(corner: 5)
        speedStuckBtn.imagePosition(at: .top, space: 8)
        speedStuckBtn.tag = 1
        speedStuckBtn.adjustsImageWhenHighlighted = false
        speedStuckBtn.setImage(UIImage.moduleImage(named: PQBFConfig.shared.speedStuckBtnImage_N, moduleName: "BFFramework", isAssets: false), for: .normal)
        speedStuckBtn.setImage(UIImage.moduleImage(named: PQBFConfig.shared.speedStuckBtnImage_H, moduleName: "BFFramework", isAssets: false), for: .selected)
        return speedStuckBtn
    }()

//    //
//    lazy var speedStuckBtnGif: UIImageView = {
//        let speedStuckBtnGif = UIImageView()
//        speedStuckBtnGif.kf.setImage(with: URL(fileURLWithPath: currentBundlePath()!.path(forResource: "speedstuck_h", ofType: "gif")!))
//        speedStuckBtnGif.isHidden = true
//        return speedStuckBtnGif
//
//    }()

    // 跳转卡点模式 btn
    lazy var jumpPointBtn: UIButton = {
        let jumpPointBtn = UIButton(type: .custom)
    
        jumpPointBtn.setTitle("跳跃卡点", for: .normal)
        jumpPointBtn.titleLabel?.font = UIFont.systemFont(ofSize: 13, weight: .regular)
        jumpPointBtn.backgroundColor =  PQBFConfig.shared.pointEditNamalBackgroundColor
        jumpPointBtn.setTitleColor(UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue), for: .selected)
        jumpPointBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#959595"), for: .normal)
        jumpPointBtn.imagePosition(at: .top, space: 8)
        jumpPointBtn.addCorner(corner: 5)
        jumpPointBtn.tag = 2
        jumpPointBtn.addTarget(self, action: #selector(editModelClick1(sender:)), for: .touchUpInside)
        jumpPointBtn.adjustsImageWhenHighlighted = false
        
        jumpPointBtn.setImage(UIImage.moduleImage(named: PQBFConfig.shared.jumpPointBtnImage_N, moduleName: "BFFramework", isAssets: false), for: .normal)
        jumpPointBtn.setImage(UIImage.moduleImage(named: PQBFConfig.shared.jumpPointBtnImage_H, moduleName: "BFFramework", isAssets: false), for: .selected)
        return jumpPointBtn
    }()

//    lazy var jumpPointBtnGif: UIImageView = {
//        let jumpPointBtnGif = UIImageView()
//        jumpPointBtnGif.kf.setImage(with: URL(fileURLWithPath: currentBundlePath()!.path(forResource: "jumpPoint_n", ofType: "gif")!))
//        jumpPointBtnGif.isHidden = true
//        return jumpPointBtnGif
//
//    }()

    // 仅配乐模式 btn
    lazy var onlyMusicBtn: UIButton = {
        let onlyMusicBtn = UIButton(type: .custom)
 
        onlyMusicBtn.setTitle("仅配乐", for: .normal)
        onlyMusicBtn.titleLabel?.font = UIFont.systemFont(ofSize: 13, weight: .regular)
        onlyMusicBtn.backgroundColor =  PQBFConfig.shared.pointEditNamalBackgroundColor
        onlyMusicBtn.setTitleColor(UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue), for: .selected)
        onlyMusicBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#959595"), for: .normal)
        onlyMusicBtn.addCorner(corner: 5)
        onlyMusicBtn.tag = 3
        onlyMusicBtn.addTarget(self, action: #selector(editModelClick1(sender:)), for: .touchUpInside)
        onlyMusicBtn.adjustsImageWhenHighlighted = false
        
        onlyMusicBtn.setImage(UIImage.moduleImage(named: PQBFConfig.shared.onlyMusicBtnImage_N, moduleName: "BFFramework", isAssets: false), for: .normal)
        onlyMusicBtn.setImage(UIImage.moduleImage(named: PQBFConfig.shared.onlyMusicBtnImage_H, moduleName: "BFFramework", isAssets: false), for: .selected)

        return onlyMusicBtn
    }()

    // 操作面板上的分割线
    lazy var optionlineView: UIView = {
        let optionlineView = UIView()
        optionlineView.backgroundColor = PQBFConfig.shared.pointEditNamalBackgroundColor
        return optionlineView
    }()

    // 固定速度 UI
    lazy var speedSettingView: PQSpeedSettingView = {
        let speedSetView = PQSpeedSettingView()
        speedSetView.backgroundColor = .clear
        speedSetView.selectSpeedCallBack = { [weak self] maxSpeed, minSpeed, selectIndex, isSettingPlayer in
            BFLog(message: "固定maxSpeed is\(maxSpeed) minSpeed \(minSpeed)")
            self?.musicEditBGView.pausePlayer()

            if maxSpeed == -1.0 && minSpeed == -1.0 {
                self?.customSpeedSettingView.isHidden = false
                self?.customSpeedSettingView.viewType = self?.speedSettingView.viewType ?? 2
            } else {
                if maxSpeed != 0.0 {
                    // 更新最后一次选择的位置恢复时使用
                    if self?.speedSettingView.viewType == 1 {
                        self?.lastSpeedSelectIndex = selectIndex
                        self?.modelSpeed_maxSpeed = maxSpeed
                        self?.modelSpeed_minSpeed = minSpeed
                    } else if self?.speedSettingView.viewType == 2 {
                        self?.lastJumpSpeedSelectIndex = selectIndex
                        self?.modelPoint_speed = maxSpeed
                    } else {
                        self?.lastCyclesSelectIndex = selectIndex
                    }
    
                } else {
                    BFLog(message: "设置速度无效")
                }
            }
            if isSettingPlayer {
                self?.settingPlayerView()
            }
        }
        return speedSetView

    }()

    // 自定义速度
    lazy var customSpeedSettingView: PQCustomSpeedSettingView = {
        let customSpeedSetView = PQCustomSpeedSettingView(frame: CGRect(x: 0, y: cScreenHeigth - 354, width: cScreenWidth, height: 354))
        customSpeedSetView.isHidden = true
        customSpeedSetView.selectSpeedCallBack = { [weak self, weak customSpeedSetView] maxSpeed, minSpeed, isJumpSpeedModel, isCancle in
            if !isCancle {
     
                BFLog(message: "自定义速度maxSpeed is\(maxSpeed) minSpeed \(minSpeed) \(isJumpSpeedModel)")
                self?.musicEditBGView.pausePlayer()

                // 自定定义的更新一下最后的选择位置
                if self?.speedSettingView.viewType == 1 {
                    self?.lastSpeedSelectIndex = -1
                    self?.modelSpeed_maxSpeed = maxSpeed
                    self?.modelSpeed_minSpeed = minSpeed
                } else if self?.speedSettingView.viewType == 2 {
                    self?.lastJumpSpeedSelectIndex = -1
                    self?.modelPoint_speed = maxSpeed
                } else {
                    self?.lastCyclesSelectIndex = Int(maxSpeed - 1)
                }

                self?.settingPlayerView()
                // 确认后 选中自定义
                self?.speedSettingView.selectCustom()
            } else {
                // 取消后恢复上一次选择的位置
                if self?.speedSettingView.viewType == 1 {
                    self?.speedSettingView.setSelectItem(index: self?.lastSpeedSelectIndex ?? 0, isSettingPlayer: false)
                } else if self?.speedSettingView.viewType == 2 {
                    self?.speedSettingView.setSelectItem(index: self?.lastJumpSpeedSelectIndex ?? 0, isSettingPlayer: false)
                } else {
                    self?.speedSettingView.setSelectItem(index: self?.lastCyclesSelectIndex ?? 0, isSettingPlayer: false)
                }

                customSpeedSetView?.isHidden = true
            }
        }
        return customSpeedSetView

    }()

    /// 音乐标题
    lazy var musicNameView: UIView = {
        let musicNameView = UIView()
        musicNameView.addSubview(musicNameLab)
        let nameWidth: CGFloat = musicNameLab.frame.width + (25 + cDefaultMargin * 3)
        musicNameView.frame = CGRect(x: (view.frame.width - nameWidth) / 2, y: cDevice_iPhoneStatusBarHei + (cDevice_iPhoneNavBarHei - cDefaultMargin * 3) / 2, width: nameWidth, height: cDefaultMargin * 3)
//        musicNameView.backgroundColor = UIColor.hexColor(hexadecimal: "#333333")
        musicNameView.addCorner(corner: musicNameView.frame.height / 2)
        let musicImageView = UIImageView()
        musicImageView.tintColor = PQBFConfig.shared.styleTitleColor
        musicImageView.image = UIImage.moduleImage(named: "stuckPoint_reCreate_music", moduleName: "BFFramework", isAssets: false)?.withRenderingMode(.alwaysTemplate)
        musicImageView.frame = CGRect(x: musicNameView.frame.height / 2 - 5, y: (musicNameView.frame.height - 22) / 2, width: 22, height: 22)
        musicNameView.addSubview(musicImageView)
        musicNameLab.frame.origin.x = musicImageView.frame.maxX + 5
        return musicNameView
    }()

    /// 音乐歌曲名称
    lazy var musicNameLab: LMJHorizontalScrollText = {
        let nameWidth: CGFloat = sizeWithText(text: "\(stuckPointMusicData?.musicName ?? "")", font: UIFont.systemFont(ofSize: 13), size: CGSize(width: view.frame.width - ((cDefaultMargin * 6 + 16 * 2) * 2) - (25 + cDefaultMargin * 3), height: cDefaultMargin * 3)).width
        let musicNameLab = LMJHorizontalScrollText(frame: CGRect(x: 0, y: 0, width: nameWidth < cDefaultMargin * 4 ? cDefaultMargin * 4 : nameWidth, height: cDefaultMargin * 3))
        musicNameLab.textColor = PQBFConfig.shared.styleTitleColor
        musicNameLab.textFont = UIFont.systemFont(ofSize: 13)
        musicNameLab.speed = 0.03
        musicNameLab.moveDirection = LMJTextScrollMoveLeft
        musicNameLab.moveMode = LMJTextScrollContinuous
        if nameWidth < cDefaultMargin * 4 {
            musicNameLab.text = " \(stuckPointMusicData?.musicName ?? "") "
        } else {
            musicNameLab.text = " \(stuckPointMusicData?.musicName ?? "") "
        }
        return musicNameLab
    }()

    /// 同步进度显示
    lazy var synchroMarskView: PQStuckPointLoadingView = {
        var synchroMarskView = PQStuckPointLoadingView(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
        synchroMarskView.cancelHandle = { [weak self] _ in
            self?.navigationController?.popViewController(animated: true)
        }
        return synchroMarskView
    }()

    @objc func willEnterForeground() {
        BFLog(message: "进入到前台")
        if(self.navigationController?.topViewController == self){
            if projectModel.sData!.sections.count > 0 {
                settingPlayerView()
            }else {
                prepareMeta()
            }
        }
    }
      
    @objc func enterBackground() {
        BFLog(message: "进入到后台")
        // 取消导出
        if(self.navigationController?.topViewController == self){
            playerView.pause()
        }
    }

    override func backBtnClick() {
        super.backBtnClick()
//        playerView.pause()
        // 点击上报：返回按钮
        PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_back, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-（点击上报：返回按钮）")
    }

    // 使用选择音乐 调用情况：1，操作面板直接选择 ，2 搜索界面点击使用
    func userstuckPointMusic(musicData: PQVoiceModel?) {
        // 1，音乐面板点击了使用
        stuckPointMusicData = musicData
        // 2，同步最新音乐数据
        synchroMusicInfoData(resetSelectIndex: false)

        // 3，更新音乐标题UI
        let nameWidth: CGFloat = sizeWithText(text: "\(stuckPointMusicData?.musicName ?? "")", font: UIFont.systemFont(ofSize: 13), size: CGSize(width: (view.frame.width) - ((cDefaultMargin * 6 + 16 * 2) * 2) - (25 + cDefaultMargin * 3), height: cDefaultMargin * 3)).width

        if nameWidth < cDefaultMargin * 4 {
            musicNameLab.text = " \(stuckPointMusicData?.musicName ?? "") "
        } else {
            musicNameLab.text = " \(stuckPointMusicData?.musicName ?? "") "
        }

        // 更新一下节奏的 UI
        sustomSwitchView.selectOneBtn(Index: (stuckPointMusicData?.speed ?? 2))
    }

    // 点击搜索音乐
    @objc func musicSearchBtnClick(sender _: UIButton) {
        let searchVC = PQEditMusicSearchController()

        searchVC.btnClickHandle = { [weak self] _, bgmData in
            // 使用音乐
            BFLog(message: "搜索音乐点击了使用")
            self?.musicEditBGView.insertSearchMusic(model: bgmData as! PQVoiceModel)
            self?.userstuckPointMusic(musicData: bgmData as? PQVoiceModel)
        }

        let navigationController: UINavigationController = UINavigationController(rootViewController: searchVC)
        navigationController.modalPresentationStyle = .fullScreen
        present(navigationController, animated: true, completion: nil)
    }

    // 卡点编辑
    @objc func pointEditerBtnClick(sender: UIButton) {
        if sender.isSelected { return }
        sender.isSelected = !sender.isSelected
        musicEditerBtn.isSelected = false
        pointEditBGView.isHidden = false
        musicEditBGView.isHidden = true

        PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_videoTab, pageSource: .sp_shanyinApp_main, extParams: nil, remindmsg: "")
    }

    // 音乐编辑
    @objc func musicEditerBtnClick(sender: UIButton) {
        if sender.isSelected { return }
        sender.isSelected = !sender.isSelected
        pointEditerBtn.isSelected = false
        pointEditBGView.isHidden = true

        musicEditBGView.showData()

        PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_musicTab, pageSource: .sp_shanyinApp_main, extParams: nil, remindmsg: "")
    }

    @objc func editModelClick1(sender: UIButton) {
        editModelClick(sender: sender)
    }

    // 三种模式修改
    @objc func editModelClick(sender: UIButton, reportLog: Bool = true) {
//        if sender.isSelected {
//            BFLog(message: "已经是选中状态")
//            return  “”
//        }

        self.musicEditBGView.pausePlayer()
        sharedImageProcessingContext.framebufferCache.purgeAllUnassignedFramebuffers()

        if sender == jumpPointBtn && selectedTotalDuration < 6 && selectedDataCount != selectedImageDataCount && reCreateVideoData == nil{
            cShowHUB(superView: view, msg: "素材时长需要大于6秒才\n可选择“跳跃卡点”模式")
            return
        }
        lastEditModelBtn?.isSelected = false
        //设置取消选中的背景色
        lastEditModelBtn?.backgroundColor = PQBFConfig.shared.pointEditNamalBackgroundColor
        sender.isSelected = !sender.isSelected
        lastEditModelBtn = sender
        //设置选中的背景色
        let styleColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
        lastEditModelBtn?.backgroundColor = UIColor(red: styleColor.rgbaf[0], green: styleColor.rgbaf[1], blue: styleColor.rgbaf[2], alpha: 0.15)
        

        BFLog(message: "sender tag is \(sender.tag)")
        // 2素材全是图片的时候三个模式都显示循环设置 UI
        if selectedDataCount == selectedImageDataCount {
            speedSettingView.viewType = 3
            customSpeedSettingView.viewType = speedSettingView.viewType
            speedSettingView.snp.remakeConstraints { make in
                make.left.equalToSuperview().offset(16)
                make.right.equalToSuperview()
                make.top.equalTo(onlyMusicBtn.snp.bottom).offset(10)
                make.height.equalTo(30)
            }

            speedSettingView.isHidden = false
            speedTitleLab.isHidden = false
            sustomSwitchView.isHidden = false
            if lastCyclesSelectIndex != -1 {
                speedSettingView.setSelectItem(index: lastCyclesSelectIndex, isSettingPlayer: false)
            }

        } else {
            // 1 ui 调整
            if sender.tag == 1 || sender.tag == 2 {
                speedSettingView.viewType = sender.tag
                customSpeedSettingView.viewType = speedSettingView.viewType

                speedSettingView.snp.remakeConstraints { make in
                    make.left.equalToSuperview().offset(16)
                    make.right.equalToSuperview()
                    make.top.equalTo(onlyMusicBtn.snp.bottom).offset(10)
                    make.height.equalTo(sender.tag == 1 ? 44 : 30)
                }
                speedSettingView.isHidden = false
                speedTitleLab.isHidden = false
                sustomSwitchView.isHidden = false
                if sender.tag == 1 { // 快慢速
                    speedSettingView.setSelectItem(index: lastSpeedSelectIndex, isSettingPlayer: false, setDisable: (selectedTotalDuration < 6 && selectedDataCount != selectedImageDataCount) ? true : false)
                } else if sender.tag == 2 { // 跳跃卡点
                    speedSettingView.setSelectItem(index: lastJumpSpeedSelectIndex, isSettingPlayer: false)
                }
            } else {
                speedTitleLab.isHidden = true
                speedSettingView.isHidden = true
                sustomSwitchView.isHidden = true
            }
        }

        // 3 设置 btn 不同显示状态
        var speedStuckBtnGifName = ""
        var jumpPointBtnGifName = ""
        if sender.tag == 1 { // 快慢速
            speedStuckBtnGifName = "speedstuck_h_pq"
            jumpPointBtnGifName = "jumpPoint_n_pq"
            currentCreateStickersModel = .createStickersModelSpeed

            if reportLog {
                PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_selectPatternSpeed, pageSource: .sp_shanyinApp_main, extParams: nil, remindmsg: "")
            }

        } else if sender.tag == 2 { // 跳跃卡点
            speedStuckBtnGifName = "speedstuck_n_pq"
            jumpPointBtnGifName = "jumpPoint_h_pq"
            currentCreateStickersModel = .createStickersModelPoint

            if reportLog {
                PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_selectPatternMusicVideo, pageSource: .sp_shanyinApp_main, extParams: nil, remindmsg: "")
            }

        } else if sender.tag == 3 { // 仅音乐
            speedStuckBtnGifName = "speedstuck_n_pq"
            jumpPointBtnGifName = "jumpPoint_n_pq"
            currentCreateStickersModel = .createStickersModelOnlyMusic
            if reportLog {
                PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_selectPatternBgm, pageSource: .sp_shanyinApp_main, extParams: nil, remindmsg: "")
            }
        }
        settingPlayerView()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        lineView?.isHidden = true
        DispatchQueue.main.async {
            UIApplication.shared.isIdleTimerDisabled = true
        }
        musicNameLab.move()

        PQNotification.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
        PQNotification.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        DispatchQueue.main.async {
            UIApplication.shared.isIdleTimerDisabled = false
        }
        musicNameLab.stop()
        playerView.pause()

        musicEditBGView.pausePlayer()
        PQNotification.removeObserver(self)
        
        self.synchroMarskView.removeMarskView()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        leftButton(image: nil, tintColor: PQBFConfig.shared.styleTitleColor)
        navHeadImageView?.addSubview(nextBtn)
        navHeadImageView?.addSubview(musicNameView)

        // 添加子视图
        addSubViews()
        
        prepareMeta()
        
        // 曝光上报：预览页面曝光上报
        PQEventTrackViewModel.baseReportUpload(businessType: .bt_windowView, objectType: .ot_view_previewSyncedUp, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-（曝光上报：预览页面曝光上报）")
        
        //从选择的素材中 第一个素材设置封面
        if selectedPhotoData != nil && selectedPhotoData!.count > 0 {
            let photo = selectedPhotoData!.first!
            let option = PHImageRequestOptions()
            option.isNetworkAccessAllowed = true //允许下载iCloud的图片
            option.resizeMode = .none
            option.deliveryMode = .highQualityFormat

            PHImageManager.default().requestImage(for: photo,
                                                  targetSize: CGSize(width: 1920, height: 1920),
                                                  contentMode: .aspectFit,
                                                  options: option)
            { [weak self] (image, nil) in
                 //image就是图片
                if image != nil {
                    self?.firstFrameImage = image
                }
            }
        }
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        playerView.resetCanvasFrame(frame: coculationPlayViewRect())
    }
    
    func prepareMeta(){
        // 导出相册视频
        exportPhotoData()
        // 同步音乐数据
        synchroMusicInfoData()

        // 插入选择的音乐信息
        musicEditBGView.firstInsertVoiceModel = stuckPointMusicData!
    }

    /// 添加子视图
    /// - Returns: <#description#>
    func addSubViews() {
        if (stuckPointMusicData?.rhythmSdata.count ?? 0) <= 0 {
            return
        }
        view.addSubview(playerView)
        view.addSubview(pointEditBGView)
        view.addSubview(musicEditBGView)
        view.addSubview(optionlineView)

        view.addSubview(pointEditerBtn)
        view.addSubview(musicEditerBtn)
        view.addSubview(customSpeedSettingView)

        // 卡点
        pointEditBGView.addSubview(pointEditRemindLab)
        pointEditBGView.addSubview(speedTitleLab)
        pointEditBGView.addSubview(speedStuckBtn)
        pointEditBGView.addSubview(jumpPointBtn)
        pointEditBGView.addSubview(onlyMusicBtn)
        pointEditBGView.addSubview(speedSettingView)
        pointEditBGView.addSubview(sustomSwitchView)

        // 音乐
        musicEditBGView.addSubview(stuckPointCuttingView)

        pointEditerBtn.snp.makeConstraints { make in
            make.left.equalToSuperview().offset(100)
            make.bottom.equalToSuperview().offset(-(14 + cAKSafeAreaHeight))
            make.height.equalTo(24)
            make.width.equalTo(24)
        }

        musicEditerBtn.snp.makeConstraints { make in
            make.right.equalToSuperview().offset(-100)
            make.top.equalTo(pointEditerBtn.snp.top)
            make.height.equalTo(24)
            make.width.equalTo(24)
        }
        pointEditBGView.snp.makeConstraints { make in
            make.left.right.equalTo(view)
            make.bottom.equalTo(pointEditerBtn.snp.top).offset(-7)
            make.height.equalTo(290)
        }
        musicEditBGView.snp.makeConstraints { make in
            make.left.right.equalToSuperview()
            make.bottom.equalTo(pointEditerBtn.snp.top).offset(-7)
            make.height.equalTo(290)
        }
        
        optionlineView.snp.makeConstraints { make in
            make.left.right.equalToSuperview()
            make.bottom.equalTo(pointEditBGView.snp.bottom).offset(-1)
            make.height.equalTo(1)
        }

        stuckPointCuttingView.snp.makeConstraints { make in
            make.left.right.equalToSuperview()
            make.bottom.equalTo(musicEditBGView.snp.bottom).offset(-1)
            make.height.equalTo(85)
        }
        pointEditRemindLab.snp.makeConstraints { make in
            make.left.equalToSuperview().offset(16)
            make.top.equalToSuperview().offset(16)
            make.height.equalTo(20)
            make.width.equalTo(80)
        }
        speedStuckBtn.snp.makeConstraints { make in
            make.left.equalToSuperview().offset(16)
            make.top.equalTo(pointEditRemindLab.snp.bottom).offset(8)
            make.height.equalTo(80)
            make.width.equalTo(80)
        }
 
        jumpPointBtn.snp.makeConstraints { make in
            make.left.equalTo(speedStuckBtn.snp.right).offset(10)
            make.top.equalTo(speedStuckBtn.snp.top)
            make.height.equalTo(80)
            make.width.equalTo(80)
        }
 
        onlyMusicBtn.snp.makeConstraints { make in
            make.left.equalTo(jumpPointBtn.snp.right).offset(10)
            make.top.equalTo(speedStuckBtn.snp.top)
            make.height.equalTo(80)
            make.width.equalTo(64)
        }

        //重新设置三个模式 btn 图片和title的位置
        speedStuckBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(speedStuckBtn.imageView?.frame.size.width ?? 0)  , bottom: -(speedStuckBtn.imageView?.frame.size.height ?? 0), right: 0);
        speedStuckBtn.imageEdgeInsets = UIEdgeInsets(top: -(speedStuckBtn.titleLabel?.intrinsicContentSize.height ?? 0), left: 0, bottom: 0, right: -(speedStuckBtn.titleLabel?.intrinsicContentSize.width ?? 0));

        jumpPointBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(jumpPointBtn.imageView?.frame.size.width ?? 0)  , bottom: -(jumpPointBtn.imageView?.frame.size.height ?? 0), right: 0);
        jumpPointBtn.imageEdgeInsets = UIEdgeInsets(top: -(jumpPointBtn.titleLabel?.intrinsicContentSize.height ?? 0), left: 0, bottom: 0, right: -(jumpPointBtn.titleLabel?.intrinsicContentSize.width ?? 0));

   
        onlyMusicBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(onlyMusicBtn.imageView?.frame.size.width ?? 0)  , bottom: -(onlyMusicBtn.imageView?.frame.size.height ?? 0), right: 0);
        onlyMusicBtn.imageEdgeInsets = UIEdgeInsets(top: -(onlyMusicBtn.titleLabel?.intrinsicContentSize.height ?? 0), left: 0, bottom: 0, right: -(onlyMusicBtn.titleLabel?.intrinsicContentSize.width ?? 0));

        speedSettingView.snp.makeConstraints { make in
            make.left.equalToSuperview().offset(16)
            make.right.equalToSuperview()
            make.top.equalTo(onlyMusicBtn.snp.bottom).offset(10)
            make.height.equalTo(44)
        }
        speedTitleLab.snp.makeConstraints { make in
            make.left.equalToSuperview().offset(16)
            make.top.equalToSuperview().offset(190)
            make.height.equalTo(20)
            make.width.equalTo(80)
        }
        sustomSwitchView.snp.makeConstraints { make in
            make.left.equalToSuperview().offset(16)
            make.top.equalTo(speedTitleLab.snp.bottom).offset(8)
            make.height.equalTo(30)
            make.width.equalTo(180)
        }
    }

    @objc func nextBtnClick(sender _: UIButton) {
        BFLog(message: "去发布")
        playerView.pause()
        // 使用深 copy
        let json = projectModel.toJSONString(prettyPrint: false)
        
        if json == nil {
            BFLog(message: "数据转换有问题 跳转")
            return
        }
        let tempModel: PQEditProjectModel? = Mapper<PQEditProjectModel>().map(JSONString: json!)
        let materialVC: PQStuckPointMaterialController? = navigationController?.viewControllers.first(where: { (vc) -> Bool in
            vc is PQStuckPointMaterialController
        }) as? PQStuckPointMaterialController

        if materialVC != nil, materialVC?.isToPublicHandle != nil {
            materialVC?.isToPublicHandle!(isReCreate, selectedTotalDuration, selectedDataCount, selectedImageDataCount, mStickers, stuckPointMusicData, tempModel,currentCreateStickersModel,modelSpeed_maxSpeed,modelSpeed_minSpeed,Float(finallyStuckPoints.last ?? 0) - Float(finallyStuckPoints.first ?? 0),getClipAudioRange(),playeTimeRange)
        } else {
            if finallyStuckPoints.count == 0 {
                cShowHUB(superView: nil, msg: "无卡点信息，返回重新选择音乐")
                return
            }
            let videoExporter = PQStuckPointPublicController()
            videoExporter.rhythmMode = currentCreateStickersModel
            videoExporter.syncedUpVideoSpeedMin = modelSpeed_maxSpeed
            videoExporter.syncedUpVideoSpeedMax = modelSpeed_minSpeed
            videoExporter.isReCreate = isReCreate
            videoExporter.selectedTotalDuration = selectedTotalDuration
            videoExporter.selectedDataCount = selectedDataCount
            videoExporter.selectedImageDataCount = selectedImageDataCount
            videoExporter.finallyUserAudioTime =  Float(finallyStuckPoints.last ?? 0) - Float(finallyStuckPoints.first ?? 0)
            videoExporter.clipAudioRange = getClipAudioRange()
            videoExporter.playeTimeRange = playeTimeRange
            videoExporter.mStickers = mStickers
            videoExporter.audioMixModel = stuckPointMusicData
            videoExporter.editProjectModel = tempModel
            navigationController?.pushViewController(videoExporter, animated: true)
        }
        // 点击上报：去合成
        PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_commit, pageSource: .sp_stuck_previewSyncedUp, extParams: ["musicName": stuckPointMusicData?.musicName ?? "", "musicId": stuckPointMusicData?.musicId ?? "", "rhythmNumber": stuckPointMusicData?.speed ?? 2, "duration": ((stuckPointMusicData?.endTime ?? 0) - (stuckPointMusicData?.startTime ?? 0)) * 1000], remindmsg: "点击上报：去合成")
    }
        
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        if touches.first?.view != self.customSpeedSettingView {
            if !customSpeedSettingView.isHidden {
                customSpeedSettingView.isHidden = true
            }
        }
    }

    // MARK: - 播放器相关操作

    /// seek 播放器
    /// - Parameter playeTimeRange: 开始和结束时间
    func seekPlayer(playeTimeRange: CMTimeRange) {
        playerView.setEnableSeek(isSeek: true)
        playerView.play(pauseFirstFrame: false, playeTimeRange: playeTimeRange)
    }

    /// 通过传入的    selectedPhotoData 、 stuckPointMusicData 创建 projectModel 模型 后面都使用 projectModel 参数
    func createPorjectData() {
        // 1,添加选择的视觉素材
        if projectModel.sData?.sections.count == 0 {
            let section: PQEditSectionModel = PQEditSectionModel()
            selectedMetarialData?.forEach { model in

                let json = model.toJSONString(prettyPrint: false)
                if json == nil {
                    BFLog(message: "数据转换有问题 跳转")
                    return
                }
                let tempModel: PQEditVisionTrackMaterialsModel = Mapper<PQEditVisionTrackMaterialsModel>().map(JSONString: json!)!
                section.sectionTimeline?.visionTrack?.visionTrackMaterials.append(tempModel)
            }
            projectModel.sData?.sections.append(section)
        }

        // 2,添加背景音乐
        projectModel.sData?.addBGM(audioMix: stuckPointMusicData!)
    }

    // 计算拼接音乐的开始和结束点
    func getClipAudioRange() -> CMTimeRange {
        // 设置音乐的拼接范围，开始：推荐的卡点  结束：点到的倒数第二位
        if stuckPointMusicData!.rhythmSdata.count > 0 && stuckPointMusicData?.rhythmSdata[0].pointTimes.count ?? 0 > 2 {
            let lastSecondPoint = Float((stuckPointMusicData!.rhythmSdata[0].pointTimes[stuckPointMusicData!.rhythmSdata[0].pointTimes.count - 2])) / Float(BASE_FILTER_TIMESCALE)
            let clipAudioRange =
                CMTimeRange(start: CMTime(value: CMTimeValue(Float(stuckPointMusicData?.startTime ?? 0) * Float(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE), end: CMTime(value: CMTimeValue((Float(lastSecondPoint)) * Float(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE))
            
            return clipAudioRange
        }
        return CMTimeRange(start: CMTime(value: 0, timescale: 1), duration: CMTime(value: 1, timescale: 1))
    }

    // 设置播放器
    func coculationPlayViewRect() -> CGRect {
        let playerShowHeight = pointEditBGView.frame.minY - (navHeadImageView?.frame.maxY ?? 0)
        var showRect: CGRect = CGRect(x: (cScreenWidth - playerShowHeight) / 2, y: 0, width: playerShowHeight, height: playerShowHeight)
        if self.firstFrameImage != nil {
            let w = self.firstFrameImage!.size.width
            let h = self.firstFrameImage!.size.height
            let ratioMaterial: Float = Float(w / max(h, 1))
            if ratioMaterial > 1 {
                // 横屏
                let tempPlayerHeight = min(cScreenWidth * CGFloat(h / w), playerShowHeight)
                showRect = CGRect(x: (cScreenWidth - tempPlayerHeight * CGFloat(ratioMaterial)) / 2, y: (playerShowHeight - tempPlayerHeight) / 2, width: tempPlayerHeight * CGFloat(ratioMaterial), height: tempPlayerHeight)
            } else {
                // 竖屏
                let playerViewWidth = (CGFloat(w) / CGFloat(h)) * playerShowHeight
                showRect = CGRect(x: (cScreenWidth - playerViewWidth) / 2, y: 0, width: playerViewWidth, height: playerShowHeight)
            }
        }
        if showRect.size.width == showRect.size.height {
            if cScreenWidth > playerShowHeight {
                showRect.origin.x = (cScreenWidth - playerShowHeight) / 2
                showRect.size.width = playerShowHeight
                showRect.size.height = playerShowHeight
            } else {
                showRect.origin.x = 0
                showRect.size.width = cScreenWidth
                showRect.size.height = cScreenWidth
            }
        }
     
        if showRect.size.width != 0, showRect.size.height != 0 {
            if(Int(showRect.height) % 2 != 0){
                showRect.size.height  = showRect.size.height + 1.0
            }
            if(Int(showRect.width) % 2 != 0){
                showRect.size.width  =  showRect.size.width + 1.0
            }
 
        }
        showRect.origin.y = (playerShowHeight - showRect.size.height) / 2.0 + (navHeadImageView?.frame.maxY ?? 0)
        
        return showRect
    }
    
    func settingPlayerView() {
        stuckPointCuttingView.resetDefaultsColor()
        synchroMarskView.show()
        // 1,设置播放器的显示区域 和画布大小
        //  - 按第一个素材尺寸自适应
        playerView.pause()
        
        var firstModel: PQEditVisionTrackMaterialsModel?
        for part in projectModel.sData!.sections {
            if part.sectionTimeline?.visionTrack?.getEnableVisionTrackMaterials().count ?? 0 > 0 {
                firstModel = part.sectionTimeline?.visionTrack?.getEnableVisionTrackMaterials().first
                break
            }
        }

        var videoSize: CGSize = CGSize(width: Int(firstModel?.width ?? 0), height: Int(firstModel?.height ?? 0))

        var minSlider = min(videoSize.width, videoSize.height)
        var maxSlider = max(videoSize.width, videoSize.height)
        let ration = 1080 / minSlider
        minSlider = minSlider * ration
        maxSlider = maxSlider * ration
        if videoSize.width > videoSize.height { // 宽屏
            videoSize = CGSize(width: maxSlider, height: minSlider)
        } else {
            videoSize = CGSize(width: minSlider, height: maxSlider)
        }
        if videoSize.width.isNaN || videoSize.height.isNaN {
            BFLog(1, message: "宽高无效NaN")
            return
        }

        let maxValue = max(videoSize.width, videoSize.height ?? 0)

        if maxValue > 1920 {
            let maxRation = 1920 / maxValue

            videoSize = CGSize(width: videoSize.width * CGFloat(maxRation), height: videoSize.height * CGFloat(maxRation))

            BFLog(message: "最长边已经超过 1920 要等比缩小 缩放后\(videoSize)")
        }

        if (Int(videoSize.width) % 2) != 0 {
            videoSize.width = videoSize.width - 1
        }
        if (Int(videoSize.height) % 2) != 0 {
            videoSize.height = videoSize.height - 1
        }

        projectModel.sData?.videoMetaData?.videoWidth = Int(videoSize.width)
        projectModel.sData?.videoMetaData?.videoHeight = Int(videoSize.height)

        let beginTime = Date()
        dealParameter(model: currentCreateStickersModel)

        // 更新裁剪时间条的的ui数据
        stuckPointCuttingView.videoDuration = max(CGFloat(finallyUserAudioTime), CGFloat(finallyStuckPoints.last!))
        let counn = (stuckPointMusicData?.rhythmSdata[0].pointTimes.count)! - 2
        let suggestRhythmStartTime = CGFloat(stuckPointMusicData?.suggestRhythmStartTime ?? 0)
        let suggestRhythmEndTime = max(suggestRhythmStartTime, CGFloat(stuckPointMusicData?.rhythmSdata[0].pointTimes[max(counn, 0)] ?? 0)/CGFloat(BASE_FILTER_TIMESCALE))
        stuckPointCuttingView.updateEndTime(
            startTime: CGFloat(CMTimeGetSeconds(playeTimeRange.start)),
            endTime: CGFloat(CMTimeGetSeconds(playeTimeRange.end)),
            suggestRhythmStartTime: suggestRhythmStartTime,
            suggestRhythmEndTime: suggestRhythmEndTime)

        // 2,创建滤镜
        DispatchQueue.global().async {
            self.mStickers = self.createStickers(sections: self.projectModel.sData?.sections ?? List(), inputSize: CGSize(width: CGFloat(self.projectModel.sData?.videoMetaData?.videoWidth ?? 0), height: CGFloat(self.projectModel.sData?.videoMetaData?.videoHeight ?? 0)), model: self.currentCreateStickersModel)
            DispatchQueue.main.async { // 串行、异步
                self.playerView.mStickers = self.mStickers

                BFLog(message: "createStickers tiskskskskme  \(Date().timeIntervalSince(beginTime))")

                // 3,设置音频
                let audioPath = self.stuckPointMusicData?.localPath ?? ""

                BFLog(message: "初始化音频播放器的音频地址为:\(audioPath)")
                self.playerView.stop()
                // 这里的测试这个音乐播放有问题
                //        self.playerView.updateAsset(URL(fileURLWithPath: "63930549652d74e477141e3b79c8d29a9ef8af81625053214516.mp3", relativeTo:Bundle.main.resourceURL!), videoComposition: nil, audioMixModel: nil)

                self.playerView.updateAsset(URL(fileURLWithPath: documensDirectory + audioPath), videoComposition: nil, audioMixModel: nil, originMusicDuration: self.finallyUserAudioTime, clipAudioRange: self.getClipAudioRange())

                // 4, 设置播放器的输出画布大小
                self.playerView.movie?.mShowVidoSize = CGSize(width: CGFloat(self.projectModel.sData?.videoMetaData?.videoWidth ?? 0), height: CGFloat(self.projectModel.sData?.videoMetaData?.videoHeight ?? 0))
                
                
                // 传给movie 音频的原始卡点
                let fir = Int64(self.stuckPointsTemp.first ?? 0)
                let endd = Int64(self.stuckPointsTemp.last ?? 0)
                self.playerView.movie?.orginStuckRange = CMTimeRange(start: CMTime(value: Int64(fir) * Int64(BASE_FILTER_TIMESCALE), timescale: BASE_FILTER_TIMESCALE), end: CMTime(value: Int64(endd) * Int64(BASE_FILTER_TIMESCALE), timescale: BASE_FILTER_TIMESCALE))

                // 5,开始播放
                self.playerView.isLoop = false
                self.playerView.showProgressLab = true

                // 初始化音频的开始和结束时间
                BFLog(message: "播放的器 开始\(String(describing: CMTimeGetSeconds(self.playeTimeRange.start))) 结束 \(String(describing: CMTimeGetSeconds(self.playeTimeRange.end)))")
                let end3: TimeInterval = Date().timeIntervalSince1970

                self.playerView.play(pauseFirstFrame: false, playeTimeRange: CMTimeRange(start: self.playeTimeRange.start, end: self.playeTimeRange.end))
                self.stuckPointCuttingView.updateProgress(progress: 0)
                self.synchroMarskView.removeMarskView()

                let end4: TimeInterval = Date().timeIntervalSince1970
                BFLog(message: " playerView.play tiskskskskme  \(end4 - end3)")

                // 6,进度回调
                self.playerView.progress = { [weak self] currentTime, tatolTime, percent in
                    if percent == 1 {
                        self?.stuckPointCuttingView.resetDefaultsColor(clearData: false)
                        sharedImageProcessingContext.framebufferCache.purgeAllUnassignedFramebuffers()
                        return
                    }
                    if(CMTimeGetSeconds(self?.playeTimeRange.duration ?? .zero) <= 0.0){
                        BFLog(message: "时长错误！！！！")
                        return
                    }
                    // 更新进度
                    let progress = (currentTime - CMTimeGetSeconds(self?.playeTimeRange.start ?? .zero)) / CMTimeGetSeconds(self?.playeTimeRange.duration ?? .zero)
                    BFLog(message: "\(currentTime) \(tatolTime) 显示播放器进度为: \(progress)")

                    self?.stuckPointCuttingView.updateProgress(progress: CGFloat(progress))

                }
            }
        }
    }

    deinit {
        musicNameLab.stop()
//        playerView.pause()
        // 取消所有的导出
        PQSingletoMemoryUtil.shared.allExportSession.forEach { _, exportSession in
            exportSession.cancelExport()
        }
        self.synchroMarskView.removeMarskView()
        sharedImageProcessingContext.framebufferCache.purgeAllUnassignedFramebuffers()
        BFLog(1, message: "卡点视频预览界面release")
    }
}

// MARK: - 视频渲染相关逻辑方法

extension PQStuckPointEditerController {
    /// 分割视频 这里只设置视频类型的 in 和 out 并不设置显示的开始和结束时间   mp4 ,png ,png ,mp4
    /// - Parameter section: 当前段
    /// - Parameter stuckPoints: 用户选择的，或推荐的卡点数
    /// - Returns: 返回分割后的所有 stickers 和卡点数是一致的
    func clipVideoMerage(section: PQEditSectionModel, stuckPoints: [Float]) -> [PQEditVisionTrackMaterialsModel] {
        var stickers: Array = Array<PQEditVisionTrackMaterialsModel>.init()
        // 第二种情况：有视频要进行分割
        /*
         1, 确定每个视频素材需要切的段数p
         2, 将所有视频时长相加，得到总视频素材时长L = l1 + l2 + ... + ln
         3, 视频素材a1需要切分的个数clipNum = max (round (kongduan * a1 / L) , 1)
         */
        // 要补的空位数
        let kongduan: Int = Int(stuckPoints.count - 1) - selectedImageDataCount

        // 所有视频总时长
        var videoTotalDuration: Float64 = 0.0
        for video in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "video") {
            let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + video.locationPath), options: nil)
            videoTotalDuration = videoTotalDuration + Float64(CMTimeGetSeconds(asset.duration))
        }
        if videoTotalDuration == 0 {
            BFLog(message: "视频总时长出现错误！！！！这里应该有视频素材的")
            return stickers
        }
        for sticker in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials() {
            if sticker.type == StickerType.VIDEO.rawValue {
                let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + sticker.locationPath), options: nil)
                // 要分割的段落
                let clipNum = Int(max(round(Double(kongduan) * CMTimeGetSeconds(asset.duration) / videoTotalDuration), 1))
                sticker.duration = CMTimeGetSeconds(asset.duration)
                BFLog(message: "单个视频\(sticker.locationPath)时长：:\(CMTimeGetSeconds(asset.duration)) ,clipNum is:\(clipNum)")
                for clipindex in 0 ..< clipNum {
                    // deep copy sticker model 防止只有一个对象
                    let deepCopySticker:PQEditVisionTrackMaterialsModel? = sticker.copy() as? PQEditVisionTrackMaterialsModel
                              
                    // 设置循环模式和适配模式
                    deepCopySticker?.generateDefaultValues()

                    deepCopySticker?.model_in = clipindex == 0 ? 0 : CMTimeGetSeconds(asset.duration) / Double(clipNum) * Double(clipindex)
                    deepCopySticker?.out = (deepCopySticker?.model_in ?? 0) + CMTimeGetSeconds(asset.duration) / Double(clipNum)

                    if (deepCopySticker?.model_in ?? 0) >= CMTimeGetSeconds(asset.duration) || (deepCopySticker?.out ?? 0) >= CMTimeGetSeconds(asset.duration) {
                        deepCopySticker?.model_in = CMTimeGetSeconds(asset.duration) - CMTimeGetSeconds(asset.duration) / Double(clipNum)

                        deepCopySticker?.out = CMTimeGetSeconds(asset.duration)
                    }

                    BFLog(message: " crilp is in \(deepCopySticker?.model_in ?? 0) out \(deepCopySticker?.out ?? 0) 总时长\(CMTimeGetSeconds(asset.duration))")

                    if deepCopySticker != nil {
                        stickers.append(deepCopySticker!)
                    }
                }
            } else if sticker.type == StickerType.IMAGE.rawValue {
                sticker.generateDefaultValues()
                stickers.append(sticker)
            }
        }
//        kongduan = clipNumTep
        return stickers
    }
    
    //更新 playeTimeRange & finallyUserAudioTime
    func updateTimeInfomation(){
        // 四，背景音乐时长处理）计算最后使用的音频时长, 如果不用拼接音频时长度是卡点的倒数第二位时间
        let asset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + (stuckPointMusicData?.localPath ?? "")), options: nil)

        // 原推荐卡点的倒数第二位时间
        let lastSecondPoint = Float((stuckPointMusicData!.rhythmSdata[0].pointTimes[stuckPointMusicData!.rhythmSdata[0].pointTimes.count - 2])) / Float(BASE_FILTER_TIMESCALE)

        finallyUserAudioTime = Float(lastSecondPoint)
        if (finallyStuckPoints.last ?? 0) > Float(CMTimeGetSeconds(asset.duration)) {
            finallyUserAudioTime = Float(finallyStuckPoints.last ?? 0) + (Float(CMTimeGetSeconds(asset.duration)) - Float(lastSecondPoint))
        }

        playeTimeRange = CMTimeRange(start: CMTime(value: CMTimeValue(Float64(finallyStuckPoints.first ?? 0) * Float64(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE), end: CMTime(value: CMTimeValue((Float64(finallyStuckPoints.last ?? 0)) * Float64(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE))

        for (index, usePoint) in finallyStuckPoints.enumerated() {
            BFLog(message: "测试人员最后使用的卡点信息 \(index) : \(usePoint)")
        }

        BFLog(message: "计算后给播放器使用的开始：\(CMTimeGetSeconds(playeTimeRange.start)) 结束时间\(CMTimeGetSeconds(playeTimeRange.end)) 播放总时长:\(CMTimeGetSeconds(playeTimeRange.end) - CMTimeGetSeconds(playeTimeRange.start))")
    }

    /// 创建sticker
    /// - Parameters:
    ///   - sections: 项目所有段落数据信息
    ///   - inputSize: 画布大小
    /// - Returns: filters 数据 播放器可直接使用
    func createStickers(sections: List<PQEditSectionModel>, inputSize _: CGSize = .zero, model: createStickersModel = .createStickersModelPoint) -> [PQEditVisionTrackMaterialsModel] {
        // 推荐卡点数
        let beginDecoderTime: TimeInterval = Date().timeIntervalSince1970

        // 保存滤镜对象数据
        var stickers: Array = Array<PQEditVisionTrackMaterialsModel>.init()
        for section in sections {
            if section.sectionType == "normal" {
                // 第一种情况：全是图片，三种模式都进行图片回环播放
                if section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "video").count == 0, section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "image").count > 0 {
                    for (index, _) in finallyStuckPoints.enumerated() {
                        let sticker: PQEditVisionTrackMaterialsModel = section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials()[index % section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().count]
                        BFLog(message: "stickerlocationPath sticker : \(sticker.locationPath)")
//
                        let deepCopySticker:PQEditVisionTrackMaterialsModel? = sticker.copy() as? PQEditVisionTrackMaterialsModel
                        
                        if deepCopySticker!.type == StickerType.IMAGE.rawValue {
                            if index + 1 < finallyStuckPoints.count {
                             
                                deepCopySticker!.timelineIn = Float64(String(format: "%.6f",finallyStuckPoints[index])) ?? 0.0
                          
                                deepCopySticker!.timelineOut = Float64(String(format: "%.6f",finallyStuckPoints[index + 1])) ?? 0.0
                                if deepCopySticker != nil {
                                    deepCopySticker?.generateDefaultValues()
                                    stickers.append(deepCopySticker!)
                                    
                                    BFLog(1, message: "测试人员index is 纯图片 timelineOut：\(deepCopySticker!.timelineIn) timelineOut :\(deepCopySticker!.timelineOut)")

                                }
                            }
                        }
                    }
                } else { // 第二种情况：视频 + 图片情况、视频要分段+图片按卡点时长创建
                    if model == .createStickersModelPoint { // 跳跃卡点
                        // 第二种情况：有视频要进行分割
                        let clipFilters = clipVideoMerage(section: section, stuckPoints: finallyStuckPoints)
                        //数据不一致时要对数据进行二次处理，不是最好方案
                        if(clipFilters.count > finallyStuckPoints.count - 1){
                            clipPoint(clipNum: clipFilters.count - finallyStuckPoints.count, oldPoints: finallyStuckPoints)

                        }else if(clipFilters.count < finallyStuckPoints.count - 1){
                            
                            while (clipFilters.count < finallyStuckPoints.count - 1) {
                                finallyStuckPoints.removeLast()
                            }
                            
                        }
                        //更新最终使用值
                        updateTimeInfomation()
                        //stikcer段数比clipFilters 数 大于 1才是正确的
                        BFLog(message: "stikcer count is\(clipFilters.count) finallyStuckPoints count is\(finallyStuckPoints.count)")
                        for (index, point) in finallyStuckPoints.enumerated() {
                            BFLog(message: "aaaaaindexindeindexxindexindexindex \(index) \(point)")
                            if index + 1 < finallyStuckPoints.count, index < clipFilters.count {
                                BFLog(message: "bbbbbindexindeindexxindexindexindex \(index) \(point)")
                                let sticker: PQEditVisionTrackMaterialsModel = clipFilters[index]
                                
                                if(sticker.type == StickerType.IMAGE.rawValue){
                                    BFLog(message: "当前是image filter !!!!!")
                                }
                                sticker.timelineIn = Float64(String(format: "%.6f",finallyStuckPoints[index])) ?? 0.0

                                sticker.timelineOut = Float64(String(format: "%.6f",finallyStuckPoints[index + 1])) ?? 0.0
                                // 卡点的时间 >  in out 值 这里就会出现鬼畜效果
                                let timelineInterval = sticker.timelineOut - sticker.timelineIn
                                let inOutInterval = sticker.out - sticker.model_in
                                if timelineInterval > inOutInterval {
                                    BFLog(message: "实际要显示卡点时长\(timelineInterval) 素材裁剪时长：\(inOutInterval)")
                                    sticker.out = sticker.model_in + timelineInterval

                                    // 下面只是 LOG 方便查问题
                                    let stickerInOut = sticker.out - sticker.model_in
                                    let stickerTimelineInOut = sticker.timelineOut - sticker.timelineIn
                                    if stickerInOut != stickerTimelineInOut {
                                        BFLog(message: "sticker.timelineIn \(sticker.timelineIn) stickerTimelineInOut is\(stickerTimelineInOut) stickerInOut is\(stickerInOut) 相差\(stickerTimelineInOut - stickerInOut)")
                                    }
                                }

                                // out > 素材的总时长in out 进行前移操作
                                let offsetAssetDuration = sticker.out - sticker.duration
                                if offsetAssetDuration > 0 {
                                    sticker.model_in = sticker.model_in - offsetAssetDuration
                                    sticker.out = sticker.out - offsetAssetDuration
                                }
                                print("跳跃卡点测试人员index is \(index)分割后 创建 filter timelineIn :\(sticker.timelineIn) timelineOut :\(sticker.timelineOut)  in :\(sticker.model_in) out:\(sticker.out) type is \(sticker.type) 显示总时长为：\(sticker.timelineOut - sticker.timelineIn)  裁剪总时长\(sticker.out - sticker.model_in)")
                            

                                stickers.append(sticker)
                            }
                        }
                    } else if model == .createStickersModelOnlyMusic || model == .createStickersModelSpeed { // 仅音乐 和 快慢速卡点
                        BFLog(message: "stuckPoints count is \(finallyStuckPoints.count)")

 
                        for sticker in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials() {
                            if sticker.type == StickerType.VIDEO.rawValue {
                                let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + sticker.locationPath), options: nil)
                                BFLog(message: "单个视频\(sticker.locationPath)时长：:\(CMTimeGetSeconds(asset.duration)) ,clipNum is:\(sticker.clipCount)")
                                var lastOutTime: Float64 = 0.0
                                for _ in 1 ... sticker.clipCount {
                                    // deep copy sticker model 防止只有一个对象
                                    let deepCopyStickerDecoderTime: TimeInterval = Date().timeIntervalSince1970
                                    
                                    let deepCopySticker:PQEditVisionTrackMaterialsModel? = sticker.copy() as? PQEditVisionTrackMaterialsModel

                                    BFLog(message: "生成stickers 总时长为 aaa\(Date().timeIntervalSince1970 - deepCopyStickerDecoderTime)")
                                    // 设置循环模式和适配模式
                                    deepCopySticker?.generateDefaultValues()
                                    deepCopySticker?.materialDurationFit?.fitType = adapterMode.staticFrame.rawValue
                                    // 当前分段的速度
                                    var tempSpeed: Float = 1.0
                                    if model == .createStickersModelSpeed {
                                        
                                        tempSpeed = (stickers.count % 2) == 0 ? modelSpeed_maxSpeed : modelSpeed_minSpeed
                                    }

                                    if stickers.count + 1 < finallyStuckPoints.count {
                                        deepCopySticker?.speedRate = tempSpeed
                                        // 定义临时使用的变量
                                        // 素材显示的开始时间和结束时间
                                        let tempTimelineIn:Float64 = Float64(String(format: "%.6f",finallyStuckPoints[stickers.count])) ?? 0.0
                                        
                                        let timelineOut:Float64 = Float64(String(format: "%.6f",finallyStuckPoints[stickers.count + 1])) ?? 0.0

                                        // 素材分割的开始时间和结束时间
                                        let tempModel_In = lastOutTime
                                        var tempOut = lastOutTime + Float64(tempSpeed) * (timelineOut - tempTimelineIn)

                                        // 处理最后一点视频素材不够卡点时长 e.g. 0.3 卡点时长0.5
                                        if tempOut > CMTimeGetSeconds(asset.duration) {
                                            // 最后一点素材时长
                                            let lastAssetDuration = CMTimeGetSeconds(asset.duration) - lastOutTime
                                            let pointDuration = timelineOut - tempTimelineIn
                                            // 要适应到卡点内要使用的C速度
                                            let needSpeed = lastAssetDuration / pointDuration
                                            // 当前卡点段为快速 快速都用 C 速处理
                                            BFLog(message: "最后一点视频素材不够卡点时长要做变速C处理 差\(tempOut - CMTimeGetSeconds(asset.duration)) \(needSpeed)")
                                            deepCopySticker?.speedRate = Float(needSpeed)
                                            tempOut = CMTimeGetSeconds(asset.duration)
                                            if(needSpeed == 0){
                                                BFLog(message: "needSpeed is 0 出现在时长和卡点正好相等")
                                                continue
                                            }
                                        }

                                        deepCopySticker?.model_in = tempModel_In
                                        deepCopySticker?.out = tempOut

                                        deepCopySticker?.timelineIn = tempTimelineIn
                                        deepCopySticker?.timelineOut = timelineOut

                                        lastOutTime = deepCopySticker?.out ?? 0
                                    }
                                    BFLog(message: "测试人员创建 sticker  crilp is in 视频 \(String(format: "%.6f", deepCopySticker?.model_in ?? 0))  out  \(String(format: "%.6f", deepCopySticker?.out ?? 0)) ,分段素材时长：\(String(format: "%.6f", (deepCopySticker?.out ?? 0) - (deepCopySticker?.model_in ?? 0))) ,分段显示时长：\(String(format: "%.6f", (deepCopySticker?.timelineOut ?? 0) - (deepCopySticker?.timelineIn ?? 0))), 视频素材原时长\(CMTimeGetSeconds(asset.duration)) clipNum \(sticker.clipCount) timelineIN: \(String(format: "%.6f", deepCopySticker?.timelineIn ?? 0)) timelineOUT:\(String(format: "%.6f", deepCopySticker?.timelineOut ?? 0)) speedRate:\(deepCopySticker?.speedRate ?? 0.0)")
                                    
                                    if deepCopySticker != nil {
                                        if(deepCopySticker?.timelineIn == 0){
                                            BFLog(message: "timelineIn data is error!!!")
                                        }
                                        stickers.append(deepCopySticker!)
                                 
                                    }
                                }
                            } else if sticker.type == StickerType.IMAGE.rawValue {
                                if stickers.count + 1 >= finallyStuckPoints.count {
                                    BFLog(message: "数据出现错误！！！查正") // 155.318253
                                    break
                                }
                                sticker.generateDefaultValues()
                            
                                sticker.timelineIn = Float64(String(format: "%.6f",finallyStuckPoints[stickers.count])) ?? 0.0
                                sticker.timelineOut = Float64(String(format: "%.6f",finallyStuckPoints[stickers.count + 1])) ?? 0.0
                                stickers.append(sticker)
                                BFLog(message: "测试人员创建 sticker  crilp is in 图片 \(String(format: "%.6f", sticker.model_in))  out  \(String(format: "%.6f", sticker.out)) ,分段素材时长：\(String(format: "%.6f", (sticker.out) - (sticker.model_in))) ,分段显示时长：\(String(format: "%.6f", (sticker.timelineOut) - (sticker.timelineIn))),   timelineIN: \(String(format: "%.6f", sticker.timelineIn)) timelineOUT:\(String(format: "%.6f", sticker.timelineOut)) speedRate:\(sticker.speedRate)")
                            }
                        }
                    }
                }
            }
        }

        BFLog(message: "生成stickers 总时长为：\(Date().timeIntervalSince1970 - beginDecoderTime)")
        return stickers
    }

    /// 根据档位取最后使用的卡点数据
    /// - Parameter seed: 档位速度
    /// - Returns: 最后使用的卡点
    func getUsedStuckPoint(seed: Int) -> Array<Float> {
        if !(stuckPointMusicData!.rhythmSdata.count > 0 && stuckPointMusicData!.rhythmSdata[0].pointTimes.count > 1){
            return []
        }
        
        // 推荐卡点数
        var stuckPoints: Array = Array<Float>.init()

        var pointsTemp = Array<Float>.init()
        //
        // 最后一个卡点时间(原推荐卡点的倒数第二位时间)
        let lastPoint = stuckPointMusicData!.rhythmSdata[0].pointTimes[stuckPointMusicData!.rhythmSdata[0].pointTimes.count - 2]

        for (index, dunshu) in stuckPointMusicData!.rhythmSdata[0].pointTimes.enumerated() {
            if dunshu >= Int64((stuckPointMusicData?.startTime ?? 0) * Float64(BASE_FILTER_TIMESCALE)) && dunshu < lastPoint {
                let savePointStr = String(format: "%.6f", Float(dunshu) / Float(BASE_FILTER_TIMESCALE))
                BFLog(message: "原所有卡点数:\(index) \(savePointStr)")
             
                pointsTemp.append(Float(savePointStr) ?? 0.0)
            }
        }

        /*
         一，快慢速模式下取卡点 2 3 4
         二，跳跃卡点模式下根据不同速度 取卡点 1,2,3
         快节奏为选中区域的所有点位，即0，1，2，3，4 5 6 7 8 9 10 ……
         适中为每两个点位取一个，即0，2，4，6……
         慢节奏为每三个点位取一个，即0，3，6，9……
         */
        // 不丢
        if seed == 1 {
            stuckPoints = pointsTemp
        } else {
            // 根据档位要丢
            for (index, point) in pointsTemp.enumerated() {
                if index % seed == 0 {
                    stuckPoints.append(point)
                }
            }
        }

        for point in stuckPoints {
            BFLog(message: "没有 start end 计算后的卡点数\(point)")
        }
        // 若音乐起点至第一个卡点点位之间时长t0<0.3时，此段时长与下一个点位时长合并，故第一段卡点部分时长为t0+d
        if Float(stuckPointMusicData?.startTime ?? 0) - (stuckPoints.first ?? 0.0) < 0.3 {
            if stuckPoints.first != nil {
                stuckPoints.removeFirst()
            }
        }
        stuckPoints.insert(Float(stuckPointMusicData?.startTime ?? 0), at: 0)

        for point in stuckPoints {
            BFLog(message: "有 start end 计算后的卡点数\(point)")
        }

        BFLog(message: "处理节奏后 stuckPoints count is \(stuckPoints.count) seed \(seed), start time:\(stuckPoints.first ?? 0.0),end time:\(stuckPoints.last ?? 0.0) 总时长为:\((stuckPoints.last ?? 0.0) - (stuckPoints.first ?? 0.0))")

        return stuckPoints
    }

    func clipPoint(clipNum: Int, oldPoints: Array<Float>) {
        BFLog(message: "拼接卡点数:\(clipNum)")
        if(clipNum < 0 || oldPoints.count < 2){
            BFLog(message: "clipNum is error!!!! \(clipNum)")
            return
        }
        // 如果是第一次拼接先补第0位
        if finallyStuckPoints.count == 0 {
            finallyStuckPoints.append(stuckPointsTemp.first ?? 0.0)
        }
        
        for i in finallyStuckPoints.count ... clipNum + finallyStuckPoints.count {
            if (i % (oldPoints.count - 1)) != 1 {
               let value =  String(format: "%.6f", finallyStuckPoints[i - 1] + oldPoints[((i-1) % (oldPoints.count - 1)) + 1] - oldPoints[((i - 2) % (oldPoints.count - 1)) + 1])
                
                finallyStuckPoints.append(Float(value) ?? 0.0)
            } else {
                let value =  String(format: "%.6f", finallyStuckPoints[i - 1] + oldPoints[1] - oldPoints[0])
                finallyStuckPoints.append(Float(value) ?? 0.0)
            }
        }
    }

    /// 根据不同模式model，    maxSpeed ,minSpeed，      self?.stuckPointMusicData?.speed 档位，生成音乐时长和最终使用的卡点信息
    func dealParameter(model: createStickersModel) {
        // 清空上一次使用的卡点数据
        finallyStuckPoints.removeAll()

        // 已经取到的视频素材总长度，用于和原视频素材时长做对比，不够多加一个点
        var useAssestDuration: Float = 0.0
        switch model {
        case .createStickersModelPoint: // 跳跃卡点
            stuckPointsTemp = getUsedStuckPoint(seed: stuckPointMusicData?.speed ?? 0)

            // 要拼接的段数
            var clipNum: Int = 0

            var i: Int = 0
            // L/（n+1）  L -原视觉素材总时长  n-抛留倍数  lastJumpSpeedSelectIndex 是位置 对应的值要+1
            // 根据公式计划出的总时长
            let jumpTime = Float(selectedTotalDuration) / Float(modelPoint_speed + 1)
            // 只有图片素材时会为0
            if jumpTime > 0 {
                while useAssestDuration < Float(jumpTime) {
                    // 回环从头取\
                    if i + 1 >= stuckPointsTemp.count {
                        i = 0
                    }
                    // 快速段
                    let LA = (stuckPointsTemp[i + 1] - stuckPointsTemp[i])
                    useAssestDuration = useAssestDuration + Float(LA)

                    i = i + 1
                    clipNum = clipNum + 1
                }

                // 拼接要使用的卡点信息

                clipPoint(clipNum: clipNum, oldPoints: stuckPointsTemp)
            }

            break
        case .createStickersModelSpeed, .createStickersModelOnlyMusic: // 快慢速
            // 快慢速  （2：快节奏，3：适中，4：慢节奏）
            var tempMaxSpeed: Float = 1
            var tempMinSpeed: Float = 1
            if model == .createStickersModelSpeed {
                // 改变速率,.只有快慢速且非只有图片素材时自动+1处理
                if model == .createStickersModelSpeed && selectedDataCount != selectedImageDataCount {
                    stuckPointsTemp = getUsedStuckPoint(seed: (stuckPointMusicData?.speed ?? 0) + 1)
                } else {
                    stuckPointsTemp = getUsedStuckPoint(seed: (stuckPointMusicData?.speed ?? 0))
                }

                tempMaxSpeed = modelSpeed_maxSpeed
                tempMinSpeed = modelSpeed_minSpeed
            } else {
                stuckPointsTemp = getUsedStuckPoint(seed: (stuckPointMusicData?.speed ?? 0))
                
            }

            /*
             - A-视频中的快速片段
             - B-视频中的慢速片段
             - d-在一档下音乐每个点位时长
             - n-不同音乐档位对应的d倍数，快节奏时，n=1；适中时，n=3；慢节奏时，n=5
             - L-原视觉素材时长
             - x-视频在A片段的播放倍速
             - y-视频在B片段的播放倍速
             */
            // LA=x*n*d，LB=y*n*d (n=1/3/5) 注：视频经过快慢速处理后的总时长约=L*2/(x+y)
            BFLog(message: "Ax快速为：\(tempMaxSpeed) By慢速为：\(tempMinSpeed) 档位 N为：\(stuckPointMusicData?.speed ?? 0)  使用的卡点总数:\(stuckPointsTemp.count)")
            // 使用新方法取使用的卡点数据
            for section in projectModel.sData?.sections ?? List() {
                if section.sectionType == "normal" {
                    for sticker in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials() {
                        if sticker.type == StickerType.VIDEO.rawValue {
                            let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + sticker.locationPath), options: nil)
                            let assetDuration = Float(CMTimeGetSeconds(asset.duration))
                            BFLog(message: "输入素材时长 \(assetDuration)")

                            if(finallyStuckPoints.count == 0){
                                finallyStuckPoints.append(stuckPointsTemp[0])
                            }
                            var j = finallyStuckPoints.count
                            // 添加卡点的段数 用于判断当前卡点是快/慢速
                            var pointCount:Int = 1
                            // 已经取到的视频素材总长度，用于和原视频素材时长做对比，不够多加一个卡点
                            var useAssestDurationTemp: Float = 0.0
                            while useAssestDurationTemp < assetDuration {
                                //当前段的应该使用的速度 A/B
                                let useSpeed = (pointCount % 2 != 0 ) ? tempMaxSpeed : tempMinSpeed
                                // 计算卡点
                                //要添加的卡点数值
                                var sub:Float = 0.0
                                if stuckPointsTemp.count > 2 {
                                    if ((j - 1) % (stuckPointsTemp.count - 1)) != 0{
                                        sub =  stuckPointsTemp[((j - 1) % (stuckPointsTemp.count-1)) + 1] - stuckPointsTemp[(j - 1) % (stuckPointsTemp.count - 1)]
                                    }else {
                                        sub = stuckPointsTemp[1] - stuckPointsTemp[0]
                                    }
                          
                                    finallyStuckPoints.append(finallyStuckPoints[j - 1] + sub)
                                    j += 1
                                }
                                useAssestDurationTemp += sub * useSpeed
                                if(useAssestDurationTemp > assetDuration){
                                    useAssestDurationTemp -= sub * useSpeed
                                    break
                                }
                                pointCount += 1
                                BFLog(2, message: "finallyStuckPoints last ；\((finallyStuckPoints.last ?? 0.0) - (finallyStuckPoints.first ?? 0.0)), tmp:\(useAssestDurationTemp)")
    
                            }

                            // 2拼接要使用的卡点信息
                            sticker.clipCount = pointCount
                            if stuckPointsTemp.count < 1 {
                                //todo 和产品沟通提示
                                BFLog(message: "卡点数据有错误！！！")
                                return
                            }
                            BFLog(message: "finallyStuckPoints\(finallyStuckPoints)")

                            // 3,多补一个卡点 做 C级 速处理,要根据条件不满足 要删除最后一位,
                            if useAssestDurationTemp < assetDuration {
                                /*
                                // 下一个卡的的速度性质快、慢,e.g. sticker.clipCount = 5时 tempSpeed 应该是慢速。下面计算正确。
                                var tempSpeed: Float = 1.0
                                if model == .createStickersModelSpeed {
                                    tempSpeed = (sticker.clipCount) % 2 != 0 ? modelSpeed_maxSpeed : modelSpeed_minSpeed
                                }
                                // 最后一点素材时长
                                let lastAssetDuration = Float(CMTimeGetSeconds(asset.duration)) - useAssestDurationTemp

                                let lastPointIndex = (sticker.clipCount % stuckPointsTemp.count)
                                // 两个卡点
                                let a: Float = stuckPointsTemp[lastPointIndex]
                                var b: Float = 0.0
                                if lastPointIndex + 1 < stuckPointsTemp.count {
                                    b = stuckPointsTemp[lastPointIndex + 1]
                                    let pointDuration = b - a
                                    // 要适应到卡点内要使用的C速度
                                    let needSpeed = lastAssetDuration / pointDuration
                                    // 当前卡点段为快速
                                    if tempSpeed >= 1 {
//                                        if needSpeed < 0.4 * tempSpeed {
//                                            BFLog(message: "条件不满足不用补位 删除多加的一位")
//                                            finallyStuckPoints.removeLast()
//                                        }
                                    } else { // 当前卡点段为慢速
                                        if needSpeed >= 0.4 * tempSpeed && needSpeed >= 0.2 {
                                            // 查找使用的最后一个卡点在原数组中的位置
                                        } else {
                                            BFLog(message: "条件不满足不用补位 删除多加的一位")
                                            finallyStuckPoints.removeLast()
                                        }

                                    }
                                }
 */
                            }else {
                                //出现在第一个卡点X 倍速 > 原素材
                                finallyStuckPoints.removeLast()
                            }
                        }
                    }
                }
            }

            break
        }
        // 拼接图片所使用的时长.选择一组图片 按图片数量计算卡点的总时长
        if selectedImageDataCount > 0 {
            clipPoint(clipNum: selectedImageDataCount - 1, oldPoints: stuckPointsTemp)
        }

        // 全是图片时数组里放着的一定都是图片的使用的卡点
        // 定义一次循环的总时长
        var oneSelectImageDuration: Float = 0.0
        if selectedDataCount == selectedImageDataCount {
            oneSelectImageDuration = (finallyStuckPoints.last ?? 0) - (finallyStuckPoints.first ?? 0)
        }

        // 3)素材全是图片处理
        if selectedDataCount == selectedImageDataCount{
           
            // lastCyclesSelectIndex != -1 已经设置过循环次数 应该是手动设置的值
            if lastCyclesSelectIndex != -1 {
                // 纯图片时 已经默认添加一次循环 所以要用lastCyclesSelectIndex - 1
                if(lastCyclesSelectIndex != 0){
                    clipPoint(clipNum: selectedImageDataCount * lastCyclesSelectIndex - 1, oldPoints: stuckPointsTemp)
                }
           
            } else {
                if(oneSelectImageDuration < 10 ){
                    lastCyclesSelectIndex = 0
                    while oneSelectImageDuration < 10 {
                        // 不够10S 时 一次加图片数量的卡点数
                        clipPoint(clipNum: selectedImageDataCount - 1, oldPoints: stuckPointsTemp)
                        oneSelectImageDuration = Float((finallyStuckPoints.last ?? 0) - (finallyStuckPoints.first ?? 0))

                        lastCyclesSelectIndex = lastCyclesSelectIndex + 1
                    }
                    speedSettingView.setSelectItem(index: lastCyclesSelectIndex, isSettingPlayer: false,enableInsert: true)
                }else{
                    lastCyclesSelectIndex = 0
                }
            }
        }
        
        // 设置速度选择的位置
        if speedSettingView.viewType == 1 {
            speedSettingView.setSelectItem(index: lastSpeedSelectIndex, isSettingPlayer: false)
        } else if speedSettingView.viewType == 2 {
            speedSettingView.setSelectItem(index: lastJumpSpeedSelectIndex, isSettingPlayer: false)
        } else if speedSettingView.viewType == 3 {
            if lastCyclesSelectIndex != -1 {
                speedSettingView.setSelectItem(index: lastCyclesSelectIndex, isSettingPlayer: false)
            } else {
                speedSettingView.setSelectItem(index: 0, isSettingPlayer: false)
            }
        }

       updateTimeInfomation()
    }
}

// MARK: - 同步/下载素材相关

/// 同步/下载素材相关
extension PQStuckPointEditerController {
    /// 同步音乐相关数据
    /// - Returns: <#description#>
    func synchroMusicInfoData(resetSelectIndex:Bool = true) {
        if (stuckPointMusicData?.rhythmSdata.count ?? 0) <= 0 {
            synchroMarskView.show()
            PQStuckPointViewModel.stuckPointMusicDetailData(musicId: stuckPointMusicData?.musicId ?? "", originType: stuckPointMusicData?.originType ?? 1) { [weak self] newMusicData, _ in
                if newMusicData != nil, (newMusicData?.rhythmSdata.count ?? 0) > 0 {
                    self?.isStuckPointDataSuccess = true
                    self?.stuckPointMusicData?.rhythmSdata = newMusicData?.rhythmSdata ?? []
                    self?.stuckPointMusicData?.startTime = newMusicData?.startTime ?? 0
                    self?.stuckPointMusicData?.endTime = newMusicData?.endTime ?? 0
                    if newMusicData?.speed != nil {
                        self?.stuckPointMusicData?.speed = newMusicData?.speed ?? 2
                    }

                    if self?.stuckPointMusicData?.localPath == nil || (self?.stuckPointMusicData?.localPath?.count ?? 0) > 0 {
                        PQDownloadManager.downLoadFile(url: self?.stuckPointMusicData?.musicPath ?? "") { [weak self] filePath, error in
                            if error == nil, filePath != nil {
                                self?.isSynchroMusicInfoSuccess = true
                                self?.stuckPointMusicData?.localPath = filePath?.replacingOccurrences(of: documensDirectory, with: "")
                                // 处理所有数据完成
                                self?.dealWithDataSuccess(resetSelectIndex: resetSelectIndex)
                            }
                            self?.synchroMarskView.removeMarskView()
                        }
                    } else {
                        self?.isSynchroMusicInfoSuccess = true
                        // 处理所有数据完成
                        self?.dealWithDataSuccess(resetSelectIndex: resetSelectIndex)
                    }
                    // 添加子视图
                    self?.addSubViews()
                } else {
                    self?.synchroMarskView.removeMarskView()
                    cShowHUB(superView: nil, msg: "音乐信息加载失败，请重新选择音乐")
                    self?.navigationController?.popViewController(animated: true)
                }
            }
        } else if stuckPointMusicData?.localPath == nil || (stuckPointMusicData?.localPath?.count ?? 0) > 0 {

            synchroMarskView.show()
            isStuckPointDataSuccess = true
            
            PQDownloadManager.downLoadFile(url: stuckPointMusicData?.musicPath ?? "") { [weak self] filePath, error in
                if error == nil, filePath != nil {
                    self?.isSynchroMusicInfoSuccess = true
                    self?.stuckPointMusicData?.localPath = filePath?.replacingOccurrences(of: documensDirectory, with: "")
                    // 处理所有数据完成
                    self?.dealWithDataSuccess(resetSelectIndex: resetSelectIndex)
                } else {
                    self?.synchroMarskView.removeMarskView()
                    cShowHUB(superView: nil, msg: "音乐信息加载失败，请重新选择音乐")
//                    PQUploadRemindView.showUploadRemindView(title: nil, attributedTitle: NSAttributedString(string: "加载音乐失败，请重新选择音乐"), summary: "", confirmTitle: nil) { [weak self] _, _ in
//                        self?.navigationController?.popViewController(animated: true)
//                    }
                }
            }
        } else {
            isStuckPointDataSuccess = true
            // 处理所有数据完成
            dealWithDataSuccess(resetSelectIndex: resetSelectIndex)
        }
    }

    /// 导出相册数据
    /// - Returns: <#description#>
    func exportPhotoData() {
        // 取消所有的导出
        PQSingletoMemoryUtil.shared.allExportSession.forEach { _, exportSession in
            exportSession.cancelExport()
        }
        var isHaveVideo: Bool = false
        if selectedMetarialData != nil, (selectedMetarialData?.count ?? 0) > 0 {
            if synchroMarskView.superview == nil {
                UIApplication.shared.keyWindow?.addSubview(synchroMarskView)
            }
            let dispatchGroup = DispatchGroup()

            for photo in selectedMetarialData! {
                if photo.asset != nil, photo.asset?.mediaType == .video {
                    if !isHaveVideo {
                        isHaveVideo = true
                    }
                    dispatchGroup.enter()

                    PQPHAssetVideoParaseUtil.parasToAVAsset(phAsset: photo.asset!) { avAsset, _, _, _ in
                        if avAsset is AVURLAsset {
                            // 创建目录

                            let fileName = (avAsset as! AVURLAsset).url.absoluteString

                            BFLog(message: "video  fileName is\(fileName)")
                            let tempPhoto = self.selectedMetarialData?.first(where: { material in
                                material.asset == photo.asset
                            })

                            if fileName.count > 0 {
                                createDirectory(path: photoLibraryDirectory)
                                let outFilePath = photoLibraryDirectory + fileName.md5 + ".mp4"
                                // 文件存在先删除老文件
                                if FileManager.default.fileExists(atPath: outFilePath) {
                                    do {
                                        try FileManager.default.removeItem(at: NSURL.fileURL(withPath: outFilePath))
                                    } catch {
                                        BFLog(message: "导出相册视频-error == \(error)")
                                    }
                                }

                                do {
                                    try FileManager.default.copyItem(atPath: fileName.replacingOccurrences(of: "file:///", with: ""), toPath: outFilePath)
                                    print("Success to copy file.")
                                } catch {
                                    print("Failed to copy file.")
                                }

                                tempPhoto?.locationPath = outFilePath.replacingOccurrences(of: documensDirectory, with: "")
                                BFLog(message: "导出视频相册地址为 \(String(describing: tempPhoto?.locationPath))")

                                dispatchGroup.leave()
                            }
                        }else {
                            // 结束loading动画
                            DispatchQueue.main.async {
                                self.synchroMarskView.removeMarskView()
                            }
                        }
                    }
                }
            }

            dispatchGroup.notify(queue: DispatchQueue.main) { [weak self] in
                self?.isExportVideosSuccess = true
                BFLog(message: "所有相册视频导出成功")
                // 处理所有数据完成
                if isHaveVideo {
                    self?.dealWithDataSuccess()
                }
            }

            // 只有图片
            if !isHaveVideo {
                isExportVideosSuccess = true
                BFLog(message: "所有相册视频导出成功")

                dealWithDataSuccess()
            }
        }
    }

    /// 处理所有数据完成
    /// - Returns: <#description#>
    /// resetSelectIndex : 是否重置用户选择的速度设置，在编辑界面切换音乐时不重置
    func dealWithDataSuccess(resetSelectIndex:Bool = true) {
        BFLog(message: "三组参数：\(isSynchroMusicInfoSuccess)  \(isStuckPointDataSuccess) \(isExportVideosSuccess)")
        if !isSynchroMusicInfoSuccess || !isStuckPointDataSuccess || !isExportVideosSuccess {
            return
        }

        createPorjectData()
        BFLog(1, message: "界面编辑界面时参数 选择素材时长:\(selectedTotalDuration) 选择素材总数:\(selectedDataCount) 选择图片总数\(selectedImageDataCount) 再创建类型：\(String(describing: reCreateVideoData?.rhythmMode))")

        if(resetSelectIndex){
            // 1 生成默认参数值
            /*
             - 当素材总时长∈[0-6)s 时，提示推荐仅配乐模式 or 快慢速模式
             - 当素材总时长∈[6-80)s 时，卡点抛留倍数为1x
             - 当素材总时长∈[80,120)s 时，卡点抛留倍数为2x
             - 当素材总时长∈[120,160)s 时，卡点抛留倍数为3x
             - 当素材总时长∈[160,200)s 时，卡点抛留倍数为4x
             - 当素材总时长∈[200,240)s 时，卡点抛留倍数为5x
             - 当素材总时长∈[240,∞)s 时，卡点抛留倍数为5x
             */
            // 1.1) 生成跳跃卡点的默认值
            if selectedTotalDuration >= 6 && selectedTotalDuration < 80 {
                lastJumpSpeedSelectIndex = 0
            } else if selectedTotalDuration >= 80 && selectedTotalDuration < 120 {
                lastJumpSpeedSelectIndex = 1
            } else if selectedTotalDuration >= 120 && selectedTotalDuration < 160 {
                lastJumpSpeedSelectIndex = 2
            } else if selectedTotalDuration >= 160 && selectedTotalDuration < 200 {
                lastJumpSpeedSelectIndex = 3
            } else if (selectedTotalDuration >= 200 && selectedTotalDuration < 240) || selectedTotalDuration >= 240 {
                lastJumpSpeedSelectIndex = 4
            }

            /* 默认进入快慢速模式
             - 当素材总时长∈[120,144]s 时，快慢速处理方式：快速为 6x，慢速为 1.2x，效果是快&快
             - 当素材总时长∈[70,120)s 时，快慢速处理方式：快速为5x，慢速为 1x，效果是快&正常
             - 当素材总时长∈[56,70)s 时，快慢速处理方式：快速为3x，慢速为 0.5x，效果是快&慢
             - 当素材总时长∈[17.5,56)s 时，快慢速处理方式：快速为 2.4x，慢速为 0.4x，效果是快&慢
             - 当素材总时长∈[10.5,17.5)s 时，快慢速处理方式：快速为 1.8x，慢速为 0.3x，效果是快&慢
             - 当素材总时长∈(0,10.5)s 时，快慢速处理方式：快速为 1x，慢速为 0.2x，效果是正常&慢

             */
            // 1.2)生成快慢速的默认值
            if selectedTotalDuration >= 120 && selectedTotalDuration <= 144 || selectedTotalDuration > 144 {
                lastSpeedSelectIndex = 5
            } else if selectedTotalDuration >= 70 && selectedTotalDuration < 120 {
                lastSpeedSelectIndex = 4
            } else if selectedTotalDuration >= 56 && selectedTotalDuration < 70 {
                lastSpeedSelectIndex = 3
            } else if selectedTotalDuration >= 17.5 && selectedTotalDuration < 56 {
                lastSpeedSelectIndex = 2
            } else if selectedTotalDuration >= 10.5 && selectedTotalDuration < 17.5 {
                lastSpeedSelectIndex = 1
            } else if selectedTotalDuration > 0 && selectedTotalDuration < 10.5 {
                lastSpeedSelectIndex = 0
            }
            
            // 如果是再创作进来的安原视频的模式
            if reCreateVideoData != nil {
                BFLog(message: "是再创作进来的 \(reCreateVideoData!.rhythmMode)")
                switch reCreateVideoData!.rhythmMode {
                case 1:
                    editModelClick(sender: jumpPointBtn, reportLog: false)
                    break
                case 2:
                    editModelClick(sender: speedStuckBtn, reportLog: false)

                    break
                case 3:
                    editModelClick(sender: onlyMusicBtn, reportLog: false)
                    break
                default: break
                }
                return
            } 
             // 跳跃卡点不可用
             if selectedTotalDuration < 6 && selectedDataCount != selectedImageDataCount {
                
                let styleColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
                jumpPointBtn.setTitleColor(UIColor.init(red: styleColor.rgbaf[0], green: styleColor.rgbaf[1], blue: styleColor.rgbaf[2], alpha: 0.3), for: .selected)
                
                let pointEditNamalBackgroundColor = PQBFConfig.shared.pointEditNamalBackgroundColor
                jumpPointBtn.backgroundColor = UIColor.init(red: pointEditNamalBackgroundColor.rgbaf[0], green: pointEditNamalBackgroundColor.rgbaf[1], blue: pointEditNamalBackgroundColor.rgbaf[2], alpha: 0.3)
 
             }

            /*
             文档规则 https://w42nne6hzg.feishu.cn/docs/doccnQZm1uCfkU4QtJb5fLxYk4d#
             */
            // 2,根据所选择所有素材时长进入默认模式
            // 全是图片
            if selectedDataCount == selectedImageDataCount {
                BFLog(message: "全是图片 \(selectedDataCount) \(selectedImageDataCount)")

                // 默认进入跳跃卡点模式
                editModelClick(sender: jumpPointBtn, reportLog: false)

            } else {
                // 默认进入快慢速模式
                if selectedTotalDuration > 0 && selectedTotalDuration <= 144 {
                    editModelClick(sender: speedStuckBtn, reportLog: false)

                } else {
                    // 默认进入卡点模式
                    editModelClick(sender: jumpPointBtn, reportLog: false)

                }
            }
        }else{
            editModelClick(sender: lastEditModelBtn ?? jumpPointBtn, reportLog: false)
        }
 
  
    }
}
