//
//  KMMainDocument.swift
//  PDF Reader Pro
//
//  Created by wanjun on 2022/12/6.
//

import Cocoa
import CoreFoundation

@objc enum KMArchiveMask: Int {
    case diskImage       = 1
    case email           = 2
}

@objc enum KMExportOption: Int {
    case `default` = 0
    case withoutNotes
    case withEmbeddedNotes
}

typealias KMMainDocumentCloudUploadHanddler = (@escaping(Bool, String)->()) -> ()
@objcMembers class KMMainDocument: CTTabContents {
    struct MDFlags {
        var exportOption: UInt32 // assuming this is a 2-bit field, change to appropriate data type
        var exportUsingPanel: UInt32 // assuming this is a 1-bit field, change to appropriate data type
        var gettingFileType: UInt32 // assuming this is a 1-bit field, change to appropriate data type
        var convertingNotes: UInt32 // assuming this is a 1-bit field, change to appropriate data type
        var needsPasswordToConvert: UInt32 // assuming this is a 1-bit field, change to appropriate data type
    }
    
    static let kLastExportedTypeKey = "SKLastExportedType"
    static let kLastExportedOptionKey = "SKLastExportedOption"
    
    static let kBundleDataFilename = "data"
    
    var mainViewController: KMMainViewController?
    var homeWindowController: KMHomeWindowController?
    var homeViewController: KMHomeViewController?
    var bookmarkSheetController: KMBookmarkSheetController?
    var bookmarkController: KMBookmarkController?
    var isNewCreated: Bool = false
    var closedByUserGestureFlag: Bool = false   // 标记 closedByUserGesture 这个状态需要延后存储(如果需要)
    var cloud: Bool = false
    var cloudUploadHanddler: KMMainDocumentCloudUploadHanddler?
    var isUnlockFromKeychain: Bool = false
    private var _saveAsing = false
    var fileUpdateChecker: SKFileUpdateChecker?
    var mdFlags: MDFlags?
    var currentDocumentSetup: [String: Any] {
        get {
            var tempSetup: [String: Any] = [:]
            let tempMainSetup: [String: Any] = mainViewController?.currentSetup() ?? [:]
            let filePath = fileURL?.path ?? ""
            if filePath.count > 0{
                tempSetup.updateValue(filePath, forKey: KMDocumentSetupFileNameKey)
            }else {
                return tempSetup
            }
            if let alias = SKAlias.init(url: fileURL){
                if let data = alias.data{
                    tempSetup.updateValue(data as Any, forKey: KMDocumentSetupAliasKey)
                }
            }
            
            if tempSetup.count > 0 {
                tempSetup.merge(tempMainSetup) { (_, new) in new }
            }
            return tempSetup
        }
        set {
            
        }
    }
    
    private var _saveToURL: URL?
    var saveToURL: URL? {
        get {
            return self._saveToURL
        }
    }
    
    var exportAccessoryC: SKExportAccessoryController?
    
    var pdfData: Data?
    
    weak var watermarkSaveDelegate: AnyObject?
    private var _trackEvents = IndexSet()
    
