//
//  RCTCPDFView.swift
//  react-native-compdfkit-pdf
//
//  Copyright © 2014-2026 PDF Technologies, Inc. All Rights Reserved.
//
//  THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
//  AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
//  UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
//  This notice may not be removed from this file.
//

import UIKit
import ComPDFKit_Tools
import ComPDFKit

protocol RCTCPDFViewDelegate: AnyObject {
  func cpdfViewAttached(_ cpdfView: RCTCPDFView)
  func cpdfViewDetached(_ cpdfView: RCTCPDFView)
  func saveDocumentChange(_ cpdfView: RCTCPDFView)
  func onPageChanged(_ cpdfView: RCTCPDFView, pageIndex: Int)
  func onPageEditDialogBackPress(_ cpdfView: RCTCPDFView)
  func onFullScreenChanged(_ cpdfView: RCTCPDFView, isFull: Bool)
  func onDocumentIsReady(_ cpdfView: RCTCPDFView)
  func onTapMainDocArea(_ cpdfView: RCTCPDFView)
  func onAnnotationHistoryChanged(_ cpdfView: RCTCPDFView)
  func onContentEditorHistoryChanged(_ cpdfView: RCTCPDFView, pageIndex: Int)
  func onIOSClickBackPressed(_ cpdfView: RCTCPDFView)
  func onAnnotationAddChanged(_ cpdfView: RCTCPDFView, annotationData: [String: Any])
  func onPencilDrawingCompleted(_ cpdfView: RCTCPDFView, payload: [String: Any])
  func onPencilDrawingDiscarded(_ cpdfView: RCTCPDFView, payload: [String: Any])
  func onFormFieldAddChanged(_ cpdfView: RCTCPDFView, formData: [String: Any])
  func onAnnotationSelectedChanged(_ cpdfView: RCTCPDFView, annotationData: [String: Any], isSelected: Bool)
  func onFormFieldSelectedChanged(_ cpdfView: RCTCPDFView, formData: [String: Any], isSelected: Bool)
  func onEditingAreaAddChanged(_ cpdfView: RCTCPDFView, editingAreaData: [String: Any])
  func onEditingAreaSelectedChanged(_ cpdfView: RCTCPDFView, editingAreaData: [String: Any], isSelected: Bool)
  func onAutoShowAnnotationChanged(_ cpdfView: RCTCPDFView, annotationData: [String: Any])
  func onAutoShowFormPickerChanged(_ cpdfView: RCTCPDFView, formData: [String: Any])
  func onCustomMenuActionChanged(_ cpdfView: RCTCPDFView, payload: [String: Any])
  func onCustomToolbarActionChanged(_ cpdfView: RCTCPDFView, payload: [String: Any])
  func onSearchBackButtonTapped(_ cpdfView: RCTCPDFView, payload: [String: Any])
  func onAnnotationStyleDialogDismissed(_ cpdfView: RCTCPDFView, payload: [String: Any])
  func onFormStyleDialogDismissed(_ cpdfView: RCTCPDFView, payload: [String: Any])
  func onContentEditorStyleDialogDismissed(_ cpdfView: RCTCPDFView, payload: [String: Any])
  func onWatermarkDialogDismissed(_ cpdfView: RCTCPDFView, payload: [String: Any])
  func onInterceptAnnotationDoAction(_ cpdfView: RCTCPDFView, annotation: [String: Any])
  func onInterceptWidgetDoAction(_ cpdfView: RCTCPDFView, widget: [String: Any])
}

extension Bundle {
  func findImageURL(for name: String) -> URL? {
    let imageFormats = ["png", "jpg", "jpeg", "gif", "bmp", "tiff", "heic", "webp"]
    
    for format in imageFormats {
      if let url = Bundle.main.url(forResource: name, withExtension: format) {
        return url
      }
    }
    
    return nil
  }
}

class RCTCPDFView: UIView, CPDFViewBaseControllerDelete {
  private enum NotificationNames {
    static let annotationsOperationChange = Notification.Name("CPDFListViewAnnotationsOperationChangeNotification")
    static let pageChanged = Notification.Name("CPDFViewPageChangedNotification")
    static let pageEditingDidChanged = Notification.Name("CPDFPageEditingDidChangedNotification")
  }
  
  weak var delegate: RCTCPDFViewDelegate?
  
  public var pdfViewController : CPDFViewController?
  
  private var navigationController : CNavigationController?
  private var hasRegisteredNotificationObservers = false
  private var hasCleanedUp = false
  private var hasReportedAttachment = false
  
  init() {
    super.init(frame: CGRect(x: 0, y: 0, width: 500, height: 400))
  }
  
  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  deinit {
    cleanupLifecycle()
  }

  override func willMove(toSuperview newSuperview: UIView?) {
    super.willMove(toSuperview: newSuperview)

    if newSuperview == nil {
      cleanupLifecycle()
    }
  }

  override func didMoveToWindow() {
    super.didMoveToWindow()

    if window != nil {
      notifyCPDFViewAttachedIfReady()
    }
  }
  
  // MARK: - Private Methods

  private func registerNotificationObservers() {
    guard !hasRegisteredNotificationObservers else { return }

    let notificationCenter = NotificationCenter.default
    notificationCenter.addObserver(
      self,
      selector: #selector(annotationsOperationChangeNotification(_:)),
      name: NotificationNames.annotationsOperationChange,
      object: pdfViewController?.pdfListView
    )
    notificationCenter.addObserver(
      self,
      selector: #selector(pageChangedNotification(_:)),
      name: NotificationNames.pageChanged,
      object: pdfViewController
    )
    notificationCenter.addObserver(
      self,
      selector: #selector(pageEditingDidChanged(_:)),
      name: NotificationNames.pageEditingDidChanged,
      object: nil
    )
    hasRegisteredNotificationObservers = true
  }

  private func cleanupLifecycle() {
    guard !hasCleanedUp else { return }
    hasCleanedUp = true

    NotificationCenter.default.removeObserver(self)
    hasRegisteredNotificationObservers = false

    delegate?.cpdfViewDetached(self)

    pdfViewController?.delegate = nil
    navigationController?.view.removeFromSuperview()
    navigationController = nil
    pdfViewController = nil
    delegate = nil
  }

  private func notifyCPDFViewAttachedIfReady(retryCount: Int = 6) {
    guard !hasReportedAttachment, !hasCleanedUp, pdfViewController != nil else {
      return
    }

    guard let reactTag = reactTag, reactTag.intValue != 0 else {
      guard retryCount > 0 else { return }
      DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in
        self?.notifyCPDFViewAttachedIfReady(retryCount: retryCount - 1)
      }
      return
    }

