//
//  DragView.swift
//  Example
//
//  Created by Sam Ding on 12/18/20.
//

import Foundation
import UIKit

@objc(DragView)
class DragView: UIView {
  
  // MARK: - Variables
  /// Drag action begins
  @objc var onDragBegin: RCTBubblingEventBlock?
  /// Drag action moves
  @objc var onDragOver: RCTBubblingEventBlock?
  /// Drag action ends
  @objc var onDragEnd: RCTBubblingEventBlock?
  /// Content of drag items
  @objc var context: [String]?
  
  private var previousReactSubview: UIView?
  private var previousSubviewIndex: Int?
  private var _reactSubviews: [UIView]?
  
  // MARK: - Intializers
  override init(frame: CGRect) {
    super.init(frame: frame)
    self.addInteraction(UIDragInteraction(delegate: self))
    
    previousReactSubview = nil
    previousSubviewIndex = -1
    _reactSubviews = []
  }
  
  required init?(coder: NSCoder) {
    super.init(coder: coder)
  }
  
  override func insertReactSubview(_ subview: UIView!, at atIndex: Int) {
    if subview is DragPreview {
      // Don't add the preview to the actual view
      previousSubviewIndex = atIndex
      previousReactSubview = subview
    } else {
      // Present actual views, excluding preview
      guard let previousSubviewIndex = previousSubviewIndex else { return }
      let index = (previousSubviewIndex != -1 && atIndex > previousSubviewIndex) ? atIndex - 1 : atIndex
      if let subview = subview {
         super.insertSubview(subview, at: index)
      }
    }
    
    if let subview = subview  {
      _reactSubviews?.insert(subview, at: atIndex)
    }
  }
  
  override func removeReactSubview(_ subview: UIView!) {
    if subview is DragPreview {
      previousSubviewIndex = -1
      previousReactSubview = nil
    } else {
      subview.removeFromSuperview()
    }
    // Remove subview if the pointer refer to the same object
    _reactSubviews?.removeAll(where: { $0 === subview })
  }
  
  override func reactSubviews() -> [UIView]! {
    return _reactSubviews!
  }
  
}

// MARK: - Drag Interaction Delegate Ectension
extension DragView: UIDragInteractionDelegate {
  func dragInteraction(_ interaction: UIDragInteraction, sessionWillBegin session: UIDragSession) {
    self.onDragBegin?([:])
  }
  
  func dragInteraction(_ interaction: UIDragInteraction, sessionDidMove session: UIDragSession) {
    let dropPoint = session.location(in: self)
    self.onDragOver?([
      "x": NSNumber(value: Double(dropPoint.x)),
      "y": NSNumber(value: Double(dropPoint.y))
    ])
  }
  
  func dragInteraction(_ interaction: UIDragInteraction, session: UIDragSession, didEndWith operation: UIDropOperation) {
    self.onDragEnd?([:])
  }
  
  func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
    var dragItems: [UIDragItem] = []
    guard let context = context else { return [] }
    for item in context {
      var itemProvider = NSItemProvider(object: item as NSString)
      
      if item.isValidatedImage() {
        // Image
        guard let url = URL(string: item) else { return dragItems }
        let data = try? Data(contentsOf: url)
        if let imageData = data {
          itemProvider = NSItemProvider(object: UIImage(data: imageData)!)
        }
      } else if item.isValidatedURL() {
        // URL
        guard let url = URL(string: item) else { return dragItems }
        itemProvider = NSItemProvider(object: url as NSURL)
      }

      let dragItem = UIDragItem(itemProvider: itemProvider)
      if let previousReactSubview = previousReactSubview {
        dragItem.previewProvider = { return UIDragPreview(view: previousReactSubview) }
      }
      dragItems.append(dragItem)
    }
    return dragItems
  }
}

extension String{
  /**
   Check if a string is a path to an image
   */
  func isValidatedImage() -> Bool{
    if (self.isValidatedURL()){
      guard let url = URL(string: self), !url.pathExtension.isEmpty else {
        return false
      }
      return ["jpg", "jpeg", "png", "gif"].contains(url.pathExtension.lowercased())
    }
    return false
  }
  
  /**
   Check if a string is an url
   */
  func isValidatedURL() -> Bool{
    let types: NSTextCheckingResult.CheckingType = [.link]
    let detector = try? NSDataDetector(types: types.rawValue)
    guard (detector != nil && self.count > 0) else { return false }
    if detector!.numberOfMatches(in: self, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, self.count)) > 0 {
        return true
    }
    return false
  }
}