    override func save(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, delegate: Any?, didSave didSaveSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
        if (self.isNewCreated) {
//            if let data = self.mainViewController, !data.isPDFDocumentEdited && !data.needSave && !self.isDocumentEdited {
                self._km_save(to: url, ofType: typeName, for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
                return
//            }
        }
        if (!self.needSaveWatermark()) {
            self._km_save(to: url, ofType: typeName, for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
            return
        }
        
        var openAccessoryView = self.watermarkSaveDelegate != nil
        if (openAccessoryView) {
            if let _browser = self.watermarkSaveDelegate as? KMBrowser, _browser.isCloseAllTabViewItem {
                openAccessoryView = false
            }
        }
        
        self._km_saveForWatermark(openAccessoryView: openAccessoryView) { [unowned self] in
            self.trackEvents()
        } callback: { [unowned self] needSave, params in
            if (needSave) {
                self._km_save(to: url, ofType: typeName, for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
            } else { // 水印保存
                if (self.watermarkSaveDelegate == nil) {
                    if let data = params.first as? KMResult, data == .cancel {
                        if let shouldClose = params.last as? Bool, shouldClose {
                            DispatchQueue.main.async {
                                self.mainViewController?.browserWindowController?.browser.windowDidBeginToClose()
                            }
                        }
                    } else {
                        DispatchQueue.main.async {
                            self.mainViewController?.browserWindowController?.browser.windowDidBeginToClose()
                        }
                    }
                    return
                }
                if let data = params.first as? KMResult, data == .cancel {
                    if var shouldClose = params.last as? Bool {
                        if let _browser = self.watermarkSaveDelegate as? KMBrowser, _browser.isCloseAllTabViewItem {
                            shouldClose = true
                        }
                        (self.watermarkSaveDelegate as? KMBrowser)?.document(self, shouldClose: shouldClose, contextInfo: nil)
                    }
                } else {
                    (self.watermarkSaveDelegate as? KMBrowser)?.document(self, shouldClose: true, contextInfo: nil)
                }
                self.watermarkSaveDelegate = nil
            }
        }
    }
    
    override func makeWindowControllers() {
        // Returns the storyboard that contains your document window.
        
        if ((self.fileURL?.path) != nil) {
            if !self.fileURL!.path.isPDFValid() {
                let alert = NSAlert()
                alert.alertStyle = .critical
                alert.messageText = NSLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: "")
                alert.runModal()
                return
            }
        }
        
        let mainWindow = NSApp.mainWindow
        var currentWindowController: KMBrowserWindowController?
        if mainWindow != nil {
            let windowController = mainWindow!.windowController
            if windowController is KMBrowserWindowController {
                currentWindowController = (windowController as! KMBrowserWindowController)
            } else {
                for window in NSApp.windows {
                    let windowController = window.windowController
                    if windowController is KMBrowserWindowController {
                        currentWindowController = (windowController as! KMBrowserWindowController)
                        break
                    }
                }
            }
        } else {
            for window in NSApp.windows {
                let windowController = window.windowController
                if windowController is KMBrowserWindowController {
                    currentWindowController = (windowController as! KMBrowserWindowController)
                    break
                }
            }
        }
        
//        if (currentWindowController == nil) && (self.fileURL != nil) {
//            let browser = KMBrowser.init() as KMBrowser
//            browser.addHomeTabContents()
//            browser.windowController = KMBrowserWindowController.init(browser: browser)
//            currentWindowController = browser.windowController as? KMBrowserWindowController
//        }
        
        if currentWindowController?.browser == nil && (self.fileURL != nil) {
            let browser: KMBrowser = KMBrowser.init()
            browser.windowController = KMBrowserWindowController.init(browser: browser)
            browser.addHomeTabContents()
            currentWindowController = browser.windowController as? KMBrowserWindowController
            browser.windowController.showWindow(self)
        }
        
        mainViewController = KMMainViewController.init()
        mainViewController?.myDocument = self
        self.mdFlags = MDFlags(exportOption: 0, exportUsingPanel: 0, gettingFileType: 0, convertingNotes: 0, needsPasswordToConvert: 0)
        
        if ((self.fileURL?.path) != nil) {
            let pdfDocument = CPDFDocument.init(url: URL(fileURLWithPath: self.fileURL!.path))
            mainViewController?.document = pdfDocument
        }
        if mainViewController?.document == nil {
            return
        }
        self.view = mainViewController?.view
        
        if let url = self.fileURL {
            AppSandboxFileAccess().persistPermissionURL(url as URL)
            let bookmarkData = try?url.bookmarkData(options: NSURL.BookmarkCreationOptions.withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil)
            if bookmarkData != nil {
                AppSandboxFileAccess().bookmarkPersistanceDelegate.setBookmarkData(bookmarkData! as Data, for: url as URL)
                AppSandboxFileAccess().bookmarkPersistanceDelegate.setBookmarkData(bookmarkData! as Data, for: NSURL(fileURLWithPath: url.path) as URL)
            }
        }
        
        if let currentBrowser = currentWindowController?.browser {
            let activeBrowser = currentBrowser.activeTabContents()
            let activeIndex = currentBrowser.activeTabIndex()
            let ishome = activeBrowser?.isHome ?? false
            let isfirstTab = (activeIndex == 0)
            if ishome && !isfirstTab { // 替换 【标签需要被替换】
                self.addWindowController(currentWindowController!)
                self.mainViewController?.browserWindowController = currentWindowController
                
                // 替换 document
                currentWindowController?.browser.replaceTabContents(at: Int32(activeIndex), with: self)
                // 刷新标签
                currentWindowController?.browser.updateTabState(at: Int32(activeIndex))
                
                // 刷新 home icon
                if let tabStripController = currentWindowController?.tabStripController {
                    if let view = tabStripController.view(at: UInt(activeIndex)) as? CTTabView {
                        view.controller().isHome = self.isHome
                        view.controller().isNewTab = self.isNewTab
                        view.controller().updateUI()
                    }
                }
            } else {
                if currentWindowController?.browser.tabCount() ?? 0 > 1 && (!KMMemberInfo.shared.isMemberAllFunction || KMPreference.shared.openDocumentType == .newWindow){
                        // 开启新窗口
                        let browser = KMBrowser.init() as KMBrowser
                        browser.windowController = KMBrowserWindowController.init(browser: browser)
                        browser.addHomeTabContents()
                        browser.windowController.showWindow(self)
                        browser.add(self, at: Int32()-1, inForeground: true)
                        self.addWindowController(browser.windowController)
                        self.mainViewController?.browserWindowController = browser.windowController as? KMBrowserWindowController
                }else { // 正常拼接到后面
                    self.addWindowController(currentWindowController!)
                    self.mainViewController?.browserWindowController = currentWindowController
                    currentWindowController?.browser.add(self, at: Int32()-1, inForeground: true)
                }
            }
        }
    }
    
    override func showWindows() {
        super.showWindows()
        
        self.setDataFromTmpData()
    }
    
    override func windowControllerDidLoadNib(_ aController: NSWindowController) {
        super.windowControllerDidLoadNib(aController)
        
        self.setDataFromTmpData()
        
        fileUpdateChecker = SKFileUpdateChecker.init(for: self)
        
        fileUpdateChecker?.isEnabled = true
        
        
    }
    
    override func save(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType) async throws {
        do {
            try await super.save(to: url, ofType: typeName, for: saveOperation)
        } catch let outError {
            NSApp.presentError(outError)
        }
        
        if saveOperation == .saveToOperation {
            if let data = self.mdFlags?.exportUsingPanel, data == 1 {
                if FileManager.default.fileExists(atPath: url.path) {
                    let ws = NSWorkspace.shared
                    ws.activateFileViewerSelecting([url])
                }
            }
        }
        
        self.mdFlags?.exportUsingPanel = 0
        self.mdFlags?.exportOption = UInt32(KMExportOption.default.rawValue)
    }
    
    override func write(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, originalContentsURL absoluteOriginalContentsURL: URL?) throws {
        try self._km_write(to: url, ofType: typeName, for: saveOperation, originalContentsURL: absoluteOriginalContentsURL)
    }
    
    override func canClose(withDelegate delegate: Any, shouldClose shouldCloseSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
        let isPrompt = KMPreferenceManager.shared.closeFileIsPrompt()
        if (isPrompt) {
            super.canClose(withDelegate: delegate, shouldClose: shouldCloseSelector, contextInfo: contextInfo)
            
            if KMAdvertisementManager.manager.appClosedCount == 0 {
                KMAdvertisementManager.manager.appClosedCount = 1
            }
            
            return
        }

        if (self.isNewCreated) {
            self.save(nil)
        } else if (self.isDocumentEdited) {
            self.save(nil)
        } else if (mainViewController != nil) {
            if self.mainViewController!.isPDFDocumentEdited || self.mainViewController!.needSave {
                self.save(nil)
            }
        }

        super.canClose(withDelegate: delegate, shouldClose: shouldCloseSelector, contextInfo: contextInfo)
    }
    
    override func saveAs(_ sender: Any?) {
        if (!self.needSaveWatermark()) {
            self._km_saveAs(sender)
            return
        }
        self._km_saveForWatermark { [weak self] needSave, _ in
            if (needSave) {
                self?._km_saveAs(sender)
            }
        }
    }
    
    override func saveTo(_ sender: Any?) {
        guard let pdfDoc = self.mainViewController?.listView.document else {
            NSSound.beep()
            return
        }
        if pdfDoc.allowsPrinting == false || pdfDoc.allowsCopying == false {
            Task {
                _ = await KMAlertTool.runModel(message: NSLocalizedString("This is a secured document. Editing is not permitted.", comment: ""))
            }
            return
        }
        
        let idx = (sender as? NSMenuItem)?.tag ?? 0
        var typeName = KMPDFDocumentType
        if idx == 0 {
            typeName = KMPDFDocumentType
        } else if idx == 1 {
            typeName = KMPDFBundleDocumentType
        } else if idx == 2 {
            typeName = KMNotesDocumentType
        } else if idx == 3 {
            typeName = KMNotesTextDocumentType
        } else if idx == 4 {
            typeName = KMNotesRTFDocumentType
        } else if idx == 5 {
            typeName = KMNotesRTFDDocumentType
        } else if idx == 6 {
            typeName = KMNotesDocumentType
        }
        
        KMDataManager.ud_set(typeName, forKey: Self.kLastExportedTypeKey)
        super.saveTo(sender)
    }
    
    override func prepareSavePanel(_ savePanel: NSSavePanel) -> Bool {
        let success = super.prepareSavePanel(savePanel)
        let exportUsingPanel = self.mdFlags?.exportUsingPanel ?? 0
        if success && exportUsingPanel > 0 {
//             *formatPopup = [[savePanel accessoryView] subviewOfClass:[NSPopUpButton class]];
            var formatPopup: NSPopUpButton?
            let svs = savePanel.accessoryView?.subviews.first?.subviews ?? []
            for sv in svs {
                if let data = sv as? NSPopUpButton {
                    formatPopup = data
                    break
                }
            }
            
            self._removeSavePanelOfFormatPopupItems(savePanel)
            if (formatPopup != nil) {
                let lastExportedType = KMDataManager.ud_string(forKey: Self.kLastExportedTypeKey)
                var lastExportedOption = KMDataManager.ud_integer(forKey: Self.kLastExportedOptionKey)
                if lastExportedOption == 0 {
                    lastExportedOption = KMExportOption.withEmbeddedNotes.rawValue
                }
//                if (lastExportedType != nil) {
//                    let idx = formatPopup?.indexOfItem(withRepresentedObject: lastExportedType) ?? -1
//                    let selectedIdx = formatPopup?.indexOfSelectedItem ?? 0
//                    if idx != -1 && idx != selectedIdx {
//                        formatPopup?.selectItem(at: idx)
//                        formatPopup?.sendAction(formatPopup?.action, to: formatPopup?.target)
//                        [savePanel setAllowedFileTypes:[NSArray arrayWithObjects:[self fileNameExtensionForType:lastExportedType saveOperation:NSSaveToOperation], nil]];
//                        if let data = self.fileNameExtension(forType: lastExportedType!, saveOperation: .saveToOperation) {
//                            savePanel.allowedFileTypes = [data]
//                        }
//                    }
//                }
                self.mdFlags?.exportOption = UInt32(lastExportedOption)
//
//                self.exportAccessoryC = SKExportAccessoryController()
//                self.exportAccessoryC?.addFormatPopUpButton(formatPopup)
//                self.exportAccessoryC?.matrix.target = self
//                self.exportAccessoryC?.matrix.action = #selector(changeExportOption)
//                savePanel.accessoryView = self.exportAccessoryC?.view
//                self._updateExportAccessoryView()
            }
        } else if success {
            self._removeSavePanelOfFormatPopupItems(savePanel)
        }
        return success
    }
    
    override func runModalSavePanel(for saveOperation: NSDocument.SaveOperationType, delegate: Any?, didSave didSaveSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
        self.mdFlags?.exportUsingPanel = saveOperation == .saveToOperation ? 1 : 0
        self.mdFlags?.exportOption = UInt32(KMExportOption.default.rawValue)
        if (self.isNewCreated) {
//            if let data = self.mainViewController, !data.isPDFDocumentEdited && !data.needSave && !self.isDocumentEdited {
                self._km_runModalSavePanel(for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
                return
//            }
        }
        
        if (!self.needSaveWatermark()) {
            self._km_runModalSavePanel(for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
            return
        }
        self._km_saveForWatermark { [weak self] needSave, _ in
            if (needSave) {
                self?._km_runModalSavePanel(for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
            }
        }
    }
    
    override func save(_ sender: Any?) {
        if (!self.needSaveWatermark()) {
            self._km_save(sender)
            return
        }
        self._km_saveForWatermark { [weak self] in
            self?.trackEvents()
        } callback: { [weak self] needSave, _ in
            if (needSave) {
                self?._km_save(sender)
            }
        }
    }
    
    func systemInteractionMode() -> KMInteractionMode {
        let mainWindow = NSApp.mainWindow
        if mainWindow != nil {
            let windowController = mainWindow!.windowController
            if windowController?.window?.screen?.isEqual(NSScreen.screens[0]) ?? false{
                return mainViewController?.interactionMode ?? .normal
            }
        }
        
        return .normal
    }
    
    func saveForWatermark() {
        if (!self.needSaveWatermark()) {
            self._km_save(nil)
            return
        }
        
        self._km_saveForWatermark { [weak self] in
            self?.trackEvents()
        } callback: { [unowned self] needSave, params in
            if (needSave) {
                self._km_save(nil)
            } else { // 水印保存
                if (self.watermarkSaveDelegate == nil) {
                    if let data = params.first as? KMResult, data == .cancel {
                        if let shouldClose = params.last as? Bool, shouldClose {
                            DispatchQueue.main.async {
                                self.mainViewController?.browserWindowController?.browser.windowDidBeginToClose()
                            }
                        }
                    } else {
                        DispatchQueue.main.async {
                            self.mainViewController?.browserWindowController?.browser.windowDidBeginToClose()
                        }
                    }
                    return
                }
                if let data = params.first as? KMResult, data == .cancel {
                    if var shouldClose = params.last as? Bool {
                        if let _browser = self.watermarkSaveDelegate as? KMBrowser, _browser.isCloseAllTabViewItem {
                            shouldClose = true
                        }
                        (self.watermarkSaveDelegate as? KMBrowser)?.document(self, shouldClose: shouldClose, contextInfo: nil)
                    }
                } else {
                    (self.watermarkSaveDelegate as? KMBrowser)?.document(self, shouldClose: true, contextInfo: nil)
                }
                self.watermarkSaveDelegate = nil
            }
        }
    }
    
    override func read(from absoluteURL: URL, ofType typeName: String) throws {
        do {
            try super.read(from: absoluteURL, ofType: typeName)
            updateChangeCount(.changeCleared)
        } catch let outError {
            Swift.print(outError)
        }
    }
    
    override func read(from data: Data, ofType typeName: String) throws {
        // Insert code here to read your document from the given data of the specified type, throwing an error in case of failure.
        // Alternatively, you could remove this method and override read(from:ofType:) instead.  If you do, you should also override isEntireFileLoaded to return false if the contents are lazily loaded.
        
//        let pdfDocument = CPDFDocument.init(data: data)
        self.pdfData = data
//        if pdfDocument == nil {
//            throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
//        }
    }
    
    // MARK: Autosaving
    
    override func close() {
        mainViewController?.cancelMeasureType()
        if self.isActive {
            if browser != nil {
                var activeIndex = 0
                let dex = browser.index(of: self)
                if dex == browser.tabCount() - 1 {
                    activeIndex = Int(browser.tabCount()-2)
                } else {
                    activeIndex = Int(dex + 1)
                }
                let activeContents = browser.tabContents(at: Int32(activeIndex))
                activeContents?.addWindowController(browser.windowController)
            }
        }
        super.close()
    }
    

    // MARK: init
    override init() {
        super.init()
        // Add your subclass-specific initialization here.
        
        NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.init(rawValue: "CPDFListViewAnnotationsAttributeHasChangeNotification"), object: nil)
//        NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.init(rawValue: "CPDFViewDocumentChangedNotification"), object: nil)
//        NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.init(rawValue: "CPDFViewPageChangedNotification"), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.init(rawValue: "CPDFListViewDidAddAnnotationNotification"), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.CPDFViewPageChanged, object: nil)
    }

    override init?(baseTabContents baseContents: CTTabContents?) {
        super.init(baseTabContents: baseContents)
        if isHome {
            homeViewController = KMHomeViewController.init()
            homeViewController?.myDocument = self
            self.view = homeViewController?.view
        }
    }
    
    // MARK: Handling User Actions
    
    override var title: String? {
        get {
            if isHome {
                if (self.isNewTab) {
                    return NSLocalizedString("New Tab", comment: "")
                } else {
                    return NSLocalizedString("Home", comment: "")
                }
            } else {
                return fileURL?.lastPathComponent
            }
        }
        set {
            super.title = newValue
        }
    }
    
    func needSaveWatermark() -> Bool {
        if let need = self.mainViewController?.saveWatermarkFlag {
            return need
        }
        return false
    }
    
    func changeExportOption(_ sender: NSMatrix?) {
        self.mdFlags?.exportOption = UInt32(sender?.selectedCell()?.tag ?? 0)
    }
    
    // MARK: Private Methods

    func pdfChangedNotification(_ notification: Notification) -> Void {
        if !isHome {
            let mainViewController = mainViewController
            var document: CPDFDocument!
            
            let dic = notification.object as? NSDictionary
            if dic?["object"] is CPDFAnnotation {
                guard let annotation = dic?["object"] as? CPDFAnnotation else { return }
                document = annotation.page.document

            } else if dic?["object"] is CPDFListView {
                let pdflistView = notification.object as? CPDFListView
                document = pdflistView?.document
            }
            
            if mainViewController != nil {
                if document == mainViewController!.document {
                    updateChangeCount(.changeDone)
                }
            }
        }
    }
    
    override func updateChangeCount(_ change: NSDocument.ChangeType) {
        let mainWindow = NSApp.mainWindow
        var currentWindowController: KMBrowserWindowController?
        if mainWindow != nil {
            let windowController = mainWindow!.windowController
            if windowController is KMBrowserWindowController {
                currentWindowController = (windowController as! KMBrowserWindowController)
            } else {
                for window in NSApp.windows {
                    let windowController = window.windowController
                    if windowController is KMBrowserWindowController {
                        currentWindowController = (windowController as! KMBrowserWindowController)
                        break
                    }
                }
            }
        } else {
            for window in NSApp.windows {
                let windowController = window.windowController
                if windowController is KMBrowserWindowController {
                    currentWindowController = (windowController as! KMBrowserWindowController)
                    break
                }
            }
        }
        if let currentBroser = currentWindowController?.browser {
            if self.isEqual(to: currentBroser.activeTabContents()) {
                super.updateChangeCount(change)
                return
            }
        }

        super.updateChangeCount(.changeCleared)
    }

    func uploadToCloud(_ callback: (@escaping(Bool, String)->())) {
        guard let handdler = self.cloudUploadHanddler else {
            return
        }
        
        handdler(callback)
    }
    
    func isPDFDocument() -> Bool {
        return true
    }
    
    func setDataFromTmpData() {
        guard let _document = self.mainViewController?.document else {
            return
        }
//        self.tryToUnlockDocument(document!)
        if (_document.permissionsStatus != .owner) {
            var password: NSString? = nil
            let fileId = self.fileId(for: _document)
            if (fileId.isEmpty) {
                return
            }
            self.getPassword(&password, fileId: fileId)
            if (password != nil) {
                self.isUnlockFromKeychain = true
//                document.unlock(withPassword: password! as String)
                self.mainViewController?.model.password = password as String?
            }
        }
        
        
        //如果已存在,开个存在页签
        var selectDocument: KMMainDocument? = self
        if selectDocument != nil {
            if let browser_ = selectDocument?.browser {
                let currentIndex = browser_.tabStripModel.index(of: selectDocument)
                browser_.tabStripModel.selectTabContents(at: Int32(currentIndex), userGesture: true)
                if browser_.window.isVisible {
                    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
                        browser_.window.orderFront(nil)
                    }
                } else if browser_.window.isMiniaturized {
                    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
                        browser_.window.orderFront(nil)
                    }
                }
            }
        }
    }
    
    func tryToUnlockDocument(_ document: CPDFDocument) {
        if (document.permissionsStatus != .owner) {
            var password: NSString? = nil
            let fileId = self.fileId(for: document)
            if (fileId.isEmpty) {
                return
            }
            self.getPassword(&password, fileId: fileId)
            if (password != nil) {
                self.isUnlockFromKeychain = true
                document.unlock(withPassword: password! as String)
            }
        }
    }
    
    func km_updateChangeCount(_ change: NSDocument.ChangeType) {
        super.updateChangeCount(change)
    }
    
    func trackEvents() {
        km_synchronized(self) {
            for i in self._trackEvents {
                if let type = KMSubscribeWaterMarkType(rawValue: i) {
                    KMTools.trackEvent(type: type)
                }
            }
        }
        
        self.clearTrackEvents()
    }
    
    func recordTrackEvent(type: KMSubscribeWaterMarkType) {
        if (type == .none) {
            return
        }
        
        km_synchronized(self) {
            self._trackEvents.insert(type.rawValue)
        }
    }
    
    func clearTrackEvents() {
        km_synchronized(self) {
            self._trackEvents.removeAll()
        }
    }
    
    @IBAction func saveArchive(_ sender: Any?) {
        guard let item = sender as? NSMenuItem else {
            NSSound.beep()
            return
        }
        guard let fileURL = self.fileURL else {
            NSSound.beep()
            return
        }
        let check = try?fileURL.checkResourceIsReachable()
        if check == false || self.isDocumentEdited {
            let msg = KMLocalizedString("You must save this file first", "Alert text when trying to create archive for unsaved document")
            let inf = KMLocalizedString("The document has unsaved changes, or has not previously been saved to disk.", "Informative text in alert dialog")
            Task {
                _ = await KMAlertTool.runModel(message: msg, informative: inf)
            }
            return
        }
//        NSString *ext = ([sender tag] | SKArchiveDiskImageMask) ? @"dmg" : @"tgz";
        let idx = item.tag
        let ext = (idx == 1 || idx == 3) ? "dmg" : "tgz"
        let isEmail = (idx == 2 || idx == 3)
        if isEmail  {
//            if (([sender tag] | SKArchiveEmailMask)) {
            let tmpDirURL = FileManager.default.uniqueChewableItemsDirectoryURL()
            let tmpFileURL = tmpDirURL.appendingPathComponent(fileURL.lastPathComponentReplacingPathExtension(ext))
            self.newSaveArchive(to: tmpFileURL, email: true)
        } else {
            let sp = NSSavePanel()
            sp.allowedFileTypes = [ext]
            sp.canCreateDirectories = true
            sp.nameFieldStringValue = fileURL.lastPathComponentReplacingPathExtension(ext)
            sp.beginSheetModal(for: self.windowForSheet!) { result in
                if result == .OK {
                    self.newSaveArchive(to: sp.url!, email: false)
                }
            }
        }
    }
    
//    func saveArchiveToURL(to fileURL: URL, email: Bool) {
//        NSTask *task = [[[NSTask alloc] init] autorelease];
//        let task = Task()
//        if fileURL.pathExtension == "dmg" {
//            [task setLaunchPath:@""];
//            task.launchPath = "/usr/bin/hdiutil"
//            [task setArguments:[NSArray arrayWithObjects:@"create", @"-srcfolder", [[self fileURL] path], @"-format", @"UDZO", @"-volname", [[fileURL lastPathComponent] stringByDeletingPathExtension], [fileURL path], nil]];
//        } else {
//            [task setLaunchPath:@"/usr/bin/tar"];
//            [task setArguments:[NSArray arrayWithObjects:@"-czf", [fileURL path], [[self fileURL] lastPathComponent], nil]];
//        }
//        [task setCurrentDirectoryPath:[[[self fileURL] URLByDeletingLastPathComponent] path]];
//        [task setStandardOutput:[NSFileHandle fileHandleWithNullDevice]];
//        [task setStandardError:[NSFileHandle fileHandleWithNullDevice]];
//
//        SKAttachmentEmailer *emailer = nil;
//        if (email)
//            emailer = [SKAttachmentEmailer attachmentEmailerWithFileURL:fileURL subject:[self displayName] waitingForTask:task];
//
//        @try {
//            [task launch];
//        }
//        @catch (id exception) {
//            [emailer taskFailed];
//        }
//    }
    
    @IBAction func readNotes(_ sender: Any?) {
        KMPrint("readNotes")
    }
    @IBAction func convertNotes(_ sender: Any?) {
        KMPrint("convertNotes")
    }
    @IBAction func batchRemovePassWord(_ sender: Any?) {
        self.mainViewController?.clickChildTool(type: .secure, index: 2)
    }
    @IBAction func batchRemovPrivatySecurity(_ sender: Any?) {
        self.mainViewController?.removeOwnerPassword()
    }
    @IBAction func printPDFDocument(_ sender: Any?) {
        self.mainViewController?.saveDocument()
        KMPrintWindowController.showNewPrintWindowControll(inputDocument: self.mainViewController?.document, inputPageRange: KMPrintPageRange())
    }
    @IBAction func performFindPanelAction(_ sender: Any?) {
        self.mainViewController?.toolbarController.showFindBar()
    }
    
    @IBAction func addBookmark(_ sender: Any?) {
        guard let item = sender as? NSMenuItem else {
            return
        }
        
//        let bookmarkSheetController = SKBookmarkSheetController(windowNibName: "BookmarkSheet")
//        NSWindow.currentWindow().beginSheet(bookmarkSheetController.window!) { resoponse in
//            
//        }
//        bookmarkSheetController.textField.stringValue = self.displayName
//        bookmarkSheetController.beginSheetModal(for: self.windowForSheet) { [unowned self] result in
//            if (result == NSApplication.ModalResponse.OK.rawValue) {
//                let label = bookmarkSheetController.textField.stringValue;
//                let folder: SKBookmark = bookmarkSheetController.selectedFolder ?? SKBookmarkController.shared().bookmarkRoot
//                var bookmark = SKBookmark()
//                switch (item.tag) {
//                    case 0:
//                        let mainViewController = self.mainViewController
//                        let page = mainViewController?.listView.currentPage()
//                        bookmark = SKBookmark.bookmark(with: self.fileURL, pageIndex: (page?.pageIndex())!, label: label) as! SKBookmark
//                    case 1:
//                        let setup = self.currentDocumntSetup()
//                        bookmark = SKBookmark.bookmark(withSetup: setup, label: label) as! SKBookmark
//                    
//                    case 2:
//                        let setups = NSApp.orderedDocuments.map { $0.value(forKey: "currentDocumentSetup") }
//                        bookmark = SKBookmark.bookmarkSession(withSetups: setups as [Any], label: label) as! SKBookmark
//                    
//                    default:
//                        break;
//                }
//                folder.mutableArrayValue(forKey: "children").add(bookmark)
//            }
//        }
        
        if item.tag == 3 {
            KMPrint("Edit Bookmark")
            bookmarkController = KMBookmarkController.showBookmarkController()
        } else if item.tag == 2 {
            KMPrint("session Bookmark")
            bookmarkSheetController = KMBookmarkSheetController.showBookmarkSheetController(type: .session)
        } else if item.tag == 0 {
            KMPrint("add Bookmark")
            bookmarkSheetController = KMBookmarkSheetController.showBookmarkSheetController(type: .bookmark)
        }
        
        bookmarkSheetController?.stringValue = self.displayName
        bookmarkSheetController?.cancelAction = { [weak self] controller, type in
            
        }
//        
        bookmarkSheetController?.doneAction = { [unowned self] controller, type, label in
            let folder = controller.selectedFolder
            var bookmark: KMBookmark?

            switch type {
            case .bookmark:
                let mainViewController = mainViewController
                
                if let page = mainViewController?.listView.currentPage() {
                    let index: UInt = page.pageIndex()
                    bookmark = KMBookmark.bookmark(url: self.fileURL!, pageIndex: index, label: label)
                }
            case .setup: break
                let setup = currentDocumentSetup
                bookmark = KMBookmark.bookmark(setup: setup, label: label)
            case .session:
                let setups = NSApp.orderedDocuments.compactMap { $0.value(forKey:"currentDocumentSetup") }
                bookmark = KMSessionBookmark.bookmarkSession(setups: setups as NSArray, label: label)
            default:
                break
            }

            if let bookmark = bookmark {
                folder?.children.append(bookmark)
            }
            KMBookmarkManager.manager.saveData()
        }
    }
    
    func currentDocumntSetup() -> [String: Any] {
        var setup: [String: Any] = [:]
        
        let data = SKAlias.init(url: fileURL).data
        if (data != nil) {
            setup.updateValue(data as Any, forKey: "_BDAlias")
        } else {
            setup.updateValue(fileURL?.path as Any, forKey: "fileName")
        }
        return setup;
    }
    
    
    
    @IBAction func showWindow(_ sender: Any?) {
        KMPrint("showWindow")
    }
        
    // MARK: - Private Methods
    
    private func _PDFBundleFileWrapper(for name: String) -> FileWrapper {
        var aName = name
        if name.isCaseInsensitiveEqual(Self.kBundleDataFilename) {
            aName = aName + "1"
        }
        
//        var data: Data?
        let fileWrapper = FileWrapper(directoryWithFileWrappers: [:])
        let info = KMInfoWindowController.shared.info(for: self)
//        NSDictionary *options = [[self mainWindowController] presentationOptions];
//        if (options) {
//            info = [[info mutableCopy] autorelease];
//            [(NSMutableDictionary *)info setObject:options forKey:SKPresentationOptionsKey];
//        }
        if let data = self.pdfData {
            fileWrapper.addRegularFile(withContents: data, preferredFilename: aName + ".pdf")
        }
        if let data = self.mainViewController?.document?.string()?.data(using: .utf8) {
            fileWrapper.addRegularFile(withContents: data, preferredFilename: Self.kBundleDataFilename + ".txt")
        }
        if let data = try?PropertyListSerialization.data(fromPropertyList: info, format: .xml, options: PropertyListSerialization.WriteOptions(0)) {
            fileWrapper.addRegularFile(withContents: data, preferredFilename: Self.kBundleDataFilename + ".plist")
        }
//        if ((data = [NSPropertyListSerialization dataWithPropertyList:info format:NSPropertyListXMLFormat_v1_0 options:0 error:NULL]))
//            [fileWrapper addRegularFileWithContents:data preferredFilename:[BUNDLE_DATA_FILENAME stringByAppendingPathExtension:@"plist"]];
//        if ([[self notes] count] > 0) {
//            if ((data = [self notesData]))
//                [fileWrapper addRegularFileWithContents:data preferredFilename:[name stringByAppendingPathExtension:@"skim"]];
//            if ((data = [[self notesString] dataUsingEncoding:NSUTF8StringEncoding]))
//                [fileWrapper addRegularFileWithContents:data preferredFilename:[name stringByAppendingPathExtension:@"txt"]];
//            if ((data = [self notesRTFData]))
//                [fileWrapper addRegularFileWithContents:data preferredFilename:[name stringByAppendingPathExtension:@"rtf"]];
//            if ((data = [self notesFDFDataForFile:[name stringByAppendingPathExtension:@"pdf"] fileIDStrings:[[self pdfDocument] fileIDStrings]]))
//                [fileWrapper addRegularFileWithContents:data preferredFilename:[name stringByAppendingPathExtension:@"fdf"]];
//        }
        return fileWrapper
    }
    
    private func _km_write(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, originalContentsURL absoluteOriginalContentsURL: URL?) throws {
        if typeName == KMPDFBundleDocumentType {
//            NSFileWrapper *fileWrapper = [self PDFBundleFileWrapperForName:[[absoluteURL lastPathComponent] stringByDeletingPathExtension]];
//            if (fileWrapper)
//                didWrite = [fileWrapper writeToURL:absoluteURL options:0 originalContentsURL:nil error:&error];
//            else
//                error = [NSError writeFileErrorWithLocalizedDescription:NSLocalizedString(@"Unable to write file", @"Error description")];
            let fileWrapper = self._PDFBundleFileWrapper(for: url.deletingPathExtension().lastPathComponent)
            do {
                try fileWrapper.write(to: url, options: FileWrapper.WritingOptions(rawValue: 0), originalContentsURL: nil)
            } catch {
                NSApp.presentError(error)
            }
            return
        }
        var success = true
        if !self.isHome {
            if mainViewController != nil {
                mainViewController?.savePdfAlertView()
                if mainViewController?.document != nil {
                    self.mainViewController?.commitEditingIfNeed()
                    
//                    if mainViewController!.document!.isEncrypted {
//                        success = mainViewController!.document!.write(to: url)
//                    } else {
                        if (mainViewController!.needSave) {
                            if let options = self.mainViewController?.secureOptions, !options.isEmpty {
                                self.mainViewController!.document?.setDocumentAttributes(self.mainViewController?.documentAttribute)
                                success = self.mainViewController!.document!.write(to: url, withOptions: options)
                            } else if let flag = self.mainViewController?.removeSecureFlag, flag {
                                success = self.mainViewController!.document!.writeDecrypt(to: url)
                            } else {
                                if(mainViewController?.hasEnterRedact() == true) {
                                    success = mainViewController?.redactController.redactPdfView.document?.write(to: url) ?? false
                                } else {
                                    success = mainViewController!.document!.write(to: url)
                                }
                            }
                        } else {
                            if(mainViewController?.hasEnterRedact() == true) {
                                success = mainViewController?.redactController.redactPdfView.document?.write(to: url) ?? false
                            } else {
                                success = mainViewController!.document!.write(to: url)
                            }
                        }
//                    }
                    self.mainViewController?.needSave = false
                    self.mainViewController?.clearSecureOptions()
                    self.mainViewController?.clearRemoveSecureFlag()
                }
            }
        } else {
            success = false
        }
        
        if (success && self._saveAsing) {
            if let tabView = self.browser?.windowController?.tabStripController?.activeTabView() as? CTTabView {
                tabView.controller()?.title = url.lastPathComponent
            }
            self._saveAsing = false
        }
        
        if success && isNewCreated && NSDocument.SaveOperationType.saveAsOperation == saveOperation {
            isNewCreated = false
        }
        if mainViewController != nil {
            mainViewController?.savePdfFinishAlertView()
        }
    }
    
    private func _km_saveForWatermark(openAccessoryView: Bool = true, subscribeDidClick: (()->Void)? = nil, callback:@escaping (_ needSave: Bool, _ param: Any...)->Void) {
        Task { @MainActor in
            if await (KMLightMemberManager.manager.canPayFunction() == false) {
                let _ = KMSubscribeWaterMarkWindowController.show(window: NSApp.mainWindow!, isContinue:false, type: .save) {
                    if let _callback = subscribeDidClick {
                        _callback()
                    }
                } completion: { isSubscribeSuccess, isWaterMarkExport, isClose in
                    if (isClose) {
                        callback(false, KMResult.cancel, false)
                        return
                    }
                    
                    if (isSubscribeSuccess) {
                        callback(true)
                        return
                    }
                    
                    if (isWaterMarkExport) {
                        guard let _document = self.mainViewController?.document else {
                            callback(false, KMResult.failure)
                            return
                        }
                        // 提交文本编辑的内容
                        self.mainViewController?.commitEditingIfNeed()
                        DispatchQueue.main.async {
                            NSPanel.savePanel(NSApp.mainWindow!, openAccessoryView, panel:{ panel in
                                if (!self.isNewCreated) {
                                    panel.directoryURL = _document.documentURL.deletingLastPathComponent()
                                }
                                
                                panel.nameFieldStringValue = _document.documentURL.lastPathComponent
                            }) { response, url, isOpen in
                                if (response == .cancel) {
                                    callback(false, KMResult.cancel, true)
                                    return
                                }
                                guard let _url = KMTools.saveWatermarkDocument(document: _document, to: url!, secureOptions: self.mainViewController?.secureOptions, documentAttribute: self.mainViewController?.documentAttribute,removePWD: self.mainViewController!.removeSecureFlag) else {
                                    callback(false, KMResult.failure)
                                    return
                                }
                                callback(false, KMResult.success)
                                if (isOpen) {
                                    NSDocumentController.shared.km_safe_openDocument(withContentsOf: _url, display: true) { _, _, _ in
                                        
                                    }
                                } else {
                                    NSWorkspace.shared.activateFileViewerSelecting([_url])
                                }
                            }
                        }
                        return
                    }
                    callback(false, KMResult.cancel, false)
                }
                return
            }
            callback(true)
        }
    }
    
    private func _km_save(_ sender: Any?) {
        super.save(sender)
    }
    
    private func _km_save(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, delegate: Any?, didSave didSaveSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
        self._saveToURL = url
        super.save(to: url, ofType: typeName, for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
        
//        self.mdFlags?.exportUsingPanel = 0
//        self.mdFlags?.exportOption = UInt32(KMExportOption.default.rawValue)
    }
    
    private func _km_saveAs(_ sender: Any?) {
        super.saveAs(sender)

        self._saveAsing = true
    }
    
    private func _km_runModalSavePanel(for saveOperation: NSDocument.SaveOperationType, delegate: Any?, didSave didSaveSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
        super.runModalSavePanel(for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
    }
    
    private func _updateExportAccessoryView() {
        let typeName = self.fileTypeFromLastRunSavePanel ?? ""
        let matrix = self.exportAccessoryC?.matrix
        matrix?.selectCell(withTag: Int(self.mdFlags?.exportOption ?? 0))
        
        if self._canAttachNotesForType(typeName) {
            matrix?.isHidden = false
            let ws = NSWorkspace.shared
            let isLocked = self.mainViewController?.listView.document.isLocked ?? false
            let allowsPrinting = self.mainViewController?.listView.document.allowsPrinting ?? false
            if ws.type(typeName, conformsToType: KMPDFDocumentType) && isLocked == false && allowsPrinting {
                (matrix?.cell(withTag: KMExportOption.withEmbeddedNotes.rawValue))?.isEnabled = true
            } else {
                (matrix?.cell(withTag: KMExportOption.withEmbeddedNotes.rawValue))?.isEnabled = false
                if let data = self.mdFlags?.exportOption, data == KMExportOption.withEmbeddedNotes.rawValue {
                    self.mdFlags?.exportOption = UInt32(KMExportOption.default.rawValue)
                    matrix?.selectCell(withTag: KMExportOption.default.rawValue)
                }
            }
        } else {
            matrix?.isHidden = true
        }
    }
    
    private func _canAttachNotesForType(_ typeName: String) -> Bool {
        let ws = NSWorkspace.shared
        return ws.type(typeName, conformsToType: KMPDFDocumentType) || ws.type(typeName, conformsToType: KMPostScriptDocumentType) || ws.type(typeName, conformsToType: KMDVIDocumentType) || ws.type(typeName, conformsToType: KMXDVDocumentType)
    }
    
    private func _removeSavePanelOfFormatPopupItems(_ savePanel: NSSavePanel) {
        var formatPopup: NSPopUpButton?
        let svs = savePanel.accessoryView?.subviews.first?.subviews ?? []
        for sv in svs {
            if let data = sv as? NSPopUpButton {
                formatPopup = data
                break
            }
        }
        
        if let item = formatPopup?.item(withTitle: "Notes as Text") {
            formatPopup?.removeItem(at: formatPopup!.index(of: item))
        }
        if let item = formatPopup?.item(withTitle: "Notes as FDF") {
            formatPopup?.removeItem(at: formatPopup!.index(of: item))
        }
        if let item = formatPopup?.item(withTitle: "Notes as RTFD") {
            formatPopup?.removeItem(at: formatPopup!.index(of: item))
        }
        if let item = formatPopup?.item(withTitle: NSLocalizedString("Text", comment: "")) {
            formatPopup?.removeItem(at: formatPopup!.index(of: item))
        }
        if let item = formatPopup?.item(withTitle: NSLocalizedString("text", comment: "")) {
            formatPopup?.removeItem(at: formatPopup!.index(of: item))
        }
        if let item = formatPopup?.item(withTitle: "PDF Reader Pro Edition Notes") {
            formatPopup?.removeItem(at: formatPopup!.index(of: item))
        }
        if let item = formatPopup?.item(withTitle: "XDV") {
            formatPopup?.removeItem(at: formatPopup!.index(of: item))
        }
        if let item = formatPopup?.item(withTitle: "DVI") {
            formatPopup?.removeItem(at: formatPopup!.index(of: item))
        }
        if let item = formatPopup?.item(withTitle: "Images") {
            formatPopup?.removeItem(at: formatPopup!.index(of: item))
        }
        if let item = formatPopup?.item(withTitle: "Encapsulated PostScript") {
            formatPopup?.removeItem(at: formatPopup!.index(of: item))
        }
    }
    
    // MARK: - Printing
    
    override func printOperation(withSettings printSettings: [NSPrintInfo.AttributeKey : Any]) throws -> NSPrintOperation {
        let printInfo = self.printInfo.copy() as! NSPrintInfo
        printInfo.dictionary().addEntries(from: printSettings)
        
        var printOperation: NSPrintOperation?
        if self.isHome {
            return NSPrintOperation()
        }
        let documentURL = self.mainViewController?.document?.documentURL
        if documentURL == nil {
            return NSPrintOperation()
        }

        guard let pdfDoc = PDFDocument(url: documentURL!) else {
            return NSPrintOperation()
        }

        if pdfDoc.responds(to: #selector(PDFDocument.printOperation(for:scalingMode:autoRotate:))) {
            printOperation = pdfDoc.printOperation(for: printInfo, scalingMode: .pageScaleNone, autoRotate: true)
        } else if pdfDoc.responds(to: #selector(PDFDocument.getPrintOperation(for:autoRotate:))) {
            printOperation = pdfDoc.getPrintOperation(for: printInfo, autoRotate: true)
        }
        
        // NSPrintProtected is a private key that disables the items in the PDF popup of the Print panel, and is set for encrypted documents
        if pdfDoc.isEncrypted {
            printOperation?.printInfo.dictionary().setValue(false, forKey: "NSPrintProtected")
        }
        
        let printPanel = printOperation?.printPanel
        printPanel?.options = [.showsCopies, .showsPageRange, .showsPaperSize, .showsOrientation, .showsScaling, .showsPreview]
        
        if printOperation == nil {
            throw NSError.printDocumentError(withLocalizedDescription: "")
        }
        
        return printOperation!
    }
    
    override func runModalPrintOperation(_ printOperation: NSPrintOperation, delegate: Any?, didRun didRunSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
        printOperation.run()
    }
}

extension PDFDocument {
    @objc func getPrintOperation(for printInfo: NSPrintInfo, autoRotate: Bool) -> NSPrintOperation {
        // 在此处实现方法的具体逻辑
        return NSPrintOperation()
    }
}

extension KMMainDocument {
    override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
        if (menuItem.action == #selector(save(_ :))) {
            if (self.isHome) {
                return false
            }
            if (self.isDocumentEdited) {
                return self.isDocumentEdited
            }
            
            guard let mainVC = self.mainViewController else {
                return false
            }
            return mainVC.isPDFDocumentEdited || mainVC.needSave
        } else if (menuItem.action == #selector(saveAs(_ :))) {
            return !self.isHome
        } else if menuItem.action == #selector(batchRemovPrivatySecurity) {
            if self.isHome {
                return false
            }
            guard let doc = self.mainViewController?.listView?.document else {
                return false
            }
            let allowsPrinting = doc.allowsPrinting
            let allowsCopying = doc.allowsCopying
            if allowsCopying && allowsPrinting {
                return false
            }
            return true
        } else if menuItem.action == #selector(saveArchive) {
            return !self.isHome
        } else if (menuItem.action == #selector(saveTo(_ :))) {
            return !self.isHome
        }  else if (menuItem.action == #selector(batchRemovePassWord)) {
            return !self.isHome
        } else if (menuItem.action == #selector(addBookmark)) {
            if menuItem.tag == 3 {
                return true
            }else {
                return !self.isHome
            }
        }
        return super.validateMenuItem(menuItem)
    }
}

extension NSDocument {
    @objc class func isDamage(url: URL) -> Bool {
        // 文件路径是否存在
        if (FileManager.default.fileExists(atPath: url.path) == false) {
            return true
        }
        
