//
//  GPVideoClipperView.swift
//  GPVideoClipperDemo-Swift
//
//  Created by Roc Kwok on 2020/5/19.
//  Copyright © 2020 Roc Kwok. All rights reserved.
//

import UIKit
import AVKit

@objc protocol GPVideoClipperViewDelegate: NSObjectProtocol {
    @objc func gp_playButtonAction(button: UIButton)
    @objc func gp_cancelButtonAction(button: UIButton)
    @objc func gp_doneButtonAction(button: UIButton)
    @objc func gp_videoLengthDidChanged(time: CGFloat)
    @objc func gp_didEndDragging()
}

class GPVideoClipperView: UIView, UICollectionViewDelegate, UICollectionViewDataSource {

    // Const
    let kDurationLabelWidth = 70.0
    let kButtonWidth: CGFloat = 60.0
    let kButtonHeight: CGFloat = 30.0
    let kLineHeight: CGFloat = 3.0
    
    // Public
    weak var delegate:GPVideoClipperViewDelegate?
    var avAsset: AVAsset! {
          willSet {
            self.avAsset = newValue
              kDelayMainShort {
                  self.p_loadThumbnailImages()
              }
          }
    }
    var progressTime: CGFloat! {
        return (self.progressView.frame.minX - self.leftImageView.frame.maxX) / self.perSecondWidth
    }
    
    // Private
    private var maker: GPVideoConfigMaker!
    private var selectedTime: CGFloat!
    private var cellWidth: CGFloat!
    private var cellCount: UInt!
    private var perSecondWidth: CGFloat!
    private var collectionInsets: UIEdgeInsets!
    private var imageArray:[UIImage] = []
    private var preOriginX: CGFloat!
    private var selectedImageView: UIImageView!
    
    private lazy var collectionView: UICollectionView = {
        var layout = UICollectionViewFlowLayout.init()
        layout.itemSize = CGSize.init(width: self.cellWidth, height: 40)
        layout.minimumInteritemSpacing = 0.0
        layout.minimumLineSpacing = 0.01
        layout.scrollDirection = .horizontal
        
        var collectionView = UICollectionView.init(frame:.zero, collectionViewLayout: layout)
        collectionView.backgroundColor = .clear
        collectionView.alwaysBounceHorizontal = true
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.clipsToBounds = false
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.contentInset = self.collectionInsets;
        collectionView.clipsToBounds = true
        collectionView.register(GPVideoClipperImageCell.self, forCellWithReuseIdentifier: NSStringFromClass(GPVideoClipperImageCell.self))
        return collectionView
    }()
    
    private lazy var imageGenerator: AVAssetImageGenerator = {
        var imageGenerator = AVAssetImageGenerator.init(asset: self.avAsset)
        imageGenerator.appliesPreferredTrackTransform = true;
        imageGenerator.requestedTimeToleranceBefore = .zero;
        imageGenerator.requestedTimeToleranceAfter = .zero;
        imageGenerator.maximumSize = CGSize.init(width: 320, height: 320)
        return imageGenerator
    }()
    
    private lazy var durationLabel: UILabel = {
        var durationLabel = UILabel.init()
        durationLabel.font = UIFont.boldSystemFont(ofSize: 12)
        durationLabel.backgroundColor = .black
        durationLabel.textColor = .white
        durationLabel.textAlignment = .right
        durationLabel.isHidden = self.maker.isHiddenSelectedTimeTag
        return durationLabel
    }()
    
    private lazy var startTimeLabel: UILabel = {
        var durationLabel = UILabel.init()
        durationLabel.font = UIFont.boldSystemFont(ofSize: 12)
        durationLabel.backgroundColor = .black
        durationLabel.textColor = .white
        durationLabel.textAlignment = .left
        durationLabel.isHidden = self.maker.isHiddenSelectedTimeTag
        return durationLabel
    }()
    
