//
//  CamelotVideoEditor.swift
//  camelot
//
//  Created by Miguel Martínez Ojeda on 13/11/2020.
//

import Foundation
import AVFoundation
import CoreMedia
import UIKit


@objc(CamelotVideoEditor)
class CamelotVideoEditor: RCTEventEmitter {
  
  override static func requiresMainQueueSetup() -> Bool {
      return false
  }
  
  @objc func merge(
    _ videos: [NSDictionary],
    resolver resolve: @escaping RCTPromiseResolveBlock,
    rejecter reject: @escaping RCTPromiseRejectBlock
  ) -> Void {
    
    print(videos)
    for videoAsset in videos {
      print(videoAsset)
    }
    
    let mainComposition = AVMutableComposition()
    let compositionVideoTrack = mainComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
//    compositionVideoTrack?.preferredTransform = CGAffineTransform(rotationAngle: .pi / 2)
    let soundtrackTrack = mainComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)

    var insertTime = CMTime.zero

    for videoAsset in videos {
      let startTime: Double = videoAsset.object(forKey: "startTime") as? Double ?? 0
      let endTime: Double = videoAsset.object(forKey: "endTime") as? Double ?? 0
      let timeRange = CMTimeRange(start: CMTime(seconds: startTime, preferredTimescale: 1000),
                                  end: CMTime(seconds: endTime, preferredTimescale: 1000))
      
      do{
        let source: String = (videoAsset.object(forKey: "source") as? String)!
        
        let asset: AVAsset! = try RNVideoEditorUtilities.requestAsset(source)
        
        let length = Float(asset.duration.value) / Float(asset.duration.timescale)
        
        print("video length: \(length) seconds, \(startTime), \(endTime)")

        
        try! compositionVideoTrack?.insertTimeRange(timeRange, of: asset.tracks(withMediaType: .video)[0], at: insertTime)
      

//        if let audioTrack: AVAssetTrack = asset.tracks(withMediaType: .audio).first{
//          do {
//            try soundtrackTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: asset.duration), of: audioTrack, at: .zero)
//          } catch {
//            return
//          }
//        } else {
//            soundtrackTrack?.insertEmptyTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: CMTime(seconds: Double(length), preferredTimescale: 1000)))
//        }
        
        
        
        
        
//        if let track = asset.tracks(withMediaType: .audio).first {
//
//          do {
//              try soundtrackTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: asset.duration), of: track, at: .zero)
//          } catch {
//              print("error")
//          }
//
//        } else {
//          print("no audio detected")
//            soundtrackTrack?.insertEmptyTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: asset.duration))
//          print("SEAD")
////          do{
////
////          }catch{
////            reject(nil, nil, "No audio in here")
////          }
////            mainComposition.removeTrack(soundtrackTrack!)
////          (timeRange, of: silentAudio, at: insertTime)
//        }
        
        let audioTracks = asset.tracks(withMediaType: .audio)

        for track in audioTracks{

          try! soundtrackTrack?.insertTimeRange(timeRange, of: track, at: insertTime)

        }
      
        
        insertTime = CMTimeAdd(insertTime, timeRange.duration)
        
        print(insertTime)
      }catch{
        reject(nil, nil, error)
      }
      
    }
    
    
    let outputFileURL = URL(fileURLWithPath: NSTemporaryDirectory() + "mergetemp.mp4")

    let fileManager = FileManager()
    try? fileManager.removeItem(at: outputFileURL)

    guard let exporter = AVAssetExportSession(asset: mainComposition, presetName: AVAssetExportPresetHighestQuality) else { return }
   
    exporter.outputURL = outputFileURL
    exporter.outputFileType = AVFileType.mp4
    exporter.shouldOptimizeForNetworkUse = true

    print(outputFileURL.absoluteString)
    