    hasReportedAttachment = true
    delegate?.cpdfViewAttached(self)
  }

  private func annotationDialogType(for annotationMode: CPDFViewAnnotationMode) -> String? {
    switch annotationMode {
    case .note: return "note"
    case .highlight: return "highlight"
    case .underline: return "underline"
    case .strikeout: return "strikeout"
    case .squiggly: return "squiggly"
    case .ink: return "ink"
    case .circle: return "circle"
    case .square: return "square"
    case .arrow: return "arrow"
    case .line: return "line"
    case .freeText: return "freetext"
    case .stamp: return "stamp"
    case .image: return "pictures"
    default: return nil
    }
  }

  private func formDialogType(for annotationMode: CPDFViewAnnotationMode) -> String? {
    switch annotationMode {
    case .formModeText: return "textField"
    case .formModeCheckBox: return "checkBox"
    case .formModeRadioButton: return "radioButton"
    case .formModeList: return "listBox"
    case .formModeCombox: return "comboBox"
    case .formModeButton: return "pushButton"
    default: return nil
    }
  }

  private func contentEditorDialogType(for editMode: CPDFEditMode) -> String? {
    let normalized = String(describing: editMode).lowercased()
    if normalized.contains("text") {
      return "editorText"
    }
    if normalized.contains("image") {
      return "editorImage"
    }
    return nil
  }
  
  private func createCPDFView() {
    let initPageIndex = pageIndex
    
    var documentPath = document.path
    var success = false
    
    let homeDiectory = NSHomeDirectory()
    let bundlePath = Bundle.main.bundlePath
    
    if (documentPath.hasPrefix(homeDiectory) || documentPath.hasPrefix(bundlePath)) {
      let documentHome = homeDiectory.appending("/Documents")
      if documentPath.hasPrefix(homeDiectory) && documentPath.hasPrefix(documentHome) {
        
      } else {
        let fileManager = FileManager.default
        let samplesFilePath = NSHomeDirectory().appending("/Documents/Files")
        let fileName = document.lastPathComponent
        let docsFilePath = samplesFilePath + "/" + fileName
        
        if !fileManager.fileExists(atPath: samplesFilePath) {
          try? FileManager.default.createDirectory(atPath: samplesFilePath, withIntermediateDirectories: true, attributes: nil)
        }
        
        try? FileManager.default.copyItem(atPath: document.path, toPath: docsFilePath)
        
        documentPath = docsFilePath
      }
    } else {
      success = document.startAccessingSecurityScopedResource()
    }
    
    let jsonData = CPDFJSONDataParse(String: configuration)
    let configurations = jsonData.configuration ?? CPDFConfiguration()
    configurations.defaultPageIndex = initPageIndex
    
    let imageName = configurations.watermarkMode.imageName
    if !imageName.isEmpty {
      if FileManager.default.fileExists(atPath: imageName) {
        if let image = UIImage(contentsOfFile: imageName) {
          configurations.watermarkMode.image = image
        }
      } else {
        if let url = Bundle.main.findImageURL(for: imageName) {
          if let image = UIImage(contentsOfFile: url.path) {
            configurations.watermarkMode.image = image
          }
        }
      }
    }
    
    let bookmarkImageName = configurations.bookmarkImageName
    if !bookmarkImageName.isEmpty {
      if FileManager.default.fileExists(atPath: bookmarkImageName) {
        if let image = UIImage(contentsOfFile: bookmarkImageName) {
          configurations.bookmarkImage = image
        }
      } else {
        if let url = Bundle.main.findImageURL(for: bookmarkImageName) {
          if let image = UIImage(contentsOfFile: url.path) {
            configurations.bookmarkImage = image
          }
        }
      }
    }
    
    // selectTextLeftImage
    let selectTextLeftImageName = configurations.selectTextLeftImageName
    if !selectTextLeftImageName.isEmpty {
      if FileManager.default.fileExists(atPath: selectTextLeftImageName) {
        if let image = UIImage(contentsOfFile: selectTextLeftImageName) {
          configurations.selectTextLeftImage = image
        }
      } else {
        if let url = Bundle.main.findImageURL(for: selectTextLeftImageName) {
          if let image = UIImage(contentsOfFile: url.path) {
            configurations.selectTextLeftImage = image
          }
        }
      }
    }
    
    // selectTextRightImage
    let selectTextRightImageName = configurations.selectTextRightImageName
    if !selectTextRightImageName.isEmpty {
      if FileManager.default.fileExists(atPath: selectTextRightImageName) {
        if let image = UIImage(contentsOfFile: selectTextRightImageName) {
          configurations.selectTextRightImage = image
        }
      } else {
        if let url = Bundle.main.findImageURL(for: selectTextRightImageName) {
          if let image = UIImage(contentsOfFile: url.path) {
            configurations.selectTextRightImage = image
          }
        }
      }
    }
    
    // rotationAnnotationImage
    let rotationAnnotationImageName = configurations.rotationAnnotationImageName
    if !rotationAnnotationImageName.isEmpty {
      if FileManager.default.fileExists(atPath: rotationAnnotationImageName) {
        if let image = UIImage(contentsOfFile: rotationAnnotationImageName) {
          configurations.rotationAnnotationImage = image
        }
      } else {
        if let url = Bundle.main.findImageURL(for: rotationAnnotationImageName) {
          if let image = UIImage(contentsOfFile: url.path) {
            configurations.rotationAnnotationImage = image
          }
        }
      }
    }
    
    pdfViewController = CPDFViewController(filePath: documentPath, password: password, configuration: configurations)
    pdfViewController?.delegate = self
    navigationController = CNavigationController(rootViewController: pdfViewController!)
    navigationController?.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    navigationController?.view.frame = self.frame
    
    navigationController?.setViewControllers([pdfViewController!], animated: true)
    
    addSubview(navigationController?.view ?? UIView())
    
    notifyCPDFViewAttachedIfReady()
    
    if success {
      document.stopAccessingSecurityScopedResource()
    }

    registerNotificationObservers()
  }
  
  func insertPDFDocument(_ document: CPDFDocument, Pages pages: [Int], Position index: Int) -> Bool {
    if let pdfListView = self.pdfViewController?.pdfListView {
      var _index = index
      if index < 0 || index > pdfListView.document.pageCount {
        if Int(index) == -1 {
          _index = Int(pdfListView.document.pageCount)
        } else {
          return false
        }
      }
      
      var indexSet = IndexSet()
      for page in pages {
        indexSet.insert(IndexSet.Element(page))
      }
      
      let success = pdfListView.document.importPages(indexSet, from: document, at: UInt(_index))
      pdfListView.layoutDocumentView()
      
      return success
    } else {
      return false
    }
  }
  
  func extractPDFDocument(_ savePath: URL, Pages pages: [Int]) -> Bool {
    if let pdfListView = self.pdfViewController?.pdfListView {
      var indexSet = IndexSet()
      for page in pages {
        indexSet.insert(page)
      }
      
      let document = CPDFDocument()
      document?.importPages(indexSet, from: pdfListView.document, at: 0)
      
      let success = document?.write(to: savePath, isSaveFontSubset: true) ?? false
      
      return success
    } else {
      return false
    }
  }
  
  func getValue<T>(from info: [String: Any]?, key: String, defaultValue: T) -> T {
    guard let value = info?[key] as? T else {
      return defaultValue
    }
    return value
  }
  
  // MARK: - Page Public Methods
  
  func getPage(_ index: UInt) -> CPDFPage {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let page = pdfListView.document.page(at: index) ?? CPDFPage()
      return page
    } else {
      return CPDFPage()
    }
  }
  
  // MARK: - Document Public Methods
  
  func saveDocument(completionHandler: @escaping (Bool) -> Void) {
    self.pdfViewController?.pdfListView?.exitDrawing()
    self.pdfViewController?.pdfListView?.becomeFirstResponder()
    if (self.pdfViewController?.pdfListView?.isEditing() == true && self.pdfViewController?.pdfListView?.isEdited() == true) {
      DispatchQueue.global(qos: .default).async {
        if self.pdfViewController?.pdfListView?.isEdited() == true {
          self.pdfViewController?.pdfListView?.commitEditing()
        }
        
        DispatchQueue.main.async {
          
          if self.pdfViewController?.pdfListView?.document.isModified() == true {
            let document = self.pdfViewController?.pdfListView?.document
            let success = document?.write(to: document?.documentURL ?? URL(fileURLWithPath: ""), isSaveFontSubset: true) ?? false
            completionHandler(success)
          } else {
            completionHandler(true)
          }
        }
      }
    } else {
      if self.pdfViewController?.pdfListView?.document.isModified() == true {
        let document = self.pdfViewController?.pdfListView?.document
        let success = document?.write(to: document?.documentURL ?? URL(fileURLWithPath: ""), isSaveFontSubset: true) ?? false
        completionHandler(success)
      } else {
        completionHandler(true)
      }
    }
  }
  
  func setMargins(left : Int, top : Int, right : Int, bottom : Int) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      pdfListView.pageBreakMargins = .init(top: CGFloat(top), left: CGFloat(left), bottom: CGFloat(bottom), right: CGFloat(right))
      pdfListView.layoutDocumentView()
    }
  }
  
  func removeAllAnnotations(completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let pageCount = pdfListView.document?.pageCount ?? 0
      for i in 0..<pageCount {
        let page = pdfListView.document?.page(at: i)
        page?.removeAllAnnotations()
      }
      self.pdfViewController?.pdfListView?.setNeedsDisplayForVisiblePages()
      self.pdfViewController?.pdfListView?.updateActiveAnnotations([])
      completionHandler(true)
    } else {
      completionHandler(false)
    }
  }
  
  func importAnnotations(xfdfFile : URL, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      
      let documentFolder = NSHomeDirectory().appending("/Documents/Files")
      if !FileManager.default.fileExists(atPath: documentFolder) {
        try? FileManager.default.createDirectory(at: URL(fileURLWithPath: documentFolder), withIntermediateDirectories: true, attributes: nil)
      }
      
      let documentPath = documentFolder + "/\(xfdfFile.lastPathComponent)"
      try? FileManager.default.copyItem(atPath: xfdfFile.path, toPath: documentPath)
      
      if !FileManager.default.fileExists(atPath: documentPath) {
        print("fail")
      }
      
      let success = pdfListView.document?.importAnnotation(fromXFDFPath: documentPath) ?? false
      if success {
        self.pdfViewController?.pdfListView?.setNeedsDisplayForVisiblePages()
      }
      completionHandler(success)
    } else {
      completionHandler(false)
    }
  }
  
  func exportAnnotations(completionHandler: @escaping (String) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let fileNameWithExtension = pdfListView.document?.documentURL.lastPathComponent ?? ""
      let fileName = (fileNameWithExtension as NSString).deletingPathExtension
      let documentFolder = NSHomeDirectory().appending("/Documents/\(fileName)_xfdf.xfdf")
      let succes = pdfListView.document?.exportAnnotation(toXFDFPath: documentFolder) ?? false
      
      if succes {
        completionHandler(documentFolder)
      } else {
        completionHandler("")
      }
    } else {
      completionHandler("")
    }
  }
  
  func setDisplayPageIndex(pageIndex : Int, rectList: [[String: Any]]) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      pdfListView.go(toPageIndex: pageIndex, animated: false)
      var m_left:CGFloat = 0.0
      var m_top:CGFloat = 0.0
      var m_right:CGFloat = 0.0
      var m_bottom:CGFloat = 0.0
      
      var areasDict: [NSNumber: [NSValue]] = [:]
      for rect in rectList {
        var rectValues:[NSValue] = []
        for (key, value) in rect {
          if key == "left"  {
            m_left = value as? CGFloat ?? 0
          } else if key == "top" {
            m_top = value as? CGFloat ?? 0
          } else if key == "right" {
            m_right = value as? CGFloat ?? 0
          } else if key == "bottom" {
            m_bottom = value as? CGFloat ?? 0
          }
        }
        rectValues.append(NSValue(cgRect: CGRect(x: m_left, y: m_top, width: m_right - m_left, height: m_bottom - m_top)))
        areasDict[NSNumber(value: UInt(truncating: pageIndex as NSNumber))] = rectValues
      }
      
      if let squareAreaDict = self.pdfViewController?.configuration?.squareAreaDict {
        print("custom style\(squareAreaDict)")
          let sytle = CPDFSquareAreaStyle()
          if let fillHex = squareAreaDict["fillColor"] as? String {
              let c = ColorHelper.colorWithHexString(hex: fillHex)
              if let a = ColorHelper.alphaFromHex(fillHex) {
                  sytle.fillOpacity = a
              }
              
              sytle.fillColor = c
          }

          if let borderHex = squareAreaDict["borderColor"] as? String {
              let c = ColorHelper.colorWithHexString(hex: borderHex)
              if let a = ColorHelper.alphaFromHex(borderHex) {
                sytle.borderOpacity = a
              }
              sytle.borderColor = c
          }

          if let w = squareAreaDict["borderWidth"] as? CGFloat {
              sytle.borderWidth = w
          }

          if let arr = squareAreaDict["borderDashPattern"] as? [NSNumber] {
              sytle.borderDashPattern = arr
          }
          
          pdfListView.setSquareAreas(areasDict, style: sytle)
      } else {
          let borderColor = ColorHelper.colorWithHexString(hex: "#1460F3")
          let fillColor = ColorHelper.colorWithHexString(hex: "#4D1460F3")
          pdfListView.setSquareAreas(areasDict, borderColor: borderColor, borderOpacity: 1.0, fill: fillColor, fillOpacity: 77.0/255.0)
      }
    }
  }
  
  func getCurrentPageIndex(completionHandler: @escaping (Int) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(pdfListView.currentPageIndex)
    } else {
      completionHandler(0)
    }
  }
  
  func hasChange(completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let success = pdfListView.document?.isModified() ?? false
      completionHandler(success)
    } else {
      completionHandler(false)
    }
  }
  
  func setScale(scale : NSNumber){
    if let pdfListView = self.pdfViewController?.pdfListView {
      pdfListView.setScaleFactor(CGFloat(truncating: scale), animated: true)
    }
  }
  
  func getScale(completionHandler: @escaping (NSNumber) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(NSNumber(value: pdfListView.scaleFactor))
    } else {
      completionHandler(1.0)
    }
  }
  
  func setBackgroundColor(hexColor : String){
    if let pdfListView = self.pdfViewController?.pdfListView {
      pdfListView.backgroundColor = ColorHelper.colorWithHexString(hex: hexColor)
    }
  }
  
  func setReadBackgroundColor(displayMode : NSString) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      switch displayMode {
      case "light":
        pdfListView.displayMode = .normal
      case "dark":
        pdfListView.displayMode = .night
      case "sepia":
        pdfListView.displayMode = .soft
      case "reseda":
        pdfListView.displayMode = .green
      default:
        pdfListView.displayMode = .normal
      }
      pdfListView.layoutDocumentView()
    }
  }
  
  func getReadbackgroundColor(completionHandler: @escaping (NSString) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let dispalyMode = pdfListView.displayMode
      switch dispalyMode {
      case .normal:
        completionHandler("#FFFFFF")
      case .night:
        completionHandler("#000000")
      case .soft:
        completionHandler("#FFFFFF")
      case .green:
        completionHandler("#FFEFBE")
      case .custom:
        completionHandler("#CDE6D0")
      @unknown default:
        completionHandler("#FFFFFF")
      }
    } else {
      completionHandler("#FFFFFF")
    }
  }
  
  func setFormFieldHighlight(formFieldHighlight : Bool){
    if let pdfListView = self.pdfViewController?.pdfListView {
      CPDFKitConfig.sharedInstance().setEnableFormFieldHighlight(formFieldHighlight)
      pdfListView.layoutDocumentView()
    }
  }
  
  func isFormFieldHighlight(completionHandler: @escaping (Bool) -> Void){
    completionHandler(CPDFKitConfig.sharedInstance().enableFormFieldHighlight())
  }
  
  func setLinkHighlight(linkHighlight : Bool) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      CPDFKitConfig.sharedInstance().setEnableLinkFieldHighlight(linkHighlight)
      pdfListView.layoutDocumentView()
    }
  }
  
  func isLinkHighlight(completionHandler: @escaping (Bool) -> Void){
    completionHandler(CPDFKitConfig.sharedInstance().enableLinkFieldHighlight())
  }
  
  func setVerticalMode(isVerticalMode : Bool) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      pdfListView.displayDirection = isVerticalMode ? .vertical : .horizontal
      pdfListView.layoutDocumentView()
    }
  }
  
  func isVerticalMode(completionHandler: @escaping (Bool) -> Void){
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(pdfListView.displayDirection == .vertical)
    } else {
      completionHandler(true)
    }
  }
  
  func setContinueMode(isContinueMode : Bool) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      pdfListView.isContinueMode = isContinueMode
      pdfListView.layoutDocumentView()
    }
  }
  
  func isContinueMode(completionHandler: @escaping (Bool) -> Void){
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(pdfListView.isContinueMode)
    }else {
      completionHandler(true)
    }
  }
  
  func setDoublePageMode(isDoublePageMode : Bool) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      pdfListView.displayTwoUp = isDoublePageMode
      pdfListView.displaysAsBook = false
      if(pdfListView.displayDirection == .horizontal){
        pdfListView.isContinueMode = false
      }
      pdfListView.layoutDocumentView()
    }
  }
  
  func isDoublePageMode(completionHandler: @escaping (Bool) -> Void){
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(pdfListView.displayTwoUp)
    }else {
      completionHandler(true)
    }
  }
  
  func setCoverPageMode(isCoverPageMode : Bool) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      pdfListView.displayTwoUp = isCoverPageMode
      pdfListView.displaysAsBook = isCoverPageMode
      if(pdfListView.displayDirection == .horizontal){
        pdfListView.isContinueMode = false
      }
      pdfListView.layoutDocumentView()
    }
  }
  
  func isCoverPageMode(completionHandler: @escaping (Bool) -> Void){
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(pdfListView.displaysAsBook)
    }else {
      completionHandler(true)
    }
  }
  
  func setCropMode(isCropMode : Bool) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      pdfListView.displayCrop = isCropMode
      pdfListView.layoutDocumentView()
    }
  }
  
  func isCropMode(completionHandler: @escaping (Bool) -> Void){
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(pdfListView.displayCrop)
    }else {
      completionHandler(true)
    }
  }
  
  func setPreviewMode(viewMode : String) {
    switch viewMode {
    case "viewer":
      self.pdfViewController?.enterViewerMode()
    case "annotations":
      self.pdfViewController?.enterAnnotationMode()
    case "contentEditor":
      self.pdfViewController?.enterEditMode()
    case "forms":
      self.pdfViewController?.enterFormMode()
    case "signatures":
      self.pdfViewController?.enterSignatureMode()
    default:
      self.pdfViewController?.enterViewerMode()
    }
  }
  
  func getPreviewMode(completionHandler: @escaping (String) -> Void) {
    let state = self.pdfViewController?.functionTypeState ?? .viewer
    switch state {
    case .viewer:
      completionHandler("viewer")
    case .edit:
      completionHandler("contentEditor")
    case .annotation:
      completionHandler("annotations")
    case .form:
      completionHandler("forms")
    case .signature:
      completionHandler("signatures")
    default:
      completionHandler("viewer")
    }
  }
  
  func showThumbnailView(isEditMode : Bool) {
    if isEditMode {
      self.pdfViewController?.enterThumbnail(false)
    } else {
      self.pdfViewController?.enterThumbnail(true)
    }
  }
  
  func showBotaView() {
    self.pdfViewController?.buttonItemClicked_Bota(UIButton(frame: .zero))
  }
  
  func showAddWatermarkView(config: [String: Any]) {
    var isWatermarkSaveAs:Bool = true
    for (configKey, configValue) in config {
      if configKey == "saveAsNewFile" {
        isWatermarkSaveAs = configValue as? Bool ?? true
      } else if configKey == "outsideBackgroundColor" {
        let string = configValue as? String ?? ""
        if !string.isEmpty {
          let color = ColorHelper.colorWithHexString(hex: string)
          
          let userDefaults = UserDefaults.standard
          userDefaults.setPDFListViewColor(color, forKey: "CWatermarkBackgroundColor")
          userDefaults.synchronize()
        }
      } else if configKey == "text" {
        self.pdfViewController?.configuration?.watermarkMode.text = configValue as? String ?? ""
      } else if configKey == "image" {
        let imageName = configValue as? String ?? ""
        self.pdfViewController?.configuration?.watermarkMode.imageName = imageName
        if !imageName.isEmpty {
          if FileManager.default.fileExists(atPath: imageName) {
            if let image = UIImage(contentsOfFile: imageName) {
              self.pdfViewController?.configuration?.watermarkMode.image = image
            }
          } else {
            if let url = Bundle.main.findImageURL(for: imageName) {
              if let image = UIImage(contentsOfFile: url.path) {
                self.pdfViewController?.configuration?.watermarkMode.image = image
              }
            }
          }
        }
      } else if configKey == "textSize" {
        let textSize = configValue as? CGFloat ?? 0
        self.pdfViewController?.configuration?.watermarkMode.fontSize = textSize
      } else if configKey == "scale" {
        let scale = configValue as? CGFloat ?? 0
        self.pdfViewController?.configuration?.watermarkMode.watermarkScale = scale
      } else if configKey == "textColor" {
        let string = configValue as? String ?? ""
        let color = ColorHelper.colorWithHexString(hex: string)
        self.pdfViewController?.configuration?.watermarkMode.textColor = color
      } else if configKey == "rotation" {
        let rotation = configValue as? CGFloat ?? 0
        self.pdfViewController?.configuration?.watermarkMode.watermarkRotation = rotation
      } else if configKey == "opacity" {
        let opacity = configValue as? CGFloat ?? 255.0
        self.pdfViewController?.configuration?.watermarkMode.watermarkOpacity = opacity/255.0
      } else if configKey == "isTilePage" {
        let isTiled = configValue as? Bool ?? true
        self.pdfViewController?.configuration?.watermarkMode.isTile = isTiled
      } else if configKey == "isFront" {
        let isFront = configValue as? Bool ?? true
        self.pdfViewController?.configuration?.watermarkMode.isFront = isFront
      } else if configKey == "types" {
        let types = configValue as? [String] ?? []
        
        var waterType: [CPDFWatermarkToolType] = []
        for type in types {
          if type == "text" {
            waterType.append(.text)
          } else if type == "image" {
            waterType.append(.image)
          }
        }
        self.pdfViewController?.configuration?.watermarkTypes = waterType
      }
    }
    self.pdfViewController?.enterPDFWatermark(isSaveAs: isWatermarkSaveAs)
  }
  
  func showSecurityView() {
    self.pdfViewController?.enterPDFSecurity()
  }
  
  func showDisplaySettingView() {
    self.pdfViewController?.enterPDFSetting()
  }
  
  func showDocumentInfoView() {
    self.pdfViewController?.enterPDFInfo()
  }
  
  func enterSnipMode() {
    self.pdfViewController?.enterPDFSnipImageMode()
  }
  
  func exitSnipMode() {
    self.pdfViewController?.exitPDFSnipImageMode()
  }
  
  func open(document : URL, password : String, pageIndex: Int, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let newDocument = CPDFDocument(url: document)
      if(newDocument?.isLocked == true){
        newDocument?.unlock(withPassword: password)
      }
      pdfListView.document = newDocument
      self.pdfViewController?.filePath = newDocument?.documentURL.path
      pdfListView.go(toPageIndex: pageIndex, animated: false)
      pdfListView.setNeedsDisplay()
      completionHandler(true)
    } else {
      completionHandler(false)
    }
  }
  
  func getFileName(completionHandler: @escaping (String) -> Void){
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(pdfListView.document.documentURL.lastPathComponent)
    }else {
      completionHandler("")
    }
  }
  
  func isEncrypted(completionHandler: @escaping (Bool) -> Void){
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(pdfListView.document.isEncrypted)
    }else {
      completionHandler(false)
    }
  }
  
  func isImageDoc(completionHandler: @escaping (Bool) -> Void){
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(pdfListView.document.isImageDocument())
    }else {
      completionHandler(false)
    }
  }
  
  func getPermissions(completionHandler: @escaping (NSNumber) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let permissions = pdfListView.document?.permissionsStatus ?? .none
      switch permissions {
      case .none:
        completionHandler(0)
      case .user:
        completionHandler(1)
      case .owner:
        completionHandler(2)
      default:
        completionHandler(0)
      }
    }else {
      completionHandler(0)
    }
  }
  
  func getPageCount(completionHandler: @escaping (NSNumber) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(pdfListView.document.pageCount as NSNumber)
    }else {
      completionHandler(0)
    }
  }
  
  func checkOwnerUnlocked(completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(pdfListView.document.isCheckOwnerUnlocked())
    }else {
      completionHandler(false)
    }
  }
  
  func checkOwnerPassword(password : String, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      completionHandler(pdfListView.document.checkOwnerPassword(password))
    }else {
      completionHandler(false)
    }
  }
  
  func removePassword(completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let newDocument = pdfListView.document
      let url = newDocument?.documentURL
      
      let success = newDocument?.writeDecrypt(to: url, isSaveFontSubset: true) ?? false
      completionHandler(success)
    } else {
      completionHandler(false)
    }
  }
  
  func setPassword(info : NSDictionary, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      
      let _info = info as? [String: Any]
      
      let userPassword : String = self.getValue(from: _info, key: "user_password", defaultValue: "")
      let ownerPassword : String = self.getValue(from: _info, key: "owner_password", defaultValue: "")
      let allowsPrinting : Bool = self.getValue(from: _info, key: "allows_printing", defaultValue: true)
      let allowsCopying : Bool = self.getValue(from: _info, key: "allows_copying", defaultValue: true)
      
      let encryptAlgo : String = self.getValue(from: _info, key: "encrypt_algo", defaultValue: "rc4")
      
      var level: Int = 0
      // Encryption mode, the type passed in is：rc4, aes128, aes256, noEncryptAlgo
      switch encryptAlgo {
      case "rc4":
        level = 0
      case "aes128":
        level = 1
      case "aes256":
        level = 2
      case "noEncryptAlgo":
        level = 3
      default:
        level = 3
      }
      
      var options:[CPDFDocumentWriteOption: Any] = [:]
      options[CPDFDocumentWriteOption.userPasswordOption] = userPassword
      
      options[CPDFDocumentWriteOption.ownerPasswordOption] = ownerPassword
      
      options[CPDFDocumentWriteOption.allowsPrintingOption] = allowsPrinting
      
      options[CPDFDocumentWriteOption.allowsCopyingOption] = allowsCopying
      
      options[CPDFDocumentWriteOption.encryptionLevelOption] = NSNumber(value: level)
      
      let newDocument = pdfListView.document
      let url = newDocument?.documentURL
      
      let success = newDocument?.write(to: url, withOptions: options, isSaveFontSubset: true) ?? false
      completionHandler(success)
    } else {
      completionHandler(false)
    }
  }
  
  func getEncryptAlgo(completionHandler: @escaping (String) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let encryptAlgo = pdfListView.document.encryptionLevel
      switch encryptAlgo {
      case .RC4:
        completionHandler("rc4")
      case .AES128:
        completionHandler("aes128")
      case .AES256:
        completionHandler("aes256")
      case .noEncryptAlgo:
        completionHandler("noEncryptAlgo")
      default:
        completionHandler("noEncryptAlgo")
      }
    }else {
      completionHandler("noEncryptAlgo")
    }
  }
  
  func printDocument() {
    self.pdfViewController?.enterPrintState();
  }
  
  func importWidgets(xfdfFile : URL, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      
      let documentFolder = NSHomeDirectory().appending("/Documents/Files")
      if !FileManager.default.fileExists(atPath: documentFolder) {
        try? FileManager.default.createDirectory(at: URL(fileURLWithPath: documentFolder), withIntermediateDirectories: true, attributes: nil)
      }
      
      let documentPath = documentFolder + "/\(xfdfFile.lastPathComponent)"
      try? FileManager.default.copyItem(atPath: xfdfFile.path, toPath: documentPath)
      
      if !FileManager.default.fileExists(atPath: documentPath) {
        print("fail")
      }
      
      let success = pdfListView.document?.importForm(fromXFDFPath: documentPath) ?? false
      if success {
        self.pdfViewController?.pdfListView?.setNeedsDisplayForVisiblePages()
      }
      completionHandler(success)
    } else {
      completionHandler(false)
    }
  }
  
  func getDocumentPath(completionHandler: @escaping (String) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let documentPath = pdfListView.document.documentURL.path
      completionHandler(documentPath)
    }
  }
  
  func exportWidgets(completionHandler: @escaping (String) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let fileNameWithExtension = pdfListView.document?.documentURL.lastPathComponent ?? ""
      let fileName = (fileNameWithExtension as NSString).deletingPathExtension
      let documentFolder = NSHomeDirectory().appending("/Documents/\(fileName)_xfdf.xfdf")
      let succes = pdfListView.document?.export(toXFDFPath: documentFolder) ?? false
      
      if succes {
        completionHandler(documentFolder)
      } else {
        completionHandler("")
      }
    } else {
      completionHandler("")
    }
  }
  
  func flattenAllPages(savePath: URL, fontSubset: Bool, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      
      let success = pdfListView.document.writeFlatten(to: savePath, isSaveFontSubset: fontSubset)
      
      completionHandler(success)
    } else {
      completionHandler(false)
    }
  }
  
  func saveAs(savePath: URL, removeSecurity: Bool, fontSubset: Bool, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      
      var success: Bool = false
      if removeSecurity {
        success = pdfListView.document.writeDecrypt(to: savePath, isSaveFontSubset: fontSubset)
      } else {
        success = pdfListView.document.write(to: savePath, isSaveFontSubset: fontSubset)
      }
      completionHandler(success)
    } else {
      completionHandler(false)
    }
  }
  
  func importDocument(_ filePath:URL, _ info : NSDictionary, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let _info = info as? [String: Any]
      
      let password: String = self.getValue(from: _info, key: "password", defaultValue: "")
      let pages: [Int] = self.getValue(from: _info, key: "pages", defaultValue: [])
      let insert_position: Int = self.getValue(from: _info, key: "insert_position", defaultValue: 0)
      
      let _document = CPDFDocument(url: filePath)
      
      if _document?.isLocked == true {
        _document?.unlock(withPassword: password)
      }
      
      let success = self.insertPDFDocument(_document!, Pages: pages, Position: insert_position)
      completionHandler(success)
    } else {
      completionHandler(false)
    }
  }
  
  func splitDocumentPages(savePath: URL, pages: [Int], completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      
      let success = self.extractPDFDocument(savePath, Pages: pages)
      
      completionHandler(success)
    } else {
      completionHandler(false)
    }
  }

  func extractImages(
    directoryPath: String,
    pages: [Int],
    completionHandler: @escaping ([String: Any]) -> Void,
    failureHandler: @escaping (String, String) -> Void
  ) {
    guard !directoryPath.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
      failureHandler("EXTRACT_IMAGES_FAIL", "directory_path is empty")
      return
    }

    guard let document = self.pdfViewController?.pdfListView?.document else {
      failureHandler("EXTRACT_IMAGES_FAIL", "Document unavailable")
      return
    }

    guard let indexSet = self.extractImageIndexSet(pages: pages, pageCount: Int(document.pageCount)) else {
      let invalidPage = pages.first { $0 < 0 || $0 >= Int(document.pageCount) } ?? -1
      failureHandler("EXTRACT_IMAGES_FAIL", "Invalid page index: \(invalidPage)")
      return
    }

    DispatchQueue.global(qos: .userInitiated).async {
      do {
        let fileManager = FileManager.default
        if fileManager.fileExists(atPath: directoryPath) {
          var isDirectory: ObjCBool = false
          fileManager.fileExists(atPath: directoryPath, isDirectory: &isDirectory)
          if !isDirectory.boolValue {
            DispatchQueue.main.async {
              failureHandler("EXTRACT_IMAGES_FAIL", "directory_path is not a directory: \(directoryPath)")
            }
            return
          }
        } else {
          try fileManager.createDirectory(
            atPath: directoryPath,
            withIntermediateDirectories: true,
            attributes: nil
          )
        }

        _ = document.extractImage(fromPages: indexSet, toPath: directoryPath)
        let imagePaths = self.filePaths(in: directoryPath)
        let result: [String: Any] = [
          "success": true,
          "count": imagePaths.count,
          "directory_path": directoryPath,
          "image_paths": imagePaths,
        ]

        DispatchQueue.main.async {
          completionHandler(result)
        }
      } catch {
        DispatchQueue.main.async {
          failureHandler("EXTRACT_IMAGES_FAIL", error.localizedDescription)
        }
      }
    }
  }

  private func extractImageIndexSet(pages: [Int], pageCount: Int) -> IndexSet? {
    if pages.isEmpty {
      return IndexSet(integersIn: 0..<pageCount)
    }

    var indexSet = IndexSet()
    for page in pages {
      guard page >= 0 && page < pageCount else {
        return nil
      }
      indexSet.insert(page)
    }
    return indexSet
  }

  private func filePaths(in directoryPath: String) -> [String] {
    guard let fileNames = try? FileManager.default.contentsOfDirectory(atPath: directoryPath) else {
      return []
    }

    return fileNames.compactMap { fileName in
      let path = (directoryPath as NSString).appendingPathComponent(fileName)
      var isDirectory: ObjCBool = false
      if FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory),
         !isDirectory.boolValue {
        return path
      }
      return nil
    }.sorted()
  }
  
  func insertBlankPage(pageIndex: Int, pageWidth: Float, pageHeight: Float, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      var _index = pageIndex
      if pageIndex < 0 || pageIndex > pdfListView.document.pageCount {
        if pageIndex == -1 {
          _index = Int(pdfListView.document.pageCount)
        } else {
          completionHandler(false)
        }
      }
      
      let size = CGSize(width: Double(pageWidth), height: Double(pageHeight))
      let success = pdfListView.document.insertPage(size, at: UInt(_index))
      self.pdfViewController?.pdfListView?.layoutDocumentView()
      
      completionHandler(success)
    } else {
      completionHandler(false)
    }
  }
  
  func insertImagePage(pageIndex: Int, imagePath: URL, pageWidth: Float, pageHeight: Float, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      var _index = pageIndex
      if pageIndex < 0 || pageIndex > pdfListView.document.pageCount {
        if pageIndex == -1 {
          _index = Int(pdfListView.document.pageCount)
        } else {
          completionHandler(false)
        }
      }
      let size = CGSize(width: Double(pageWidth), height: Double(pageHeight))
      let success = pdfListView.document.insertPage(size, withImage: imagePath.path, at: UInt(_index))
      self.pdfViewController?.pdfListView?.layoutDocumentView()
      
      completionHandler(success)
    }else {
      completionHandler(false)
    }
  }
  
  func removePages(indexes: [Int], completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      var indexSet = IndexSet()
      for page in indexes {
        indexSet.insert(page)
      }
      let success = pdfListView.document.removePage(at: indexSet)
      completionHandler(success)
    } else {
      completionHandler(false)
    }
  }

  func copyPage(pageIndex: Int, insertIndex: Int, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let pageCount = Int(pdfListView.document.pageCount)
      guard pageIndex >= 0 && pageIndex < pageCount else {
        completionHandler(false)
        return
      }

      let normalizedInsertIndex = insertIndex == -1 ? pageCount : insertIndex
      guard normalizedInsertIndex >= 0 && normalizedInsertIndex <= pageCount else {
        completionHandler(false)
        return
      }

      let tempDocument = CPDFDocument()
      let sourceIndexSet = IndexSet(integer: pageIndex)
      let importedToTemp = tempDocument?.importPages(sourceIndexSet, from: pdfListView.document, at: 0) ?? false
      guard importedToTemp, let copiedDocument = tempDocument else {
        completionHandler(false)
        return
      }

      let copiedIndexSet = IndexSet(integer: 0)
      let success = pdfListView.document.importPages(copiedIndexSet, from: copiedDocument, at: UInt(normalizedInsertIndex))
      if success {
        pdfListView.layoutDocumentView()
      }
      completionHandler(success)
    } else {
      completionHandler(false)
    }
  }
  
  func movePage(fromIndex: Int, toIndex: Int, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let success = pdfListView.document.movePage(at: UInt(fromIndex), withPageAt: UInt(toIndex))
      completionHandler(success)
    }else {
      completionHandler(false)
    }
  }
  
  
  func selection(dictionary : NSDictionary, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let selection = CPDFSearchUtil.selection(from: pdfListView.document, info: dictionary)
      if let selection = selection {
        pdfListView.go(to: selection.bounds, on: selection.page, offsetY: CGFloat(88), animated: false)
        pdfListView.setHighlightedSelection(selection, animated: true)
        completionHandler(true)
      }else {
        completionHandler(false)
      }
    } else {
      completionHandler(false)
    }
  }
  
  func getPageRotation(pageIndex : Int, completionHandler: @escaping (NSNumber) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let page = pdfListView.document.page(at: UInt(pageIndex))
      let rotation = page?.rotation ?? 0
      completionHandler(NSNumber(value: rotation))
    } else {
      completionHandler(0)
    }
    
  }
  
  func setPageRotation(pageIndex : Int, rotation : Int, completionHandler: @escaping (Bool) -> Void) {
    if let pdfListView = self.pdfViewController?.pdfListView {
      let page = pdfListView.document.page(at: UInt(pageIndex))
      page?.rotation = rotation
      completionHandler(true)
    }else {
      completionHandler(false)
    }
  }
  
  
  // MARK: - CPDFViewBaseControllerDelete
  
  func PDFViewBaseController(_ baseController: CPDFViewBaseController, SaveState success: Bool) {
    self.delegate?.saveDocumentChange(self)
  }
  
  func PDFViewBaseController(_ baseController: CPDFViewBaseController, currentPageIndex index: Int) {
    self.delegate?.onPageChanged(self, pageIndex: index)
  }
  
  func PDFViewBaseControllerPageEditBack(_ baseController: CPDFViewBaseController) {
    self.delegate?.onPageEditDialogBackPress(self)
  }
  
  func PDFViewBaseController(_ baseController: CPDFViewBaseController, HiddenState state: Bool) {
    self.delegate?.onFullScreenChanged(self, isFull: state)
  }
  
  func PDFViewBaseController(_ baseController: CPDFViewBaseController, LoadState success: Bool) {
    if success {
      self.delegate?.onDocumentIsReady(self)
    }
  }
  
  func PDFViewBaseControllerTouchEnded(_ baseController: CPDFViewBaseController) {
    self.delegate?.onTapMainDocArea(self)
  }
  
  public func PDFViewBaseControllerDissmiss(_ baseControllerDelete: CPDFViewBaseController) {
    self.delegate?.onIOSClickBackPressed(self)
  }
  
  func PDFViewBaseControllerAnndotationAdded(_ baseController: CPDFViewBaseController, forAnnotation annotation: CPDFAnnotation) {
    let page = annotation.page
    let pageUtil = RCTCPDFPageUtil(page: page)
    pageUtil.pageIndex = Int(page?.pageIndexInteger ?? 0)
    
    let dict = pageUtil.getAnnotation(FormAnnotation: annotation)
    self.delegate?.onAnnotationAddChanged(self, annotationData: dict)
  }
  
  func PDFViewBaseControllerPencilDrawingCompleted(_ baseController: CPDFViewBaseController, pageIndex: Int) {
    self.delegate?.onPencilDrawingCompleted(self, payload: [
      "type": "pencil",
      "pageIndex": pageIndex
    ])
  }

  func PDFViewBaseControllerPencilDrawingDiscarded(_ baseController: CPDFViewBaseController, pageIndex: Int) {
    self.delegate?.onPencilDrawingDiscarded(self, payload: [
      "type": "pencil",
      "pageIndex": pageIndex
    ])
  }

  func PDFViewBaseControllerAnndotationSelect(_ baseController: CPDFViewBaseController, forAnnotation annotation: CPDFAnnotation, isSelected: Bool) {
    let page = annotation.page
    let pageUtil = RCTCPDFPageUtil(page: page)
    pageUtil.pageIndex = Int(page?.pageIndexInteger ?? 0)
    
    let dict = pageUtil.getAnnotation(FormAnnotation: annotation)
    self.delegate?.onAnnotationSelectedChanged(self, annotationData: dict, isSelected: isSelected)
  }
  
  func PDFViewBaseControllerFormFieldAdded(_ baseController: CPDFViewBaseController, forFormField formField: CPDFWidgetAnnotation) {
    let page = formField.page
    let pageUtil = RCTCPDFPageUtil(page: page)
    pageUtil.pageIndex = Int(page?.pageIndexInteger ?? 0)
    
    let dict = pageUtil.getForm(FormAnnotation: formField)
    self.delegate?.onFormFieldAddChanged(self, formData: dict)
  }
  
  func PDFViewBaseControllerFormFieldSelect(_ baseController: CPDFViewBaseController, forFormField formField: CPDFWidgetAnnotation, isSelected: Bool) {
    let page = formField.page
    let pageUtil = RCTCPDFPageUtil(page: page)
    pageUtil.pageIndex = Int(page?.pageIndexInteger ?? 0)
    
    let dict = pageUtil.getForm(FormAnnotation: formField)
    self.delegate?.onFormFieldSelectedChanged(self, formData: dict, isSelected: isSelected)
  }
  
  func PDFViewBaseControllerEditingAreaAdded(_ baseController: CPDFViewBaseController, forEditingArea editArea: CPDFEditArea, withAttributes attributes: CEditAttributes?) {
    
  }
  
  func PDFViewBaseControllerEditingAreaSelect(_ baseController: CPDFViewBaseController, forEditingArea editArea: CPDFEditArea, isSelected: Bool) {
    let page = editArea.page
    let pageUtil = RCTCPDFPageUtil(page: page)
    pageUtil.pdfView = baseController.pdfListView
    pageUtil.pageIndex = Int(page?.pageIndexInteger ?? 0)
    
    let dict = pageUtil.getEditArea(FromEditArea: editArea)
    self.delegate?.onEditingAreaSelectedChanged(self, editingAreaData: dict, isSelected: isSelected)
  }
  
  func PDFViewBaseControllerAutoShowAnnotationPicker(_ baseController: CPDFViewBaseController, forAnnotationMode annotationMode: CPDFViewAnnotationMode, forAnnotation annotation: CPDFAnnotation?) {
    if annotationMode == .stamp {
      self.delegate?.onAutoShowAnnotationChanged(self, annotationData: ["type": "stamp"])
    } else if annotationMode == .signature {
      self.delegate?.onAutoShowAnnotationChanged(self, annotationData: ["type": "signature"])
    } else if annotationMode == .image {
      self.delegate?.onAutoShowAnnotationChanged(self, annotationData: ["type": "pictures"])
    } else if annotationMode == .link {
      if let linkAnnotation = annotation as? CPDFLinkAnnotation {
        let page = linkAnnotation.page
        let pageUtil = RCTCPDFPageUtil(page: page)
        pageUtil.pageIndex = Int(page?.pageIndexInteger ?? 0)
        let dict = pageUtil.getAnnotation(FormAnnotation: linkAnnotation)
        self.delegate?.onAutoShowAnnotationChanged(self, annotationData: ["type": "link", "annotation": dict])
      }
    } else if annotationMode == .note, let noteAnnotation = annotation as? CPDFTextAnnotation {
      let page = noteAnnotation.page
      let pageUtil = RCTCPDFPageUtil(page: page)
      pageUtil.pageIndex = Int(page?.pageIndexInteger ?? 0)
      let dict = pageUtil.getAnnotation(FormAnnotation: noteAnnotation)
      self.delegate?.onAutoShowAnnotationChanged(self, annotationData: ["type": "note", "annotation": dict])
    }
  }
  
  func PDFViewBaseControllerAutoShowFormPicker(_ baseController: CPDFViewBaseController, forAnnotationMode annotationMode: CPDFViewAnnotationMode, forAnnotation annotation: CPDFWidgetAnnotation?) {
    
  }
  
  func PDFViewBaseControllerHandleCustomMenuAction(_ baseController: CPDFViewBaseController, fronView view: Any, payload: [String : Any]) {
    if let CPDFAnnotation = payload["annotation"] as? CPDFAnnotation {
      let page = CPDFAnnotation.page
      let pageUtil = RCTCPDFPageUtil(page: page)
      pageUtil.pageIndex = Int(page?.pageIndexInteger ?? 0)
      let dict = pageUtil.getAnnotation(FormAnnotation: CPDFAnnotation)
      var newPayload = payload
      newPayload["annotation"] = dict
      self.delegate?.onCustomMenuActionChanged(self, payload: newPayload)
      return
    } else if let CPDFWidgetAnnotation = payload["widget"] as? CPDFWidgetAnnotation {
      let page = CPDFWidgetAnnotation.page
      let pageUtil = RCTCPDFPageUtil(page: page)
      pageUtil.pageIndex = Int(page?.pageIndexInteger ?? 0)
      let dict = pageUtil.getForm(FormAnnotation: CPDFWidgetAnnotation)
      var newPayload = payload
      newPayload["widget"] = dict
      self.delegate?.onCustomMenuActionChanged(self, payload: newPayload)
      return
    } else if let editArea = payload["editArea"] as? CPDFEditArea {
      let page = editArea.page
      let pageUtil = RCTCPDFPageUtil(page: page)
      pageUtil.pdfView = baseController.pdfListView
      pageUtil.pageIndex = Int(page?.pageIndexInteger ?? 0)
      var dict = pageUtil.getEditArea(FromEditArea: editArea)
      if let selectString = self.pdfViewController?.pdfListView?.editingSelectionString() {
        dict["text"] = selectString
      }
      var newPayload = payload
      newPayload["editArea"] = dict
      self.delegate?.onCustomMenuActionChanged(self, payload: newPayload)
      return
    } else if let image = payload["image"] as? UIImage {
      var newPayload = payload
      let data: Data = image.jpegData(compressionQuality: 0.85)!
      newPayload["image"] = data.base64EncodedString()
      self.delegate?.onCustomMenuActionChanged(self, payload: newPayload)
    } else {
      self.delegate?.onCustomMenuActionChanged(self, payload: payload)
    }
  }
  
  func PDFViewBaseControllerHandleCustomToolbarAction(_ baseController: CPDFViewBaseController, fronView view: Any, payload: [String : Any]) {
    self.delegate?.onCustomToolbarActionChanged(self, payload: payload)
  }

  func PDFViewBaseControllerAnnotationDialogDismissed(_ baseController: CPDFViewBaseController, forAnnotationMode annotationMode: CPDFViewAnnotationMode) {
    guard let type = annotationDialogType(for: annotationMode) else {
      return
    }
    self.delegate?.onAnnotationStyleDialogDismissed(self, payload: ["type": type])
  }

  func PDFViewBaseControllerFormPropertyDialogDismissed(_ baseController: CPDFViewBaseController, forAnnotationMode annotationMode: CPDFViewAnnotationMode) {
    guard let type = formDialogType(for: annotationMode) else {
      return
    }
    self.delegate?.onFormStyleDialogDismissed(self, payload: ["type": type])
  }

  func PDFViewBaseControllerEditPropertyDialogDismissed(_ baseController: CPDFViewBaseController, forEditMode editMode: CPDFEditMode) {
    guard let type = contentEditorDialogType(for: editMode) else {
      return
    }
    self.delegate?.onContentEditorStyleDialogDismissed(self, payload: ["type": type])
  }

  func PDFViewBaseControllerSearchToolbarBack(_ baseController: CPDFViewBaseController) {
    self.delegate?.onSearchBackButtonTapped(self, payload: [:])
  }

  func PDFViewBaseControllerWatermarkDialogDismissed(_ baseController: CPDFViewBaseController) {
    self.delegate?.onWatermarkDialogDismissed(self, payload: [:])
  }
  
  func PDFViewBaseControllerInterceptAnnotationDoAction(_ baseController: CPDFViewBaseController, forAnnotation annotation:CPDFAnnotation?) {
      if annotation == nil {
          return
      }
      let page = annotation?.page
      let pageUtil = RCTCPDFPageUtil(page: page)
      pageUtil.pageIndex = Int(page?.pageIndexInteger ?? 0)
      let dict = pageUtil.getAnnotation(FormAnnotation: annotation!)
      self.delegate?.onInterceptAnnotationDoAction(self, annotation: dict)
  }
  
  func PDFViewBaseControllerInterceptWidgetDoAction(_ baseController: CPDFViewBaseController, forAnnotation annotation: CPDFWidgetAnnotation?) {
      if annotation == nil {
          return
      }
      let page = annotation?.page
      let pageUtil = RCTCPDFPageUtil(page: page)
      pageUtil.pageIndex = Int(page?.pageIndexInteger ?? 0)
      let dict = pageUtil.getForm(FormAnnotation: annotation!)
      self.delegate?.onInterceptWidgetDoAction(self, widget: dict)
  }
  
  // MARK: - Notification
  
  @objc func annotationsOperationChangeNotification(_ notification: Notification) {
    self.delegate?.onAnnotationHistoryChanged(self)
  }
  
  @objc func pageChangedNotification(_ notification: Notification) {
    let pageIndex = self.pdfViewController?.pdfListView?.currentPageIndex ?? 0
    self.delegate?.onContentEditorHistoryChanged(self, pageIndex: pageIndex)
  }
  
  @objc func pageEditingDidChanged(_ notification: Notification) {
    guard let page = notification.object as? CPDFPage else {
      return
    }

    guard let currentPage = self.pdfViewController?.pdfListView?.document.page(at: UInt(Int(page.pageIndexInteger))),
          currentPage === page else {
      return
    }

    let pageIndex = page.pageIndexInteger
    self.delegate?.onContentEditorHistoryChanged(self, pageIndex: Int(pageIndex))
  }
  
  // MARK: - RCT Methods
  
  private var configuration: String = ""
  private var document: URL = URL(fileURLWithPath: "")
  private var password: String = ""
  private var pageIndex: Int = 0
  
  private var isDocumentSet = false
  private var isConfigurationSet = false
  private var isPasswordSet = false
  private var hasCreatedView = false
  private var isPageIndexSet = false
  
  @objc func setConfiguration(_ newSection: String) {
    configuration = newSection
    isConfigurationSet = !configuration.isEmpty
    tryCreateCPDFViewIfReady()
  }
  
  
  @objc func setDocument(_ newSection: URL) {
    document = newSection
    isDocumentSet = !document.path.isEmpty
    tryCreateCPDFViewIfReady()
  }
  
  
  @objc func setPassword(_ newSection: String) {
    password = newSection
    isPasswordSet = true
    tryCreateCPDFViewIfReady()
  }
  
  @objc func setPageIndex(_ pageIndex: Int) {
    self.pageIndex = pageIndex
    self.isPageIndexSet = true
    tryCreateCPDFViewIfReady()
  }
  
  private func tryCreateCPDFViewIfReady() {
    guard !hasCreatedView else { return }
    if isDocumentSet && isConfigurationSet && isPasswordSet && isPageIndexSet {
      hasCreatedView = true
      createCPDFView()
    }
  }
  
  public var onChange: RCTBubblingEventBlock?
  @objc func setOnChange(_ newSection: @escaping RCTBubblingEventBlock) {
    onChange = newSection
  }
}
