//
//  RNDragDropView + DropExtension.swift
//  Example
//
//  Created by Sam Ding on 11/15/20.
//

import Foundation
import UIKit
import MobileCoreServices

extension DropZone: UIDropInteractionDelegate {
  func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool {
    return session.hasItemsConforming(toTypeIdentifiers: self.allowedTypeIdentifiers)
  }
  
  func dropInteraction(_ interaction: UIDropInteraction, sessionDidEnter session: UIDropSession) {
    let dropPoint = session.location(in: self)
    print("Enter")
    self.onDragEnter?([
      "x": NSNumber(value: Double(dropPoint.x)),
      "y": NSNumber(value: Double(dropPoint.y))
    ])
  }
  
  func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
    let dropPoint = session.location(in: self)
    self.onDragOver?([
      "x": NSNumber(value: Double(dropPoint.x)),
      "y": NSNumber(value: Double(dropPoint.y))
    ])
    return UIDropProposal(operation: .copy)
  }
  
  func dropInteraction(_ interaction: UIDropInteraction, sessionDidExit session: UIDropSession) {
    self.onDragLeave?([:])
  }
  
  func dropInteraction(_ interaction: UIDropInteraction, sessionDidEnd session: UIDropSession) {
    self.onDragExit?([:])
  }
  
  func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
    /// Wait for all UIDragItems to be processed, then notify to pass data to React Native
    let dispatchGroup = DispatchGroup()
    
    let dropPoint = session.location(in: self)
    let dropPointData = [
      "x": NSNumber(value: Double(dropPoint.x)),
      "y": NSNumber(value: Double(dropPoint.y))
    ]
    
    self.onProcessingDrop?(dropPointData)
    
    items = ["dropPoint": dropPointData,
             "files": [NSString](),
             "texts": [NSString](),
             "urls" : [NSString]()
    ]
    
    outerLoop: for item in session.items {
      let provider = item.itemProvider
      
      // Image
      if provider.hasItemConformingToTypeIdentifier(DataType.image.baseUTType){
        self.loadItemAsGenericFile(provider, withUTI: DataType.image.baseUTType, in: dispatchGroup)
        continue outerLoop
      }
      
      // Video
      if provider.hasItemConformingToTypeIdentifier(DataType.video.baseUTType) {
        self.loadItemAsGenericFile(provider, withUTI: DataType.video.baseUTType, in: dispatchGroup)
        continue outerLoop
      }
      
      // Audio
      if provider.hasItemConformingToTypeIdentifier(DataType.audio.baseUTType) {
        self.loadItemAsGenericFile(provider, withUTI: DataType.audio.baseUTType, in: dispatchGroup)
        continue outerLoop
      }
      
      // URL
      if provider.hasItemConformingToTypeIdentifier(DataType.url.baseUTType) {
        self.loadItemsAsURL(provider, in: dispatchGroup)
        continue outerLoop
      }
      
      // Text
      if provider.hasItemConformingToTypeIdentifier(DataType.text.baseUTType) {
        self.loadItemsAsText(provider, in: dispatchGroup)
        continue outerLoop
      }
      
      // Other UTIs
      self.loadItemAsGenericFile(provider, withUTI: provider.registeredTypeIdentifiers[0], in: dispatchGroup)
      
    }
    
    dispatchGroup.notify(queue: .main, execute: { [weak self] in
      self?.onDrop?(self?.items)
    })
    
  }
  
  /// Load provider items as strings
  private func loadItemsAsText( _ provider: NSItemProvider, in dispatchGroup: DispatchGroup){
    dispatchGroup.enter()
    provider.loadObject(ofClass: NSString.self) { [weak self] text, error in
      if let text: NSString = text as? NSString {
        // Add the text to the items
        if let textArr = self?.items["texts"] as? [NSString]{
          self?.items["texts"] = textArr + [text]
        } else {
          self?.items["texts"] = [text]
        }
      } else {
        print("Unable to read Item as String")
      }
      dispatchGroup.leave()
    }
  }
  
  /// Load provider items as urls
  private func loadItemsAsURL(_ provider: NSItemProvider, in dispatchGroup: DispatchGroup){
    dispatchGroup.enter()
    provider.loadObject(ofClass: NSURL.self){ [weak self] url, error in
      if let url = url as? NSURL {
        // Add the url to the items
        guard let urlString = url.absoluteString else {
          dispatchGroup.leave()
          return;
        }
        if let urlArr = self?.items["urls"] as? [NSString]{
          self?.items["urls"] = urlArr + [urlString]
        } else {
          self?.items["urls"] = [urlString]
        }
      } else {
        print("Unable to read Item as URL")
      }
      dispatchGroup.leave()
    }
  }
  
  private func loadItemAsGenericFile(_ provider: NSItemProvider, withUTI uti: String, in dispatchGroup: DispatchGroup){
    dispatchGroup.enter()
    let tempDirectURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent("file.ReactNativeDropZone")
    
    provider.loadFileRepresentation(forTypeIdentifier: uti) { [weak self] url, error in
      if let url = url{
        do {
          try FileManager.default.createDirectory(at: tempDirectURL, withIntermediateDirectories: true, attributes: nil)
          let fileName = url.pathComponents.last;
          
          let accessedURL = tempDirectURL
             .appendingPathComponent(fileName!)

          do {
            if (!FileManager.default.fileExists(atPath: accessedURL.path)){
                // Move items from provided url to a custom url because
                // react native cannot access the original url from NSItemProvider
              try FileManager.default.moveItem(at: url, to: accessedURL)
            }
            // Add the url to the items
            let urlString = accessedURL.absoluteString
            if let urlArr = self?.items["files"] as? [NSString]{
              self?.items["files"] = urlArr + [urlString]
            } else {
              self?.items["files"] = [urlString]
            }
             
          } catch let error as NSError {
             print("Unable to create file due to \(error.localizedDescription)")
          }
          
        } catch let error as NSError{
          print("Unable to create directory due to \(error.localizedDescription)")
        }

      } else {
        print("Unable to read Item as URL")
      }
      dispatchGroup.leave()
    }
  }
  

}