    var exportProgressBarTimer = Timer() // initialize timer
    if #available(iOS 10.0, *) {
      DispatchQueue.main.async {
        exportProgressBarTimer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { timer in
          let progress = Float((exporter.progress));
          if (progress <= 1.0) {
            self.sendEvent(withName: "onIncrement", body: ["progress": progress])
          }
        }
      }
    }
    
    exporter.exportAsynchronously(completionHandler: {
        
      switch exporter.status {
        case .exporting:
            exportProgressBarTimer.invalidate()
            print("test exporting")
        case .completed:
            print("test termino")
            exportProgressBarTimer.invalidate()
            resolve(outputFileURL.absoluteString)
        case .failed:
            exportProgressBarTimer.invalidate()
            reject(nil, nil, "Export failed. \(String(describing: exporter.error))")
        case .cancelled:
            exportProgressBarTimer.invalidate()
            print("test cancelado")
            reject(nil, nil, "Cancelled by user.")
        default:
            print("test fallo")
            reject(nil, nil, exporter.error)
        }
    })
  }
  
  
  @objc func trim(
    _ source: String,
    options: NSDictionary,
    resolver resolve: @escaping RCTPromiseResolveBlock,
    rejecter reject: @escaping RCTPromiseRejectBlock
  ) -> Void {
    do {
      let asset: AVAsset! = try RNVideoEditorUtilities.requestAsset(source)
      let startTime: Double = options.object(forKey: "startTime") as? Double ?? 0
      let endTime: Double = options.object(forKey: "endTime") as? Double ?? 0
      
      let fileName: String = options.object(forKey: "fileName") as? String ?? "tempvideo"
      let fileManager = FileManager.default
      let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]

      
      let length = Float(asset.duration.value) / Float(asset.duration.timescale)
      print("video length: \(length) seconds")

      var outputURL = documentDirectory.appendingPathComponent("output")
      do {
          try fileManager.createDirectory(at: outputURL, withIntermediateDirectories: true, attributes: nil)
          outputURL = outputURL.appendingPathComponent("\(fileName).mp4")
      }catch let error {
          print(error)
      }

      //Remove existing file
      try? fileManager.removeItem(at: outputURL)

      guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) else { return }
      exportSession.outputURL = outputURL
      exportSession.outputFileType = .mp4

      let timeRange = CMTimeRange(start: CMTime(seconds: startTime, preferredTimescale: 1000),
                                  end: CMTime(seconds: endTime, preferredTimescale: 1000))

      exportSession.timeRange = timeRange
      
      
      var exportProgressBarTimer = Timer() // initialize timer
      if #available(iOS 10.0, *) {
        DispatchQueue.main.async {
          exportProgressBarTimer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { timer in
            let progress = Float((exportSession.progress));
            if (progress <= 1.0) {
              self.sendEvent(withName: "onIncrement", body: ["progress": progress])
            }
          }
        }
      }
      
      
      exportSession.exportAsynchronously(completionHandler: {
          switch exportSession.status {
          case .exporting:
              print("test exporting")
          case .completed:
              print("test termino")
              exportProgressBarTimer.invalidate()
              resolve(outputURL.absoluteString)
          case .failed:
              exportProgressBarTimer.invalidate()
              reject(nil, nil, "Export failed.")
          case .cancelled:
              exportProgressBarTimer.invalidate()
              print("test cancelado")
              reject(nil, nil, "Cancelled by user.")
          default:
              print("test fallo")
              reject(nil, nil, "Export failed.")
          }
      })

    } catch {
        reject(nil, nil, error)
    }
  }

  
  
  override func supportedEvents() -> [String]! {
    return ["onIncrement"]
  }
  
  
  
  @objc func getVideoInfo(
      _ source: String,
      resolver resolve: RCTPromiseResolveBlock,
      rejecter reject: RCTPromiseRejectBlock
  ) -> Void {
      do {
          let asset: AVAsset! = try RNVideoEditorUtilities.requestAsset(source)
          var data: [String: Any] = [:]
          
          data["duration"] = asset.duration.seconds
        data["size"] = asset.tracks[0].naturalSize
          
          resolve(data)
      } catch {
          reject(nil, nil, error)
      }
  }
  
  @objc func getVideoOrientationFromAsset(asset : AVAsset) -> UIImage.Orientation {
      let videoTrack: AVAssetTrack? = asset.tracks(withMediaType: .video)[0]
      let size = videoTrack!.naturalSize

      let txf: CGAffineTransform = videoTrack!.preferredTransform

      if (size.width == txf.tx && size.height == txf.ty) {
        return .left;
      } else if (txf.tx == 0 && txf.ty == 0) {
        return .right;
      } else if (txf.tx == 0 && txf.ty == size.width) {
        return .down;
      } else {
        return .up;
      }
    }
  
  
  @objc func getFrameWithDraw(
      _ source: String,
      options: NSDictionary,
      resolver resolve: RCTPromiseResolveBlock,
      rejecter reject: RCTPromiseRejectBlock
  ) -> Void {
      do {
       
        let draw: String = options.object(forKey: "draw") as? String ?? ""
        var second: Double = options.object(forKey: "second") as? Double ?? 0
        
        let url = URL(string: draw)
        let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
        let drawImage = UIImage(data: data!)
        
        
        let asset: AVAsset! = try RNVideoEditorUtilities.requestAsset(source)
        
        let size = asset.tracks[0].naturalSize
        
        if second > Double(asset.duration.seconds) || second < 0 {
            second = 0
        }
        
        let imageGenerator: AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
        imageGenerator.appliesPreferredTrackTransform = true
        
        imageGenerator.maximumSize = CGSize(width: size.width, height: size.height);
        
        let timestamp: CMTime = CMTime(seconds: second, preferredTimescale: 1000)
        
        imageGenerator.requestedTimeToleranceBefore = CMTimeMake(value: 1, timescale: 15)
        imageGenerator.requestedTimeToleranceAfter = CMTimeMake(value: 1, timescale: 15)
        
        let imageRef: CGImage = try imageGenerator.copyCGImage(at: timestamp, actualTime: nil)
        let image: UIImage = UIImage(cgImage: imageRef)
        
        
        UIGraphicsBeginImageContextWithOptions(image.size, false, 0.0)
        image.draw(in: CGRect(x: 0.0, y: 0.0, width: image.size.width, height: image.size.height))
        drawImage!.draw(in: CGRect(x: 0.0, y: 0.0, width: image.size.width, height: image.size.height))
        let result = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        let imgData = result?.jpegData(compressionQuality: 1.0)
        
        let outputURL: URL = try RNVideoEditorUtilities.createTempFile("jpg")
        try imgData?.write(to: outputURL, options: .atomic)
        resolve(outputURL.absoluteString)

        

      } catch {
          reject(nil, nil, error)
      }
  }
  
  @objc func getPictureAtPosition(
      _ source: String,
      options: NSDictionary,
      resolver resolve: RCTPromiseResolveBlock,
      rejecter reject: RCTPromiseRejectBlock
  ) -> Void {
      do {
        let asset: AVAsset! = try RNVideoEditorUtilities.requestAsset(source)
        let format: String = options.object(forKey: "format") as? String ?? "base64"
        var second: Double = options.object(forKey: "second") as? Double ?? 0
        let height: Int = options.object(forKey: "height") as? Int ?? 250
        let width: Int = options.object(forKey: "width") as? Int ?? 360
          if second > Double(asset.duration.seconds) || second < 0 {
              second = 0
          }
          
          let imageGenerator: AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
          imageGenerator.appliesPreferredTrackTransform = true
          imageGenerator.maximumSize = CGSize(width: width, height: height);
          let timestamp: CMTime = CMTime(seconds: second, preferredTimescale: 600)
          
          let imageRef: CGImage = try imageGenerator.copyCGImage(at: timestamp, actualTime: nil)
          let image: UIImage = UIImage(cgImage: imageRef)
          let imgData: Data? = image.jpegData(compressionQuality: 0.5)
          
          if format == "jpg" {
              let outputURL: URL = try RNVideoEditorUtilities.createTempFile("jpg")
              try imgData?.write(to: outputURL, options: .atomic)
              resolve(outputURL.absoluteString)
          } else {
              let base64String: String? = imgData?.base64EncodedString(options: Data.Base64EncodingOptions.init(rawValue: 0))
              resolve(base64String != nil ? "data:image/png;base64,\(base64String!)" : "")
          }
      } catch {
          reject(nil, nil, error)
      }
  }
  
  @objc func getPictures(
      _ source: String,
      resolver resolve: @escaping RCTPromiseResolveBlock,
      rejecter reject: RCTPromiseRejectBlock
  ) -> Void {
      do {
          let asset: AVAsset! = try RNVideoEditorUtilities.requestAsset(source)
          
          var numberOfPictures: Double = 10
          let duration: Double = asset.duration.seconds
          if duration > 30 {
              numberOfPictures = 2 * (floor((duration / 30) + 1))
          }
          let second: Double = floor(duration / numberOfPictures)
          
          let imageGenerator: AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
          
          imageGenerator.appliesPreferredTrackTransform = true
          imageGenerator.maximumSize = CGSize(width: 150, height: 150);
          var timeValues: [NSValue] = []
          var pictures: Array<String> = []
        
          for n in 0..<Int(numberOfPictures) {
            let timestamp: CMTime = CMTime(seconds: (Double(n) * second), preferredTimescale: 600)
            let cmValue = NSValue(time: timestamp)
            timeValues.append(cmValue)
          }
        
          let dispatchGroup = DispatchGroup()
        
          timeValues.forEach { _ in
              dispatchGroup.enter()
          }
        
          imageGenerator.generateCGImagesAsynchronously(forTimes: timeValues) { (requestedTime, cgImage, actualTime, result, error) in
              if let cgImage = cgImage {
                    //print("Requested: \(requestedTime.seconds), Actual: \(actualTime.seconds)")
                    let image = UIImage(cgImage: cgImage)
                    let imgData: Data? = image.jpegData(compressionQuality: 0.2)
                    
                    let base64String: String? = imgData?.base64EncodedString(options: Data.Base64EncodingOptions.init(rawValue: 0))
                    let picture = base64String != nil ? "data:image/png;base64,\(base64String!)" : ""
                    // scale image if you want
                    pictures.append(picture)
                    dispatchGroup.leave()
              }
          }
        
          dispatchGroup.notify(queue: .main) {
            resolve(pictures)
          }
        
//          for n in 0..<Int(numberOfPictures) {
//              let timestamp: CMTime = CMTime(seconds: (Double(n) * second), preferredTimescale: 600)
//              let imageRef: CGImage = try imageGenerator.copyCGImage(at: timestamp, actualTime: nil)
//              let image: UIImage = UIImage(cgImage: imageRef)
//              let imgData: Data? = image.jpegData(compressionQuality: 0.2)
//
//              let base64String: String? = imgData?.base64EncodedString(options: Data.Base64EncodingOptions.init(rawValue: 0))
//              let picture = base64String != nil ? "data:image/png;base64,\(base64String!)" : ""
//              pictures.append(picture)
//          }
          
      } catch {
          reject(nil, nil, error)
      }
  }
  

  @objc func imageToVideo(
    _ source: String,
    duration: Double,
    name: String,
    resolver resolve: @escaping RCTPromiseResolveBlock,
    rejecter reject: @escaping RCTPromiseRejectBlock
  ) -> Void {
        
      let outputSize = CGSize(width: 1920, height: 1280)
//      var asset: AVAsset!
//      let audioIsEnabled: Bool = false //if your video has no sound
    
      let url = URL(string: source)
      let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
      let drawImage = UIImage(data: data!)?.cgImage

      let outputURL = NSURL(fileURLWithPath: NSHomeDirectory() + "/Documents/" + name + ".MP4")
    
      removeFileAtURLIfExists(url: outputURL)
    
      guard let videoWriter = try? AVAssetWriter(outputURL: outputURL as URL, fileType: AVFileType.mp4) else {
            fatalError("AVAssetWriter error")
        }
      let outputSettings = [AVVideoCodecKey : AVVideoCodecType.h264, AVVideoWidthKey : NSNumber(value: Float(outputSize.width)), AVVideoHeightKey : NSNumber(value: Float(outputSize.height))] as [String : Any]
      guard videoWriter.canApply(outputSettings: outputSettings, forMediaType: AVMediaType.video) else {
            fatalError("Negative : Can't apply the Output settings...")
        }
      let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: outputSettings)
      let sourcePixelBufferAttributesDictionary = [kCVPixelBufferPixelFormatTypeKey as String : NSNumber(value: kCVPixelFormatType_32ARGB), kCVPixelBufferWidthKey as String: NSNumber(value: Float(outputSize.width)), kCVPixelBufferHeightKey as String: NSNumber(value: Float(outputSize.height))]
      let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriterInput, sourcePixelBufferAttributes: sourcePixelBufferAttributesDictionary)
      
    
      if videoWriter.canAdd(videoWriterInput) {
          videoWriter.add(videoWriterInput)
      }
    
      if videoWriter.startWriting() {
        
        let zeroTime = CMTime(seconds: duration, preferredTimescale: 1000)
        videoWriter.startSession(atSourceTime: zeroTime)
        assert(pixelBufferAdaptor.pixelBufferPool != nil)
        
      }
    
    
      var pixelBufferCreated = true
      var pixelBuffer: CVPixelBuffer? = nil
      let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pixelBufferAdaptor.pixelBufferPool!, &pixelBuffer)

      if let pixelBuffer = pixelBuffer, status == 0 {
          let managedPixelBuffer = pixelBuffer
          CVPixelBufferLockBaseAddress(managedPixelBuffer, CVPixelBufferLockFlags(rawValue: CVOptionFlags(0)))

          let data = CVPixelBufferGetBaseAddress(managedPixelBuffer)
          let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
          let context = CGContext(data: data, width: Int(outputSize.width), height: Int(outputSize.height), bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(managedPixelBuffer), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue)
          
          context!.clear(CGRect(x: 0, y: 0, width: CGFloat(outputSize.width), height: CGFloat(outputSize.height)))

          context?.draw(drawImage!, in: CGRect(x: 0, y: 0, width: outputSize.width, height: outputSize.height))

          CVPixelBufferUnlockBaseAddress(managedPixelBuffer, CVPixelBufferLockFlags(rawValue: CVOptionFlags(0)))
      } else {
          print("Failed to allocate pixel buffer")
          pixelBufferCreated = false
      }
    
      if (pixelBufferCreated) {
        var appendSucceeded = pixelBufferAdaptor.append(pixelBuffer!, withPresentationTime: CMTime.zero);
        if (!appendSucceeded) {
                    // something went wrong, up to you to handle. Should probably return so the rest of the code is not executed though
        }
        
        while !videoWriterInput.isReadyForMoreMediaData {}
        
        let frameTime: CMTime = CMTime(seconds: duration, preferredTimescale: 1000)
//          CMTimeMake(value: Double(duration), timescale: 1)
        
        
        appendSucceeded = pixelBufferAdaptor.append(pixelBuffer!, withPresentationTime: frameTime);
        if (!appendSucceeded) {
            // something went wrong, up to you to handle. Should probably return so the rest of the code is not executed though
        }
        
        videoWriterInput.markAsFinished()
        videoWriter.finishWriting(completionHandler: { () -> Void in
          
          if videoWriter.status != .completed {
                          // Error writing the video... handle appropriately
          } else {
              print("FINISHED!!!!!")
              print("-----video1 url = \(outputURL)")
              resolve(outputURL.absoluteString)
          }
            
            
            // self.asset = AVAsset(url: self.imageArrayToVideoURL as URL)
            // self.exportVideoWithAnimation()
          
        })
      }
    
  }
  

  func removeFileAtURLIfExists(url: NSURL) {
      if let filePath = url.path {
          let fileManager = FileManager.default
          if fileManager.fileExists(atPath: filePath) {
              do{
                  print("REMOVED")
                  try fileManager.removeItem(atPath: filePath)
              } catch let error as NSError {
                  print("Couldn't remove existing destination file: \(error)")
              }
          }
      }
  }
  
  