    private lazy var endTimeLabel: UILabel = {
        var durationLabel = UILabel.init()
        durationLabel.font = UIFont.boldSystemFont(ofSize: 12)
        durationLabel.backgroundColor = .black
        durationLabel.textColor = .white
        durationLabel.textAlignment = .right
        durationLabel.isHidden = self.maker.isHiddenSelectedTimeTag
        return durationLabel
    }()
    
    private lazy var playBtn: UIButton = {
        var playBtn = UIButton.createButton(image: UIImage(named: "edit_pause"))
        playBtn.addTarget(self, action: #selector(palyButtonAction(_:)), for: .touchUpInside)
        playBtn.isSelected = true
        return playBtn
    }()
    
    private lazy var leftPlayBtnView: UIView = {
        let view = UIView()
        
        let bgImageView = UIImageView.createImageView(imageName:"edit_play_bg")
        bgImageView.frame = CGRect(x: 0, y: 0, width: 54, height: 40)
        view.addSubview(bgImageView)
        
        playBtn.frame = CGRect(x: 12, y: 7, width: 24, height: 24)
        view.addSubview(playBtn)
        
        return view
        
    }()
    
    private lazy var doneButton: UIButton = {
        var doneButton = TSViewTool.createNormalSubmitBtn(title: "Create Live Wallpaper".localized)
        doneButton.layer.cornerRadius = 24
        doneButton.clipsToBounds = true
        doneButton.addTarget(self, action: #selector(doneButtonAction(_:)), for: .touchUpInside)
        return doneButton
    }()
    
    private lazy var leftImageView: UIImageView = {
        var leftImageView = UIImageView.init()
        leftImageView.isUserInteractionEnabled = true
        leftImageView.contentMode = .scaleToFill
//        if ((self.maker?.selectedBoxColor) != nil) {
//            leftImageView.backgroundColor = self.maker.selectedBoxColor
//        }
        if ((self.maker?.leftSelectedImage) != nil) {
            leftImageView.image = self.maker.leftSelectedImage
        }
        var panGesture = UIPanGestureRecognizer.init(target: self, action: Selector.init(("panGesture:")))
        panGesture.maximumNumberOfTouches = 1
        leftImageView.addGestureRecognizer(panGesture)
        return leftImageView
    }()
    
    private lazy var rightImageView: UIImageView = {
        var rightImageView = UIImageView.init()
        rightImageView.isUserInteractionEnabled = true
        rightImageView.contentMode = .scaleAspectFill
//        if ((self.maker?.selectedBoxColor) != nil) {
//            rightImageView.backgroundColor = self.maker.selectedBoxColor
//        }
        if ((self.maker?.rightSelectedImage) != nil) {
            rightImageView.image = self.maker.rightSelectedImage
        }
        var panGesture = UIPanGestureRecognizer.init(target: self, action: Selector.init(("panGesture:")))
        panGesture.maximumNumberOfTouches = 1
        rightImageView.addGestureRecognizer(panGesture)
        return rightImageView
    }()
    
    private lazy var topLine: UIImageView = {
        var topLine = UIImageView()
        if ((self.maker?.selectedBoxColor) != nil ){
            topLine.backgroundColor = self.maker.selectedBoxColor
        }
        return topLine
    }()
    
    private lazy var bottomLine: UIImageView = {
        var bottomLine = UIImageView()
        if ((self.maker?.selectedBoxColor) != nil ){
            bottomLine.backgroundColor = self.maker.selectedBoxColor
        }
        return bottomLine
    }()
    
    private lazy var progressView: UIButton = {
        var progressView = UIButton.init(type:.custom)
        progressView.isEnabled = true
        progressView.isUserInteractionEnabled = true
        progressView.backgroundColor = UIColor.white
        return progressView
    }()
    
    //MARK: - Action
    
    @objc func panGesture(_ gesture: UIPanGestureRecognizer) {
        let point = gesture.translation(in: self.superview)
        switch gesture.state {
        case .began:
            self.preOriginX = 0
            self.selectedImageView = gesture.view as? UIImageView
            break
        case .changed:
            var offsetX = point.x - self.preOriginX
            self.preOriginX = point.x
            if self.selectedImageView == self.leftImageView {
                var frame = self.leftImageView.frame
                frame.origin.x += offsetX
                if frame.origin.x <= self.maker.leftMargin {
                    offsetX += self.maker.leftMargin - frame.origin.x;
                    frame.origin.x = self.maker.leftMargin;
                }
                let minLength = self.rightImageView.frame.origin.x - self.maker.clippedVideoMinDuration * self.perSecondWidth - self.maker.selectedImageWidth
                if (frame.origin.x >= minLength) {
                    offsetX -= frame.origin.x - minLength;
                    frame.origin.x = minLength;
                }
                let time = offsetX / self.perSecondWidth;
                self.maker.startTime = self.maker.startTime + time;
                self.leftImageView.frame = frame;
                if (self.delegate != nil) && (self.delegate?.responds(to: #selector(self.delegate?.gp_videoLengthDidChanged(time:))))! {
                    self.delegate?.gp_videoLengthDidChanged(time: self.maker!.startTime)
                }
            } else if self.selectedImageView == self.rightImageView {
                var frame = self.rightImageView.frame
                frame.origin.x += offsetX
                let rightImageMaxX = self.frame.size.width - self.maker.rightMargin - self.maker.selectedImageWidth
                if (frame.origin.x >= rightImageMaxX) {
                    offsetX -= frame.origin.x - rightImageMaxX;
                    frame.origin.x = rightImageMaxX;
                }
                let rightImageMinX = self.leftImageView.frame.maxX + self.maker.clippedVideoMinDuration * self.perSecondWidth;
                if (frame.origin.x <= rightImageMinX) {
                    offsetX += rightImageMinX - frame.origin.x;
                    frame.origin.x = rightImageMinX;
                }
                let time = offsetX / self.perSecondWidth;
                self.maker.endTime = self.maker.endTime + time;
                self.rightImageView.frame = frame;
                if (self.delegate != nil) && (self.delegate?.responds(to: #selector(self.delegate?.gp_videoLengthDidChanged(time:))))! {
                    self.delegate?.gp_videoLengthDidChanged(time: self.maker!.endTime)
                }
            }
            
            self.progressView.frame = CGRect.init(x: self.leftImageView.frame.maxX, y: self.progressView.frame.origin.y, width: self.progressView.frame.size.width, height: self.progressView.frame.size.height)
            self.selectedTime = self.maker.endTime - self.maker.startTime;
            self.durationLabel.text = String.init(format: "已选取%.1fs", self.selectedTime)
            
            
            var startDuration:Double = self.maker.startTime
            if let duration = Double(String(format: "%.2f", arguments: [startDuration])) {
                startDuration = duration
            }
            var endDuration:Double = self.maker.endTime
            if let duration = Double(String(format: "%.2f", arguments: [endDuration])) {
                endDuration = duration
            }
            self.startTimeLabel.text = startDuration.time
            self.endTimeLabel.text = endDuration.time
            
            
            var topLineFrame = self.topLine.frame;
            var bottomLineFrame = self.bottomLine.frame;
            topLineFrame.origin.x = self.leftImageView.frame.maxX
            bottomLineFrame.origin.x = self.leftImageView.frame.maxX
            topLineFrame.size.width = self.rightImageView.frame.minX - self.leftImageView.frame.maxX
            bottomLineFrame.size.width = self.rightImageView.frame.minX - self.leftImageView.frame.maxX
            
            self.topLine.frame = topLineFrame;
            self.bottomLine.frame = bottomLineFrame;
            break
        case .ended:
            self.selectedImageView = nil
            if (self.delegate != nil) && (self.delegate?.responds(to: #selector(self.delegate?.gp_didEndDragging)))! {
                self.delegate?.gp_didEndDragging()
            }
            break
        default:
            break
        }
    }
    
    //MARK: - <UICollectionViewDelegate, UICollectionViewDataSource>
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.imageArray.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(GPVideoClipperImageCell.self), for: indexPath) as! GPVideoClipperImageCell
        cell.imageView.image = self.imageArray[indexPath.item]
        return cell
    }
    
    //MARK: - <ScrollViewDelegate>
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let time = (scrollView.contentOffset.x + self.collectionInsets.left) / self.perSecondWidth
        let startTime = time + (self.leftImageView.frame.maxX - self.collectionInsets.left) / self.perSecondWidth
        
        guard startTime < 0 else {
            self.maker.startTime = startTime
            let endTime = self.maker.startTime + self.selectedTime
            self.maker.endTime = endTime > self.maker.sourceVideoTotalDuration ? self.maker.sourceVideoTotalDuration:endTime
            self.maker.startTime = self.maker.endTime - self.selectedTime
            self.gp_updateProgressViewWithProgress(0.0)
            if (self.delegate != nil) && (self.delegate?.responds(to: #selector(self.delegate?.gp_videoLengthDidChanged(time:))))! {
                self.delegate?.gp_videoLengthDidChanged(time: self.maker!.startTime + self.progressTime)
            }
            return
        }
    }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if (self.delegate != nil) && (self.delegate?.responds(to: #selector(self.delegate?.gp_didEndDragging)))! {
            self.delegate?.gp_didEndDragging()
        }
    }
    
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        if !decelerate {
           if (self.delegate != nil) && (self.delegate?.responds(to: #selector(self.delegate?.gp_didEndDragging)))! {
               self.delegate?.gp_didEndDragging()
           }
        }
    }
    
    @objc func cancelButtonAction(_ button: UIButton) {
        if (self.delegate != nil) && (self.delegate?.responds(to: #selector(self.delegate?.gp_cancelButtonAction(button:))))! {
            self.delegate?.gp_cancelButtonAction(button: button)
        }
    }
    
    @objc func doneButtonAction(_ button: UIButton) {
        if (self.delegate != nil) && (self.delegate?.responds(to: #selector(self.delegate?.gp_doneButtonAction(button:))))! {
            self.delegate?.gp_doneButtonAction(button: button)
        }
    }
    
    @objc func palyButtonAction(_ button: UIButton) {
        if (self.delegate != nil) && (self.delegate?.responds(to: #selector(self.delegate?.gp_playButtonAction(button:))))! {
            self.delegate?.gp_playButtonAction(button: button)
            
            button.isSelected = !button.isSelected
            button.setImage(UIImage(named: button.isSelected ? "edit_pause" : "edit_play"), for: .normal)
        }
    }
    
    
    //MARK: - Init
    
    init(frame: CGRect, maker: GPVideoConfigMaker) {
        self.maker = maker
        super.init(frame: frame)
        self.isUserInteractionEnabled = true
        self.clipsToBounds = true
        self.backgroundColor = .clear
        
        let length = self.frame.size.width - self.maker.leftMargin - self.maker.rightMargin - self.maker.selectedImageWidth * 2
        self.cellWidth = length / CGFloat(self.maker.defaultSelectedImageCount)
        
        if self.maker.sourceVideoTotalDuration <= self.maker.clippedVideoMaxDuration {
            self.maker.endTime = self.maker.sourceVideoTotalDuration;
            self.cellCount = self.maker.defaultSelectedImageCount
        } else {
            self.maker.endTime = self.maker.clippedVideoMaxDuration;
            let temp = self.maker.endTime / CGFloat(self.maker.defaultSelectedImageCount)
            self.cellCount = UInt(self.maker.sourceVideoTotalDuration / CGFloat(temp))
        }
        
        let value = self.frame.size.width - self.maker.leftMargin - self.maker.rightMargin - self.maker.selectedImageWidth - self.maker.selectedImageWidth
        self.perSecondWidth = value / CGFloat(self.maker.endTime - self.maker.startTime)
        self.collectionInsets = UIEdgeInsets.init(top: 0, left: 0, bottom: 0, right: 0)
        self.p_configSubview()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    //MARK: - Private
    
    func p_configSubview() {
        
        let topLineY = 40.0
        let collectionViewH = 40.0
        
        leftPlayBtnView.frame = CGRect(x: 37, y: topLineY, width: 54, height: 40)
        self.addSubview(leftPlayBtnView)
        
        let collectionViewX = self.maker.leftMargin + self.maker.selectedImageWidth
        let collectionViewW = self.frame.size.width - self.maker.selectedImageWidth - self.maker.rightMargin - collectionViewX
        self.addSubview(self.collectionView)
        self.collectionView.frame = CGRect.init(x: collectionViewX, y: topLineY, width:collectionViewW, height: collectionViewH)
    
        self.addSubview(self.durationLabel)
        self.durationLabel.frame = CGRect.init(x: (self.frame.size.width - kDurationLabelWidth)/2.0, y: topLineY + collectionViewH, width: kDurationLabelWidth, height: 14)
        
        self.addSubview(self.startTimeLabel)
        self.startTimeLabel.frame = CGRect.init(x: self.maker.leftMargin, y: 16, width: 60, height: 16)
        
        self.addSubview(self.endTimeLabel)
        self.endTimeLabel.frame = CGRect.init(x: self.frame.size.width - 37.0 - 60.0, y: 16, width: 60, height: 16)
        
        
        self.addSubview(self.leftImageView)
        self.leftImageView.frame = CGRect.init(x: self.maker.leftMargin, y: topLineY, width: self.maker.selectedImageWidth, height: collectionViewH)
        
        self.addSubview(self.rightImageView)
        self.rightImageView.frame = CGRect.init(x: self.frame.size.width - self.maker.selectedImageWidth - self.maker.rightMargin, y:topLineY, width: self.maker.selectedImageWidth, height: collectionViewH)
        
        self.addSubview(self.topLine)
        self.topLine.frame = CGRect.init(x: self.maker.leftMargin + self.maker.selectedImageWidth, y: topLineY, width: self.frame.size.width - self.maker.leftMargin - self.maker.rightMargin - self.maker.selectedImageWidth * 2, height: kLineHeight)
        
        self.addSubview(self.bottomLine)
        self.bottomLine.frame = CGRect.init(x: self.topLine.frame.origin.x, y: self.collectionView.frame.maxY - kLineHeight, width: self.topLine.frame.size.width, height: kLineHeight)
        
        self.addSubview(self.progressView)
        self.progressView.frame = CGRect.init(x: self.leftImageView.frame.maxX, y: topLineY, width: 2.0, height: collectionViewH)
        
        self.addSubview(self.doneButton)
        self.doneButton.frame = CGRect.init(x: 48, y: self.frame.size.height - 48, width: self.frame.size.width - 48*2, height: 48)
    }
    
    func p_loadThumbnailImages () {
        self.selectedTime = self.maker.endTime - self.maker.startTime
        self.durationLabel.text = String.init(format: "已选取%.1fs", self.selectedTime)
        
        
        var startDuration:Double = self.maker.startTime
        if let duration = Double(String(format: "%.2f", arguments: [startDuration])) {
            startDuration = duration
        }
        var endDuration:Double = self.maker.endTime
        if let duration = Double(String(format: "%.2f", arguments: [endDuration])) {
            endDuration = duration
        }
        self.startTimeLabel.text = startDuration.time
        self.endTimeLabel.text = endDuration.time
        
        var array: [NSValue] = []
        
        var startTime: CMTime = .zero
        let addTime: CMTime = CMTimeMakeWithSeconds(Float64(self.maker.sourceVideoTotalDuration/CGFloat(self.cellCount)), preferredTimescale: 1000)
        let endTime: CMTime = CMTimeMakeWithSeconds(Float64(self.maker!.sourceVideoTotalDuration), preferredTimescale: 1000)

        while startTime <= endTime {
            array.append(NSValue.init(time: startTime))
            startTime = CMTimeAdd(startTime, addTime)
        }
        weak var weakSelf = self
        var index = 0
        self.imageGenerator.generateCGImagesAsynchronously(forTimes: array) { (requestedTime, image, actualTime, result, error) in
            if result == .succeeded {
                let img = UIImage.init(cgImage: image!)
                DispatchQueue.main.async {
                    self.imageArray.append(img)
                    let indexPath = NSIndexPath.init(item: index, section: 0)
                    weakSelf?.collectionView.insertItems(at: [(indexPath as IndexPath)])
                    index += 1
                }
            }else{
                debugPrint(error)
            }
        }
    }
    
    //MARK: - Public
    
    func gp_updateProgressViewWithProgress(_ progress: CGFloat) {
        guard self.selectedImageView != nil else {
            let width = self.rightImageView.frame.minX - self.leftImageView.frame.maxX;
            let newX = self.leftImageView.frame.maxX + progress * width;

            UIView.animate(withDuration: 0.1, delay: 0, options:[.curveLinear, .allowUserInteraction], animations: {
                self.progressView.frame = CGRect.init(x: newX, y: self.progressView.frame.origin.y, width: self.progressView.frame.size.width, height: self.progressView.frame.size.height)
            }, completion: nil)
            return
        }
    }
}

class GPVideoClipperImageCell: UICollectionViewCell {
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.addSubview(self.imageView)
        self.contentView.backgroundColor = .clear
        self.backgroundColor = .clear
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    //MARK: - Getters
    
    public lazy var imageView: UIImageView = {
        var imageView = UIImageView()
        imageView.frame = self.contentView.bounds
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        return imageView
    }()
}


fileprivate extension Double {
    var time: String {
        if self < 60 {
            let sec = Double(String(format: "%.2f", arguments: [self])) ?? self
            if sec < 10 {
                return String(format: "00:0%.2f", arguments: [sec])
            }else {
                return String(format: "00:%.2f", arguments: [sec])
            }
        }else {
            var min = Double(Int(self / 60))
            let sec = Double(String(format: "%.2f", arguments: [self - (min * 60)])) ?? self - (min * 60)
            if min < 60 {
                if min < 10 {
                    if sec < 10 {
                        return String(format: "0%.0f:0%.2f", arguments: [min, sec])
                    }else {
                        return String(format: "0%.0f:%.2f", arguments: [min, sec])
                    }
                }else {
                    if sec < 10 {
                        return String(format: "%.0f:0%.2f", arguments: [min, sec])
                    }else {
                        return String(format: "%.0f:%.2f", arguments: [min, sec])
                    }
                }
            }else {
                let hour = Double(Int(min / 60))
                min -= hour * 60
                if hour < 10 {
                    if min < 10 {
                        if sec < 10 {
                            return String(format: "0%.0f:0%.0f:0%.2f", arguments: [hour, min, sec])
                        }else {
                            return String(format: "0%.0f:0%.0f:%.2f", arguments: [hour, min, sec])
                        }
                    }else {
                        if sec < 10 {
                            return String(format: "0%.0f:%.0f:0%.2f", arguments: [hour, min, sec])
                        }else {
                            return String(format: "0%.0f:%.0f:%.2f", arguments: [hour, min, sec])
                        }
                    }
                }
                if min < 10 {
                    if sec < 10 {
                        return String(format: "%.0f:0%.0f:0%.2f", arguments: [hour, min, sec])
                    }else {
                        return String(format: "%.0f:0%.0f:%.2f", arguments: [hour, min, sec])
                    }
                }else {
                    if sec < 10 {
                        return String(format: "%.0f:%.0f:0%.2f", arguments: [hour, min, sec])
                    }else {
                        return String(format: "%.0f:%.0f:%.2f", arguments: [hour, min, sec])
                    }
                }
            }
        }
    }
}