        /// PDF 格式文件
        if (url.pathExtension.lowercased() == "pdf") {
            let document = PDFDocument(url: url)
            if (document == nil) {
                return true
            }
            
            if (document!.isLocked) { // 加锁文件不在这里判断
                return false
            }
            
            if (document!.pageCount <= 0) {
                return true
            }
            return false
        }
        
        // 支持的图片格式
        let imageExts = ["jpg","cur","bmp","jpeg","gif","png","tiff","tif","ico","icns","tga","psd","eps","hdr","jp2","jpc","pict","sgi","heic"]
        let isImage = imageExts.contains(url.pathExtension.lowercased())
        if (isImage == false) { // 其他格式目前返回没损坏,后续再补充(如果有需求)
            return false
        }
        
        // 图片格式
        let image = NSImage(contentsOf: url)
        let data = image?.tiffRepresentation
        if (data == nil) {
            return true
        }
        let imageRep = NSBitmapImageRep(data: data!)
        imageRep!.size = image!.size
        var imageData: NSData?
        if (url.pathExtension.lowercased() == "png") {
            imageData = imageRep?.representation(using: .png, properties: [:]) as NSData?
        } else {
            imageData = imageRep?.representation(using: .jpeg, properties: [:]) as NSData?
        }
        if (imageData == nil) {
            return true
        }
        