//  func exportVideoWithAnimation() {
//          let composition = AVMutableComposition()
//
//    let track =  asset?.tracks(withMediaType: AVMediaType.video)
//          let videoTrack:AVAssetTrack = track![0] as AVAssetTrack
//    let timerange = CMTimeRangeMake(start: CMTime.zero, duration: (asset?.duration)!)
//
//    let compositionVideoTrack:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: CMPersistentTrackID())!
//
//          do {
//            try compositionVideoTrack.insertTimeRange(timerange, of: videoTrack, at: CMTime.zero)
//              compositionVideoTrack.preferredTransform = videoTrack.preferredTransform
//          } catch {
//              print(error)
//          }
//
//          //if your video has sound, you don’t need to check this
//          if audioIsEnabled {
//            let compositionAudioTrack:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: CMPersistentTrackID())!
//
//            for audioTrack in (asset?.tracks(withMediaType: AVMediaType.audio))! {
//                  do {
//                    try compositionAudioTrack.insertTimeRange(audioTrack.timeRange, of: audioTrack, at: CMTime.zero)
//                  } catch {
//                      print(error)
//                  }
//              }
//          }
//
//          let size = videoTrack.naturalSize
//
//          let videolayer = CALayer()
//          videolayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
//
//          let parentlayer = CALayer()
//          parentlayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
//          parentlayer.addSublayer(videolayer)
//
//
//          //this is the animation part
//          var time = [0.00001, 3, 6, 9, 12] //I used this time array to determine the start time of a frame animation. Each frame will stay for 3 secs, thats why their difference is 3
//          var imgarray = [UIImage]()
//
//          for image in 0..<5 {
//              imgarray.append(UIImage(named: "\(image + 1).JPG")!)
//
//              let nextPhoto = imgarray[image]
//
//              let horizontalRatio = CGFloat(self.outputSize.width) / nextPhoto.size.width
//              let verticalRatio = CGFloat(self.outputSize.height) / nextPhoto.size.height
//              let aspectRatio = min(horizontalRatio, verticalRatio)
//              let newSize: CGSize = CGSize(width: nextPhoto.size.width * aspectRatio, height: nextPhoto.size.height * aspectRatio)
//              let x = newSize.width < self.outputSize.width ? (self.outputSize.width - newSize.width) / 2 : 0
//              let y = newSize.height < self.outputSize.height ? (self.outputSize.height - newSize.height) / 2 : 0
//
//              ///I showed 10 animations here. You can uncomment any of this and export a video to see the result.
//
//              ///#1. left->right///
//              let blackLayer = CALayer()
//              blackLayer.frame = CGRect(x: -videoTrack.naturalSize.width, y: 0, width: videoTrack.naturalSize.width, height: videoTrack.naturalSize.height)
//              blackLayer.backgroundColor = UIColor.black.cgColor
//
//              let imageLayer = CALayer()
//              imageLayer.frame = CGRect(x: x, y: y, width: newSize.width, height: newSize.height)
//              imageLayer.contents = imgarray[image].cgImage
//              blackLayer.addSublayer(imageLayer)
//
//              let animation = CABasicAnimation()
//              animation.keyPath = "position.x"
//              animation.fromValue = -videoTrack.naturalSize.width
//              animation.toValue = 2 * (videoTrack.naturalSize.width)
//              animation.duration = 3
//              animation.beginTime = CFTimeInterval(time[image])
//            animation.fillMode = CAMediaTimingFillMode.forwards
//              animation.isRemovedOnCompletion = false
//              blackLayer.add(animation, forKey: "basic")
//
//              ///#2. right->left///
//  //            let blackLayer = CALayer()
//  //            blackLayer.frame = CGRect(x: 2 * videoTrack.naturalSize.width, y: 0, width: videoTrack.naturalSize.width, height: videoTrack.naturalSize.height)
//  //            blackLayer.backgroundColor = UIColor.black.cgColor
//  //
//  //            let imageLayer = CALayer()
//  //            imageLayer.frame = CGRect(x: x, y: y, width: newSize.width, height: newSize.height)
//  //            imageLayer.contents = imgarray[image].cgImage
//  //            blackLayer.addSublayer(imageLayer)
//  //
//  //            let animation = CABasicAnimation()
//  //            animation.keyPath = "position.x"
//  //            animation.fromValue = 2 * (videoTrack.naturalSize.width)
//  //            animation.toValue = -videoTrack.naturalSize.width
//  //            animation.duration = 3
//  //            animation.beginTime = CFTimeInterval(time[image])
//  //            animation.fillMode = kCAFillModeForwards
//  //            animation.isRemovedOnCompletion = false
//  //            blackLayer.add(animation, forKey: "basic")
//
//              ///#3. top->bottom///
//  //            let blackLayer = CALayer()
//  //            blackLayer.frame = CGRect(x: 0, y: 2 * videoTrack.naturalSize.height, width: videoTrack.naturalSize.width, height: videoTrack.naturalSize.height)
//  //            blackLayer.backgroundColor = UIColor.black.cgColor
//  //
//  //            let imageLayer = CALayer()
//  //            imageLayer.frame = CGRect(x: x, y: y, width: newSize.width, height: newSize.height)
//  //            imageLayer.contents = imgarray[image].cgImage
//  //            blackLayer.addSublayer(imageLayer)
//  //
//  //            let animation = CABasicAnimation()
//  //            animation.keyPath = "position.y"
//  //            animation.fromValue = 2 * videoTrack.naturalSize.height
//  //            animation.toValue = -videoTrack.naturalSize.height
//  //            animation.duration = 3
//  //            animation.beginTime = CFTimeInterval(time[image])
//  //            animation.fillMode = kCAFillModeForwards
//  //            animation.isRemovedOnCompletion = false
//  //            blackLayer.add(animation, forKey: "basic")
//
//              ///#4. bottom->top///
//  //            let blackLayer = CALayer()
//  //            blackLayer.frame = CGRect(x: 0, y: -videoTrack.naturalSize.height, width: videoTrack.naturalSize.width, height: videoTrack.naturalSize.height)
//  //            blackLayer.backgroundColor = UIColor.black.cgColor
//  //
//  //            let imageLayer = CALayer()
//  //            imageLayer.frame = CGRect(x: x, y: y, width: newSize.width, height: newSize.height)
//  //            imageLayer.contents = imgarray[image].cgImage
//  //            blackLayer.addSublayer(imageLayer)
//  //
//  //            let animation = CABasicAnimation()
//  //            animation.keyPath = "position.y"
//  //            animation.fromValue = -videoTrack.naturalSize.height
//  //            animation.toValue = 2 * videoTrack.naturalSize.height
//  //            animation.duration = 3
//  //            animation.beginTime = CFTimeInterval(time[image])
//  //            animation.fillMode = kCAFillModeForwards
//  //            animation.isRemovedOnCompletion = false
//  //            blackLayer.add(animation, forKey: "basic")
//
//              ///#5. opacity(1->0)(left->right)///
//  //            let blackLayer = CALayer()
//  //            blackLayer.frame = CGRect(x: -videoTrack.naturalSize.width, y: 0, width: videoTrack.naturalSize.width, height: videoTrack.naturalSize.height)
//  //            blackLayer.backgroundColor = UIColor.black.cgColor
//  //
//  //            let imageLayer = CALayer()
//  //            imageLayer.frame = CGRect(x: x, y: y, width: newSize.width, height: newSize.height)
//  //            imageLayer.contents = imgarray[image].cgImage
//  //            blackLayer.addSublayer(imageLayer)
//  //
//  //            let animation = CABasicAnimation()
//  //            animation.keyPath = "position.x"
//  //            animation.fromValue = -videoTrack.naturalSize.width
//  //            animation.toValue = 2 * (videoTrack.naturalSize.width)
//  //            animation.duration = 3
//  //            animation.beginTime = CFTimeInterval(time[image])
//  //            animation.fillMode = kCAFillModeForwards
//  //            animation.isRemovedOnCompletion = false
//  //            blackLayer.add(animation, forKey: "basic")
//  //
//  //            let fadeOutAnimation = CABasicAnimation(keyPath: "opacity")
//  //            fadeOutAnimation.fromValue = 1
//  //            fadeOutAnimation.toValue = 0
//  //            fadeOutAnimation.duration = 3
//  //            fadeOutAnimation.beginTime = CFTimeInterval(time[image])
//  //            fadeOutAnimation.isRemovedOnCompletion = false
//  //            blackLayer.add(fadeOutAnimation, forKey: "opacity")
//
//              ///#6. opacity(1->0)(right->left)///
//  //            let blackLayer = CALayer()
//  //            blackLayer.frame = CGRect(x: 2 * videoTrack.naturalSize.width, y: 0, width: videoTrack.naturalSize.width, height: videoTrack.naturalSize.height)
//  //            blackLayer.backgroundColor = UIColor.black.cgColor
//  //
//  //            let imageLayer = CALayer()
//  //            imageLayer.frame = CGRect(x: x, y: y, width: newSize.width, height: newSize.height)
//  //            imageLayer.contents = imgarray[image].cgImage
//  //            blackLayer.addSublayer(imageLayer)
//  //
//  //            let animation = CABasicAnimation()
//  //            animation.keyPath = "position.x"
//  //            animation.fromValue = 2 * videoTrack.naturalSize.width
//  //            animation.toValue = -videoTrack.naturalSize.width
//  //            animation.duration = 3
//  //            animation.beginTime = CFTimeInterval(time[image])
//  //            animation.fillMode = kCAFillModeForwards
//  //            animation.isRemovedOnCompletion = false
//  //            blackLayer.add(animation, forKey: "basic")
//  //
//  //            let fadeOutAnimation = CABasicAnimation(keyPath: "opacity")
//  //            fadeOutAnimation.fromValue = 1
//  //            fadeOutAnimation.toValue = 0
//  //            fadeOutAnimation.duration = 3
//  //            fadeOutAnimation.beginTime = CFTimeInterval(time[image])
//  //            fadeOutAnimation.isRemovedOnCompletion = false
//  //            blackLayer.add(fadeOutAnimation, forKey: "opacity")
//
//              ///#7. opacity(1->0)(top->bottom)///
//  //            let blackLayer = CALayer()
//  //            blackLayer.frame = CGRect(x: 0, y: 2 * videoTrack.naturalSize.height, width: videoTrack.naturalSize.width, height: videoTrack.naturalSize.height)
//  //            blackLayer.backgroundColor = UIColor.black.cgColor
//  //
//  //            let imageLayer = CALayer()
//  //            imageLayer.frame = CGRect(x: x, y: y, width: newSize.width, height: newSize.height)
//  //            imageLayer.contents = imgarray[image].cgImage
//  //            blackLayer.addSublayer(imageLayer)
//  //
//  //            let animation = CABasicAnimation()
//  //            animation.keyPath = "position.y"
//  //            animation.fromValue = 2 * videoTrack.naturalSize.height
//  //            animation.toValue = -videoTrack.naturalSize.height
//  //            animation.duration = 3
//  //            animation.beginTime = CFTimeInterval(time[image])
//  //            animation.fillMode = kCAFillModeForwards
//  //            animation.isRemovedOnCompletion = false
//  //            blackLayer.add(animation, forKey: "basic")
//  //
//  //            let fadeOutAnimation = CABasicAnimation(keyPath: "opacity")
//  //            fadeOutAnimation.fromValue = 1
//  //            fadeOutAnimation.toValue = 0
//  //            fadeOutAnimation.duration = 3
//  //            fadeOutAnimation.beginTime = CFTimeInterval(time[image])
//  //            fadeOutAnimation.isRemovedOnCompletion = false
//  //            blackLayer.add(fadeOutAnimation, forKey: "opacity")
//
//              ///#8. opacity(1->0)(bottom->top)///
//  //            let blackLayer = CALayer()
//  //            blackLayer.frame = CGRect(x: 0, y: -videoTrack.naturalSize.height, width: videoTrack.naturalSize.width, height: videoTrack.naturalSize.height)
//  //            blackLayer.backgroundColor = UIColor.black.cgColor
//  //
//  //            let imageLayer = CALayer()
//  //            imageLayer.frame = CGRect(x: x, y: y, width: newSize.width, height: newSize.height)
//  //            imageLayer.contents = imgarray[image].cgImage
//  //            blackLayer.addSublayer(imageLayer)
//  //
//  //            let animation = CABasicAnimation()
//  //            animation.keyPath = "position.y"
//  //            animation.fromValue = -videoTrack.naturalSize.height
//  //            animation.toValue = 2 * videoTrack.naturalSize.height
//  //            animation.duration = 3
//  //            animation.beginTime = CFTimeInterval(time[image])
//  //            animation.fillMode = kCAFillModeForwards
//  //            animation.isRemovedOnCompletion = false
//  //            blackLayer.add(animation, forKey: "basic")
//  //
//  //            let fadeOutAnimation = CABasicAnimation(keyPath: "opacity")
//  //            fadeOutAnimation.fromValue = 1
//  //            fadeOutAnimation.toValue = 0
//  //            fadeOutAnimation.duration = 3
//  //            fadeOutAnimation.beginTime = CFTimeInterval(time[image])
//  //            fadeOutAnimation.isRemovedOnCompletion = false
//  //            blackLayer.add(fadeOutAnimation, forKey: "opacity")
//
//              ///#9. scale(small->big->small)///
//  //            let blackLayer = CALayer()
//  //            blackLayer.frame = CGRect(x: 0, y: 0, width: videoTrack.naturalSize.width, height: videoTrack.naturalSize.height)
//  //            blackLayer.backgroundColor = UIColor.black.cgColor
//  //            blackLayer.opacity = 0
//  //
//  //            let imageLayer = CALayer()
//  //            imageLayer.frame = CGRect(x: x, y: y, width: newSize.width, height: newSize.height)
//  //            imageLayer.contents = imgarray[image].cgImage
//  //            blackLayer.addSublayer(imageLayer)
//  //
//  //            let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
//  //            scaleAnimation.values = [0, 1.0, 0]
//  //            scaleAnimation.beginTime = CFTimeInterval(time[image])
//  //            scaleAnimation.duration = 3
//  //            scaleAnimation.isRemovedOnCompletion = false
//  //            blackLayer.add(scaleAnimation, forKey: "transform.scale")
//  //
//  //            let fadeInOutAnimation = CABasicAnimation(keyPath: "opacity")
//  //            fadeInOutAnimation.fromValue = 1
//  //            fadeInOutAnimation.toValue = 1
//  //            fadeInOutAnimation.duration = 3
//  //            fadeInOutAnimation.beginTime = CFTimeInterval(time[image])
//  //            fadeInOutAnimation.isRemovedOnCompletion = false
//  //            blackLayer.add(fadeInOutAnimation, forKey: "opacity")
//
//              ///#10. scale(big->small->big)///
//  //            let blackLayer = CALayer()
//  //            blackLayer.frame = CGRect(x: 0, y: 0, width: videoTrack.naturalSize.width, height: videoTrack.naturalSize.height)
//  //            blackLayer.backgroundColor = UIColor.black.cgColor
//  //            blackLayer.opacity = 0
//  //
//  //            let imageLayer = CALayer()
//  //            imageLayer.frame = CGRect(x: x, y: y, width: newSize.width, height: newSize.height)
//  //            imageLayer.contents = imgarray[image].cgImage
//  //            blackLayer.addSublayer(imageLayer)
//  //
//  //            let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
//  //            scaleAnimation.values = [1, 0, 1]
//  //            scaleAnimation.beginTime = CFTimeInterval(time[image])
//  //            scaleAnimation.duration = 3
//  //            scaleAnimation.isRemovedOnCompletion = false
//  //            blackLayer.add(scaleAnimation, forKey: "transform.scale")
//  //
//  //            let fadeOutAnimation = CABasicAnimation(keyPath: "opacity")
//  //            fadeOutAnimation.fromValue = 1
//  //            fadeOutAnimation.toValue = 1
//  //            fadeOutAnimation.duration = 3
//  //            fadeOutAnimation.beginTime = CFTimeInterval(time[image])
//  //            fadeOutAnimation.isRemovedOnCompletion = false
//  //            blackLayer.add(fadeOutAnimation, forKey: "opacity")
//
//          parentlayer.addSublayer(blackLayer)
//      }
//      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//      let layercomposition = AVMutableVideoComposition()
//      layercomposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
//      layercomposition.renderSize = size
//      layercomposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videolayer, in: parentlayer)
//      let instruction = AVMutableVideoCompositionInstruction()
//      instruction.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: CMTime.zero)
//      let videotrack = composition.tracks(withMediaType: AVMediaType.video)[0] as AVAssetTrack
//      let layerinstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videotrack)
//      instruction.layerInstructions = [layerinstruction]
//      layercomposition.instructions = [instruction]
//
//      let animatedVideoURL = NSURL(fileURLWithPath: NSHomeDirectory() + "/Documents/video2.mp4")
//      removeFileAtURLIfExists(url: animatedVideoURL)
//
//      guard let assetExport = AVAssetExportSession(asset: composition, presetName:AVAssetExportPresetHighestQuality) else {return}
//      assetExport.videoComposition = layercomposition
//      assetExport.outputFileType = AVFileType.mp4
//      assetExport.outputURL = animatedVideoURL as URL
//      assetExport.exportAsynchronously(completionHandler: {
//          switch assetExport.status{
//          case  AVAssetExportSessionStatus.failed:
//              print("failed \(String(describing: assetExport.error))")
//          case AVAssetExportSessionStatus.cancelled:
//              print("cancelled \(String(describing: assetExport.error))")
//          default:
//              print("Exported")
//          }
//      })
//  }


  func createSilentAudio(
    startFrm: Int64,
    nFrames: Int,
    sampleRate: Float64,
    numChannels: UInt32
  ) -> CMSampleBuffer? {
          let bytesPerFrame = UInt32(2 * numChannels)
          let blockSize = nFrames*Int(bytesPerFrame)

          var block: CMBlockBuffer?
          var status = CMBlockBufferCreateWithMemoryBlock(
              allocator: kCFAllocatorDefault,
              memoryBlock: nil,
              blockLength: blockSize,
              blockAllocator: nil,
              customBlockSource: nil,
              offsetToData: 0,
              dataLength: blockSize,
              flags: 0,
              blockBufferOut: &block
          )
          assert(status == kCMBlockBufferNoErr)

          guard var eBlock = block else { return nil }

          // we seem to get zeros from the above, but I can't find it documented. so... memset:
          status = CMBlockBufferFillDataBytes(with: 0, blockBuffer: eBlock, offsetIntoDestination: 0, dataLength: blockSize)
          assert(status == kCMBlockBufferNoErr)


          var asbd = AudioStreamBasicDescription(
              mSampleRate: sampleRate,
              mFormatID: kAudioFormatLinearPCM,
              mFormatFlags: kLinearPCMFormatFlagIsSignedInteger,
              mBytesPerPacket: bytesPerFrame,
              mFramesPerPacket: 1,
              mBytesPerFrame: bytesPerFrame,
              mChannelsPerFrame: numChannels,
              mBitsPerChannel: 16,
              mReserved: 0
          )

          var formatDesc: CMAudioFormatDescription?
          status = CMAudioFormatDescriptionCreate(allocator: kCFAllocatorDefault, asbd: &asbd, layoutSize: 0, layout: nil, magicCookieSize: 0, magicCookie: nil, extensions: nil, formatDescriptionOut: &formatDesc)
          assert(status == noErr)

          var sampleBuffer: CMSampleBuffer?

          status = CMAudioSampleBufferCreateReadyWithPacketDescriptions(
              allocator: kCFAllocatorDefault,
              dataBuffer: eBlock,
              formatDescription: formatDesc!,
              sampleCount: nFrames,
              presentationTimeStamp: CMTimeMake(value: startFrm, timescale: Int32(sampleRate)),
              packetDescriptions: nil,
              sampleBufferOut: &sampleBuffer
          )
          assert(status == noErr)
          return sampleBuffer
      }


}