        let path = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!)
        if (FileManager.default.fileExists(atPath: path!) == false) {
            try?FileManager.default.createDirectory(atPath: path!, withIntermediateDirectories: false)
        }
        
        var tagString: String = ""
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyMMddHHmmss"
        tagString.append(dateFormatter.string(from: Date()))
        tagString = tagString.appendingFormat("%04d", arc4random()%10000)
        let filePath = path?.appending("/\(tagString).png")
        if (imageData!.write(toFile: filePath!, atomically: true) == false) {
            return true
        }
        
        // 删除临时图片
        try?FileManager.default.removeItem(atPath: filePath!)
        
        return false
    }
    
    @objc class func isDamage(url: URL, needAlertIfDamage need: Bool) -> Bool {
        let result = self.isDamage(url: url)
        if (result == false) {
            return false
        }
        
        if (need == false) {
            return true
        }
        
        let alert = NSAlert()
        alert.messageText = NSLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: "")
        alert.runModal()
        return true
    }
}

// MARK: -
// MARK: 保存密码
extension NSDocument {
    func savePasswordInKeychain(_ password: String, _ document: CPDFDocument) {
        if (document.isLocked || password.isEmpty) {
            return
        }
        
        let fileId = self.fileId(for: document)
        if (fileId.isEmpty) {
            return
        }
        
//        let status: SKPasswordStatus =
        let label = "PDF Reader Pro: \(self.displayName!)"
        SKKeychain.setPassword(password, item: nil, forService: self.passwordServiceName(), account: fileId, label: label, comment: self.fileURL?.path)
    }
    
    func getPassword(_ password: AutoreleasingUnsafeMutablePointer<NSString?>, fileId: String) {
        let status = SKKeychain.getPassword(password, item: nil, forService: self.passwordServiceName(), account: fileId)
//        if (status == .found) {
            
//        }
    }
    
    fileprivate func fileId(for document: CPDFDocument) -> String {
        return "\(document.documentURL.path.hash)"
    }
    
    private func passwordServiceName() -> String {
        return "PDF Reader Pro password"
    }
}

extension KMMainDocument: SKPDFSynchronizerDelegate {
    func synchronizer(_ synchronizer: SKPDFSynchronizer!, foundLine line: Int, inFile file: String!) {
        if FileManager.default.fileExists(atPath: file) {
            let defaults = UserDefaults.standard
            
            var editorPreset = defaults.string(forKey: SKTeXEditorPresetKey) ?? ""
            var editorCmd: String?
            var editorArgs: String?
            var cmdString: String?
                
            if !KMSyncPreferences.getTeXEditorCommand(command: &editorCmd, arguments: &editorArgs, forPreset: editorPreset) {
                editorCmd = defaults.string(forKey: SKTeXEditorCommandKey)
                editorArgs = defaults.string(forKey: SKTeXEditorArgumentsKey)
            }
                
            if var cmdString = editorArgs {
                if !editorCmd!.hasPrefix("/") {
                    var searchPaths = ["/usr/bin", "/usr/local/bin"]
                    var toolPath: String?
                    
                    let fm = FileManager.default
                    
                    if !(editorPreset.isEmpty) {
                        if let path = NSWorkspace.shared.fullPath(forApplication: editorPreset) {
                            if let appBundle = Bundle(path: path) {
                                if let contentsPath = appBundle.path(forResource: "Contents", ofType: nil) {
                                    searchPaths.insert(contentsPath, at: 0)
                                }
                                if editorPreset != "BBEdit", let execPath = appBundle.executablePath {
                                    searchPaths.insert(execPath, at: 0)
                                }
                                if let resourcePath = appBundle.resourcePath {
                                    searchPaths.insert(resourcePath, at: 0)
                                }
                                if let sharedSupportPath = appBundle.sharedSupportPath {
                                    searchPaths.insert(sharedSupportPath, at: 0)
                                }
                            }
                        }
                    } else {
                        let appSupportDirs = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
                        let appSupportPaths = appSupportDirs.map { $0.path }
                        searchPaths.append(contentsOf: appSupportPaths)
                    }
                    
                    for path in searchPaths {
                        toolPath = (path as NSString).appendingPathComponent(editorCmd!)
                        if fm.isExecutableFile(atPath: toolPath!) {
                            editorCmd = toolPath
                            break
                        }
                        toolPath = ((path as NSString).appendingPathComponent("bin") as NSString).appendingPathComponent(editorCmd!)
                        if fm.isExecutableFile(atPath: toolPath!) {
                            editorCmd = toolPath
                            break
                        }
                    }
                }
                
                cmdString = cmdString.replacingOccurrences(of: "%line", with: "\(line + 1)")
                cmdString = cmdString.replacingOccurrences(of: "%file", with: file)
                cmdString = cmdString.replacingOccurrences(of: "%output", with: fileURL?.path ?? "")
                
                cmdString.insert(contentsOf: "\" ", at: cmdString.startIndex)
                cmdString.insert(contentsOf: editorCmd!, at: cmdString.startIndex)
                cmdString.insert("\"", at: cmdString.startIndex)
                
                let ws = NSWorkspace.shared
                if let theUTI = try? ws.type(ofFile: editorCmd!) {
                    if ws.type(theUTI, conformsToType: "com.apple.applescript.script") || ws.type(theUTI, conformsToType: "com.apple.applescript.text") {
                        cmdString.insert(contentsOf: "/usr/bin/osascript ", at: cmdString.startIndex)
                    }
                }
                
                let task = Process()
                task.launchPath = "/bin/sh"
                task.currentDirectoryPath = (file as NSString).deletingLastPathComponent
                task.arguments = ["-c", cmdString]
                task.standardOutput = FileHandle.nullDevice
                task.standardError = FileHandle.nullDevice
                do {
                    try task.run()
                } catch let error {
                    Swift.print("command failed: \(cmdString ?? ""): \(error)")
                }
            }
        }
    }
    
    func synchronizer(_ synchronizer: SKPDFSynchronizer!, foundLocation point: NSPoint, atPageIndex pageIndex: UInt, options: Int) {
        guard let pdfDoc = self.mainViewController?.document else { return }
        if pageIndex < pdfDoc.pageCount {
            if let page = pdfDoc.page(at: pageIndex) {
                var adjustedPoint = point
                if options & SKPDFSynchronizerFlippedMask != 0 {
                    let mediaBox = page.bounds(for: .mediaBox)
                    adjustedPoint.y = NSMaxY(mediaBox) - adjustedPoint.y
                }
                
                self.mainViewController?.listView.displayLine(at: adjustedPoint, inPageAtIndex: Int(pageIndex), showReadingBar: options & SKPDFSynchronizerShowReadingBarMask != 0)
            }
        }
    }
}

extension KMMainDocument {
    
    
}