//
//  KMOutlineViewController.swift
//  PDF Reader Pro
//
//  Created by lxy on 2022/10/10.
//

import Cocoa

class KMOutlineViewController: NSViewController {
  
    @IBOutlet var contendView: NSView!
    @IBOutlet weak var topView: NSView!
    @IBOutlet weak var titleLabel: NSTextField!
    
    @IBOutlet weak var lineView: NSView!
    
    @IBOutlet weak var addButton: NSButton!
    @IBOutlet weak var moreButton: NSButton!
    @IBOutlet var topSepline: NSView!
    
    @IBOutlet weak var emptyView: NSView!
    @IBOutlet weak var bigTipLabel: NSTextField!
    @IBOutlet weak var tipLabel: NSTextField!
    
    @IBOutlet weak var BOTAOutlineView: KMBOTAOutlineView!
    
    var dragPDFOutline : KMBOTAOutlineItem!
    var renameTextField : NSTextField!
    var listView : CPDFListView!
    var renamePDFOutline : KMBOTAOutlineItem!
    let moreMenu = NSMenu()
    var isLocalEvent = false
    
    func dealloc() {
        
        NotificationCenter.default.removeObserver(self)
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
        self.BOTAOutlineView.delegate = nil
    }
    
    override func viewWillDisappear() {
        super.viewWillDisappear()
        self.cancelSelect()
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.emptyView.wantsLayer = true
//        self.emptyView.layer?.backgroundColor = NSColor.red.cgColor
        
        self.addOutlineMenu()
        self.titleLabel.stringValue = NSLocalizedString("Outline", comment: "")
        self.topView.wantsLayer = true

        self.BOTAOutlineView.delegate = self
        self.BOTAOutlineView.inputData = self.listView.document.outlineRoot()
        self.BOTAOutlineView.outlineView.doubleAction = #selector(outlineViewDoubleAction)
        
        self.refreshUI()
        
        self.initNotification()
    }
    
    func refreshUI() {
        self.contendView.wantsLayer = true
        self.contendView.layer?.backgroundColor = NSColor.km_init(hex: "#F7F8FA").cgColor
        
        self.topSepline.wantsLayer = true
        self.topSepline.layer?.backgroundColor = NSColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor
        
        self.titleLabel.textColor = NSColor.km_init(hex: "#252629")
        self.titleLabel.font = NSFont.SFProTextSemiboldFont(14.0)
        
        self.bigTipLabel.font = NSFont.SFProTextRegularFont(14.0)
        self.bigTipLabel.textColor = NSColor.km_init(hex: "#616469")
        self.bigTipLabel.stringValue = NSLocalizedString("No Outlines", comment: "")
        
        self.lineView.backgroundColor(NSColor.km_init(hex: "#EDEEF0"))
        
        
        let title = NSLocalizedString("To create an outline, please right-click on the selected page and choose \"Add Outline\", or click \"Add\" in the upper right corner.", comment: "")
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineHeightMultiple = 1.32
        paragraphStyle.alignment = .center
        self.tipLabel.attributedStringValue = NSMutableAttributedString(string: title, attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle, .foregroundColor : NSColor.km_init(hex: "#94989C")])
        
        
        self.addButton.toolTip = NSLocalizedString("Add Outline", comment: "")
        self.moreButton.toolTip = NSLocalizedString("More", comment: "")
        
    }
    
    private func addOutlineMenu(){
        moreMenu.addItem(withTitle:  NSLocalizedString("Expand All", comment: ""), action: #selector(expandAllComments), target: self, tag: 0)
        moreMenu.addItem(withTitle:  NSLocalizedString("Collapse All", comment: ""), action: #selector(collapseAllComments), target: self, tag: 1)
        moreMenu.addItem(withTitle:  NSLocalizedString("Remove All Outlines", comment: ""), action: #selector(removeAllOutlineItem), target: self, tag: 2)
    }
    
    func addRightMenu(view: NSView, event: NSEvent) {
        let menus : NSMenu = NSMenu(title: "")
        let addItem = self.menuItemWithTitle(title: NSLocalizedString("Add Item", comment: ""), action: #selector(addItemAction))
        let addChildItem = self.menuItemWithTitle(title: NSLocalizedString("Add Sub-Item", comment: ""), action: #selector(addChildItemAction))
        let addHigherItem = self.menuItemWithTitle(title: NSLocalizedString("Add A Higher Level", comment: ""), action: #selector(addHigherItemAction))
        let deleteItem = self.menuItemWithTitle(title: NSLocalizedString("Delete", comment: ""), action: #selector(deleteItemAction))
//        let editItem = self.menuItemWithTitle(title: NSLocalizedString("Edit", comment: ""), action: #selector(editItemAction))
        let renameItem = self.menuItemWithTitle(title: NSLocalizedString("Rename", comment: ""), action: #selector(renameItemAction))
        let changeItem = self.menuItemWithTitle(title: NSLocalizedString("Change Destination", comment: ""), action: #selector(changeItemAction))
        let promoteItem = self.menuItemWithTitle(title: NSLocalizedString("Promote", comment: ""), action: #selector(promoteItemAction))
        let demoteItem = self.menuItemWithTitle(title: NSLocalizedString("Demote", comment: ""), action: #selector(demoteItemAction))
        menus.addItem(addItem)
        menus.addItem(addChildItem)
        menus.addItem(addHigherItem)
        menus.addItem(NSMenuItem.separator())
        menus.addItem(deleteItem)
        menus.addItem(NSMenuItem.separator())
//        menus.addItem(editItem)
        menus.addItem(renameItem)
        menus.addItem(changeItem)
        menus.addItem(NSMenuItem.separator())
        menus.addItem(promoteItem)
        menus.addItem(demoteItem)
        
        let point = view.convert(event.locationInWindow, from: nil)
        menus.popUp(positioning: nil, at: point, in: view)
    }
    
    func menuItemWithTitle(title:String, action:Selector?) -> NSMenuItem {
        let menuItem = NSMenuItem.init(title: title as String, action: action, keyEquivalent: "")
        menuItem.target = self
        return menuItem
    }
    
    func initNotification() {
        NotificationCenter.default.addObserver(self, selector: #selector(KMPDFViewCurrentPageDidChangedNotification), name: NSNotification.Name.init(rawValue: "KMPDFViewCurrentPageDidChanged"), object: nil)
    }
    
    func removeNotification() {
        NotificationCenter.default.removeObserver(self)
    }
    
    func reloadData() {
        self.BOTAOutlineView.reloadData(expandItemType: .none)
    }
    
    func editOutlineUI(editVC : KMOutlineEditViewController!) {
        if editVC.pageButton.state == NSControl.StateValue.on {
            let index = Int(editVC.outlineTargetPageIndexTextField.stringValue)!
            if editVC.originalDestination?.pageIndex != index {
                let page = editVC.pdfView?.document.page(at: UInt(index))
                if page != nil {
                    let destination = CPDFDestination.init(document: editVC.pdfView!.document, pageIndex: index)
                    editVC.outline?.destination = destination
                } else {
                    __NSBeep()
                }
            }
        } else if editVC.urlButton.state == NSControl.StateValue.on {
            if editVC.originalURLString != editVC.outlineURLTextField.stringValue {
                var urlString = editVC.outlineURLTextField.stringValue
                let tLowerUrl = urlString.lowercased()
                if !tLowerUrl.hasPrefix("https://") && !tLowerUrl.hasPrefix("pf]://")  && !urlString.hasPrefix("https://") &&
                    urlString.lengthOfBytes(using: String.Encoding(rawValue: String.Encoding.utf16.rawValue)) > 0 {
                    urlString = "http://\(urlString)"
                }
                let action = CPDFURLAction.init(url: urlString)
                editVC.outline?.action = action
            }
        } else if editVC.mailButton.state == NSControl.StateValue.on {
            var mailString = editVC.mailAddressTextField.stringValue
            let tLowerStr = mailString.lowercased()
            if !tLowerStr.hasPrefix("mailto:") {
                mailString = "mailto:\(mailString)"
            }
            if mailString != editVC.originalURLString {
                var action = CPDFURLAction.init(url: mailString)
                if action?.url == nil {
                    action = CPDFURLAction.init(url: "mailto:")
                }
                editVC.outline?.action = action
            }
        }
//        self.mainWindowController.document?.undo()
//        if editVC.outlineNameTextView.string != editVC.originalLabel {
//            self.renamePDFOutline(outline: editVC.outline, label: editVC.outlineNameTextView.string)
//        }
    }
}


//Data
extension KMOutlineViewController {
    func updateExtempViewState() {
        if(self.listView.document.outlineRoot() == nil || self.listView.document.outlineRoot().numberOfChildren == 0) { //无数据时的图
            self.emptyView.isHidden = false
        } else {
            self.emptyView.isHidden = true
        }
    }
}

//MARK: -  Notification
extension KMOutlineViewController {
    @objc func KMPDFViewCurrentPageDidChangedNotification(notification: NSNotification) {
        if notification.object is CPDFDocument {
            let pdfdocument : CPDFDocument = notification.object as! CPDFDocument
            if pdfdocument.isEqual(self.listView.document) {
                if !isLocalEvent {
                    self.updateOutlineSelection()
                }
                self.isLocalEvent = false
            }
        }
    }
}

//MARK: -  Menu 右键菜单
extension KMOutlineViewController {
    @objc func outlineViewDoubleAction() {
        if(self.BOTAOutlineView.outlineView.clickedRow >= 0) {
            self.renameItemAction()
        }
    }
    
    @objc func addItemAction() {
        let outlineView: KMOutlineView = self.BOTAOutlineView.outlineView
        let selectRowIndexs = outlineView.selectedRowIndexes
        let dataCount = self.BOTAOutlineView.data?.children.count
        var index: Int
        var parent: KMBOTAOutlineItem = KMBOTAOutlineItem()
        var outlineItem: KMBOTAOutlineItem = KMBOTAOutlineItem()

        if selectRowIndexs.count == 0 {
            var lastOulineItem = KMBOTAOutlineItem()
            if dataCount == nil || dataCount == 0 {
                let item = KMBOTAOutlineItem()
                item.outline = self.listView.document.setNewOutlineRoot()
                item.parent = nil
                parent = item
                
                lastOulineItem = item
            } else {
                outlineItem = outlineView.item(atRow: outlineView.numberOfRows - 1) as! KMBOTAOutlineItem
                lastOulineItem = outlineItem
                while lastOulineItem.parent != nil {
                    lastOulineItem = lastOulineItem.parent!
                }
                parent = lastOulineItem
            }
            index = Int(lastOulineItem.outline.numberOfChildren)
        } else {
            outlineItem = outlineView.item(atRow: selectRowIndexs.last ?? 0) as! KMBOTAOutlineItem
            parent = outlineItem.parent ?? KMBOTAOutlineItem()
            index = Int(outlineItem.outline.index + 1)
        }
        
        self.addOutlineToIndex(index: index, parent: parent)
    }
    
    @objc func addChildItemAction() {
        let outlineView: KMOutlineView = self.BOTAOutlineView.outlineView
        let selectRowIndexs = outlineView.selectedRowIndexes
        if selectRowIndexs.count != 0 {
            let outlineItem: KMBOTAOutlineItem = outlineView.item(atRow: selectRowIndexs.last!) as! KMBOTAOutlineItem
            let index = outlineItem.outline.numberOfChildren
            self.addOutlineToIndex(index: NSInteger(index), parent: outlineItem)
        }
    }
    
    @objc func addHigherItemAction() {
        let outlineView: KMOutlineView = self.BOTAOutlineView.outlineView
        let selectRowIndexs = outlineView.selectedRowIndexes
        if selectRowIndexs.count != 0 {
            let outlineItem: KMBOTAOutlineItem = outlineView.item(atRow: selectRowIndexs.last!) as! KMBOTAOutlineItem
            var parent = outlineItem.parent
            let index = NSInteger(parent!.outline.index) + 1
            parent = parent?.parent
            if parent != nil {
                self.addOutlineToIndex(index: index, parent: parent!)
            }
        }
    }
    
    @objc func deleteItemAction() {
        let outlineView: KMOutlineView = self.BOTAOutlineView.outlineView
        let selectRowIndexs = outlineView.selectedRowIndexes
        if selectRowIndexs.count != 0 {
            var outlineItems: [KMBOTAOutlineItem] = []
            for index in selectRowIndexs {
                let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: index) as! KMBOTAOutlineItem
                outlineItem.toIndex = index
                outlineItem.parent = outlineItem.parent ?? KMBOTAOutlineItem()
                outlineItems.append(outlineItem)
            }
            self.deleteOutline(outlineItems: outlineItems)
        }
    }
    
    @objc func editItemAction() {
        if self.BOTAOutlineView.outlineView.clickedRow >= 0 {
            if self.BOTAOutlineView.outlineView.rowView(atRow: self.BOTAOutlineView.outlineView.clickedRow, makeIfNecessary: true) != nil {
                let cell = self.BOTAOutlineView.outlineView.rowView(atRow: self.BOTAOutlineView.outlineView.clickedRow, makeIfNecessary: true)
                let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem
                let outlineEditViewController = KMOutlineEditViewController.init(outline: outlineItem.outline, document: self.listView)
                let popover = NSPopover()
                popover.delegate = self
                popover.contentViewController = outlineEditViewController
                popover.animates = true
                popover.behavior = .transient
                popover.show(relativeTo: cell!.bounds, of: cell!, preferredEdge: .minX)
            }
        } else {
            __NSBeep()
        }
    }
    
    @objc func renameItemAction() {
        if self.BOTAOutlineView.outlineView.clickedRow >= 0 {
            self.renameOutlineWithRow(row: self.BOTAOutlineView.outlineView.clickedRow)
        } else {
            __NSBeep()
        }
    }
    
    @objc func changeItemAction() {
        if self.BOTAOutlineView.outlineView.clickedRow >= 0 {
            let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem
            let alter = NSAlert()
            alter.alertStyle = NSAlert.Style.informational
            alter.messageText = NSLocalizedString("Are you sure you want to set the target location of the selected outline to the current page?", comment: "")
            alter.addButton(withTitle: NSLocalizedString("Yes", comment:""))
            alter.addButton(withTitle: NSLocalizedString("No", comment:""))
            let modlres = alter.runModal()
            if modlres == NSApplication.ModalResponse.alertFirstButtonReturn {
                self.changeLocation(outlineItem: outlineItem, destination: self.fetchCurrentDestination())
            }
        } else {
            __NSBeep()
        }
    }
    
    @objc func promoteItemAction() {
        if self.BOTAOutlineView.outlineView.clickedRow >= 0 {
            let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem
            var parent = outlineItem.parent
            let index = NSInteger(parent!.outline.index) + 1
            parent = parent?.parent
            if parent != nil {
                self.moveOutline(outlineItem: outlineItem, index: index, parent: parent)
            }
        }
    }
    
    @objc func demoteItemAction() {
        if self.BOTAOutlineView.outlineView.clickedRow >= 0 {
            let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem
            let parent = outlineItem.parent
            let newParent = parent?.children[Int(outlineItem.outline.index) - 1]
            let index = newParent?.children.count
            if (index != nil) {
                self.moveOutline(outlineItem: outlineItem, index: NSInteger(index ?? 0), parent: newParent)
            }
        }
    }
    
    @objc private func expandAllComments(item: NSMenuItem) {
        self.BOTAOutlineView.expandAllComments(item: item)
    }
    
    @objc private func collapseAllComments(item: NSMenuItem) {
        
        self.BOTAOutlineView.collapseAllComments(item: item)
    }
    
    @objc private func removeAllOutlineItem(item: NSMenuItem) {
        let alter = NSAlert()
        alter.alertStyle = NSAlert.Style.informational
        alter.messageText = NSLocalizedString("This will permanently remove all outlines. Are you sure to continue?", comment: "")
        alter.addButton(withTitle: NSLocalizedString("Yes", comment:""))
        alter.addButton(withTitle: NSLocalizedString("No", comment:""))
        let modlres = alter.runModal()
        if modlres == NSApplication.ModalResponse.alertFirstButtonReturn {
            self.removeAllOutline()
        }
    }
    
    
    @objc private func removeAllOutline() {
        guard let data = self.BOTAOutlineView.data else { return }
        
        for item in data.children {
            item.toIndex = Int(item.outline.index)
        }
        self.deleteOutline(outlineItems: data.children)
        self.BOTAOutlineView.reloadData(expandItemType: .none)
    }
}

//MARK: -  Action
extension KMOutlineViewController {
    @IBAction func addNewOutline(_ sender: Any) {
        self.addItemAction()
    }
    
    @IBAction func moreButton_click(_ sender: NSButton) {
        let rect = sender.convert(sender.bounds, to: self.view)
        moreMenu.popUp(positioning: nil, at: NSPoint(x: rect.origin.x, y: rect.origin.y-10), in: self.view)
    }
    
    
    @IBAction func escButtonAction(_ sender: Any) {
        self.cancelSelect()
    }
    
    func cancelSelect() {
        self.BOTAOutlineView.cancelSelect()
    }
    
    func renameOutlineWithRow(row: NSInteger) {
        DispatchQueue.main.async {
            self.renamePDFOutline = self.BOTAOutlineView.outlineView.item(atRow: row) as? KMBOTAOutlineItem
            let cell : KMBOTAOutlineCellView = self.BOTAOutlineView.outlineView.view(atColumn: 0, row: row, makeIfNecessary: true) as! KMBOTAOutlineCellView
            self.renameTextField = cell.titleLabel
            self.renameTextField.delegate = self
            self.renameTextField.isEditable = true
            self.renameTextField.becomeFirstResponder()
        }
    }
    
    func addOutlineToIndex(index: Int, parent: KMBOTAOutlineItem) {
        let pageIndex: Int = self.listView.currentPageIndex
        let label: String = self.fetchCurrentLabel(pageIndex: pageIndex)
        let destination: CPDFDestination = self.fetchCurrentDestination()
    
        self.addOutlineToIndex(index: index, pageIndex: pageIndex, destination: destination, lable: label, parent: parent)
    }
    
    func addOutlineToIndex(index: Int, pageIndex: Int, destination: CPDFDestination, lable: String, parent: KMBOTAOutlineItem) {
        let outlineItem = KMBOTAOutlineItem()
        outlineItem.destination = destination
        outlineItem.label = lable
        outlineItem.parent = parent
        outlineItem.toIndex = index
        self.addOutline(outlineItems: [outlineItem])
        
        let tempOutlineView = self.BOTAOutlineView!
        var index = -1
        if tempOutlineView.outlineView.numberOfRows == 1 || tempOutlineView.data == nil {
            index = 0
        } else {
            index = tempOutlineView.outlineView.row(forItem: outlineItem)
        }

        tempOutlineView.selectIndex(index: index)

        //滑动到指定位置
        if(tempOutlineView.outlineView.selectedRow >= 0) {
            self.renameOutlineWithRow(row: tempOutlineView.outlineView.selectedRow)
        }
        
        let row = tempOutlineView.outlineView.row(forItem: outlineItem)
        if Thread.current.isMainThread {
            tempOutlineView.outlineView.scrollToVisible(tempOutlineView.outlineView.rect(ofRow: row))
        } else {
            DispatchQueue.main.async {
                tempOutlineView.outlineView.scrollToVisible(tempOutlineView.outlineView.rect(ofRow: row))
            }
        }
    }
    
    func updateOutlineSelection() {
        KMPrint("updateOutlineSelection")
        let currentPageIndex = self.listView.currentPageIndex
        let numRows = self.BOTAOutlineView.outlineView.numberOfRows
        if numRows > 0 {
            for i in 0...numRows - 1 {
                let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: i) as! KMBOTAOutlineItem
                if (outlineItem.outline.destination == nil) {
                    continue
                }
                
                if outlineItem.outline.destination.pageIndex == currentPageIndex {
                    self.BOTAOutlineView.selectIndex(index: currentPageIndex)
                    break
                }
            }
        }
    }
    
    func fetchCurrentDestination() -> CPDFDestination {
        //destination
        var destination: CPDFDestination
        let pageIndex: Int
        if self.listView.currentSelection != nil {
            let des :CPDFDestination = CPDFDestination.init(document: self.listView.document, pageIndex: Int(self.listView.currentSelection.page.pageIndex()), at: CGPoint(x: self.listView.currentSelection.bounds.origin.x, y: self.listView.currentSelection.bounds.origin.y + self.listView.currentSelection.bounds.size.height), zoom: self.listView.scaleFactor)
            destination = des
            pageIndex = Int(self.listView.currentSelection.page.pageIndex())
        } else {
            let des :CPDFDestination = CPDFDestination.init(document: self.listView.document, pageIndex: Int(self.listView.currentPageIndex), at: CGPoint(x: 0, y: self.listView.currentPage().size.height), zoom: self.listView.scaleFactor)
            destination = des
            pageIndex = Int(self.listView.currentPageIndex)
        }
        
        if "\(destination.point.x )" == "nan" {
            destination = CPDFDestination(document: self.listView.document, pageIndex: pageIndex, at: CGPoint(x: 0, y: 0), zoom: self.listView.scaleFactor)
        }
        return destination
    }
    
    func fetchCurrentLabel(pageIndex: Int) -> String{
        //label
        var label = "\(NSLocalizedString("Page", comment: ""))\(pageIndex + 1)"
        if self.listView.currentSelection != nil && self.listView.currentSelection.selectionsByLine != nil {
            for currentSelection in  self.listView.currentSelection.selectionsByLine {
                label = currentSelection.string()
            }
        }
        return label
    }
}

//MARK: - KMBOTAOutlineViewDelegate
extension KMOutlineViewController: KMBOTAOutlineViewDelegate {
    func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, rightDidMoseDown: KMBOTAOutlineItem, event: NSEvent) {
        let row = outlineView.outlineView.row(forItem: rightDidMoseDown)
        if outlineView.outlineView.rowView(atRow: row, makeIfNecessary: false) != nil {
            let rowView = outlineView.outlineView.rowView(atRow: row, makeIfNecessary: false)
            self.addRightMenu(view: rowView!, event: event)
        }
    }

    func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, didReloadData: KMBOTAOutlineItem) {
        self.updateExtempViewState()
    }
    
    func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, didSelectItem: [KMBOTAOutlineItem]) {
        if self.BOTAOutlineView.outlineView.selectedRowIndexes.count == 1 {
            self.isLocalEvent = true
            
            let outlineItem: KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow:self.BOTAOutlineView.outlineView.selectedRow) as! KMBOTAOutlineItem
            
            if outlineItem.outline.destination != nil {
                if outlineItem.outline.destination.page() != nil {
                    self.listView.go(toTargetPoint: outlineItem.outline.destination.point, on: outlineItem.outline.destination.page() , at: .top)
                } else {
                    let alter = NSAlert()
                    alter.alertStyle = NSAlert.Style.informational
                    alter.messageText = NSLocalizedString("The target page is invalid, please relocate it.", comment: "")
                    alter.addButton(withTitle: NSLocalizedString("OK", comment:""))
                    alter.beginSheetModal(for: self.view.window ?? NSWindow())
                }
            } else if outlineItem.outline.action != nil {
                let alter = NSAlert()
                alter.alertStyle = NSAlert.Style.informational
                alter.messageText = NSLocalizedString("The target page is invalid, please relocate it.", comment: "")
                alter.addButton(withTitle: NSLocalizedString("OK", comment:""))
                alter.beginSheetModal(for: self.view.window ?? NSWindow())
//                self.listView.perform(outlineItem.outline.action)
            }
        }
    }
    
    func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, writeItems items: [Any], to pasteboard: NSPasteboard) -> Bool {
        if outlineView.outlineView.selectedRowIndexes.count > 1 || outlineView.outlineView.selectedRow == -1 {
            return false
        }
        
        self.dragPDFOutline = items.first as? KMBOTAOutlineItem
        
        let indexSet = [outlineView.outlineView.clickedRow]
        let indexSetData: Data = try!NSKeyedArchiver.archivedData(withRootObject: indexSet, requiringSecureCoding: true)
        pasteboard.declareTypes([NSPasteboard.PasteboardType(rawValue: "kKMPDFViewOutlineDragDataType")], owner: self)
        pasteboard.setData(indexSetData, forType: NSPasteboard.PasteboardType(rawValue: NSPasteboard.PasteboardType.RawValue("kKMPDFViewOutlineDragDataType")))
        return true
    }
    
    func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
        var dragOperation = NSDragOperation.init(rawValue: 0)
        if index >= 0 {
            dragOperation = NSDragOperation.move
        }
        return dragOperation
    }
    
    func BOTAOutlineView(_ outlineView: KMBOTAOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
        guard let dragOutlineItem = self.dragPDFOutline else { return false }
        let outlineItem: KMBOTAOutlineItem = (item ?? KMBOTAOutlineItem()) as! KMBOTAOutlineItem
        if index < 0 {
            return false
        }
        
        if outlineItem.parent == nil {
            var root = dragOutlineItem.parent
            while root?.parent?.children != nil {
                root = root?.parent!
            }
            if dragOutlineItem.parent!.isEqual(root) {
                if dragOutlineItem.outline.index > index {
                    self.moveOutline(outlineItem: dragOutlineItem, index: index, parent: root)
                } else {
                    self.moveOutline(outlineItem: dragOutlineItem, index: index - 1, parent: root)
                }
            } else {
                self.moveOutline(outlineItem: dragOutlineItem, index: index, parent: root)
            }
        } else {
            if dragOutlineItem.parent!.isEqual(item) {
//                if dragOutlineItem.outline.index != 0 {
                    if dragOutlineItem.outline.index > index {
                        self.moveOutline(outlineItem: dragOutlineItem, index: index, parent: item as? KMBOTAOutlineItem)
                    } else {
                        self.moveOutline(outlineItem: dragOutlineItem, index: index - 1, parent: item as? KMBOTAOutlineItem)
                    }
//                } else {
//                    return false
//                }
            } else {
                var tOutline = outlineItem
                var isContains = false
                while (tOutline.parent != nil) {
                    if tOutline.outline.isEqual(dragOutlineItem.outline) {
                        isContains = true
                        break
                    }
                    tOutline = tOutline.parent!
                }
                if isContains == false {
                    self.moveOutline(outlineItem: dragOutlineItem, index: index, parent: item as? KMBOTAOutlineItem)
                }
            }
        }
        self.BOTAOutlineView.selectItem(outlineItem: dragOutlineItem)
        
        return true
    }
}

//MARK: - NSTextFieldDelegate
extension KMOutlineViewController: NSTextFieldDelegate {
    func controlTextDidEndEditing(_ obj: Notification) {
        if (self.renameTextField.isEqual(obj.object)) {
            let textField : NSTextField = obj.object as! NSTextField
            self.renamePDFOutline(outlineItem: self.renamePDFOutline, label: textField.stringValue)
        }
    }
}

//MARK: - NSPopoverDelegate
extension KMOutlineViewController: NSPopoverDelegate {
    func popoverWillClose(_ notification: Notification) {
        let popover : NSPopover = notification.object as! NSPopover
        if popover.contentViewController!.isKind(of: KMOutlineEditViewController.self) {
            
        }
    }
}

//MARK: - NSMenuItemValidation
extension KMOutlineViewController: NSMenuDelegate, NSMenuItemValidation {
    func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
        let action = menuItem.action
        if action == #selector(addItemAction) ||
            action == #selector(addChildItemAction) ||
            action == #selector(addHigherItemAction) ||
            action == #selector(deleteItemAction) ||
            action == #selector(editItemAction) ||
            action == #selector(changeItemAction) ||
            action == #selector(renameItemAction) ||
            action == #selector(promoteItemAction) ||
            action == #selector(demoteItemAction) {
            if self.BOTAOutlineView.outlineView.selectedRowIndexes.count > 1 {
                if action == #selector(deleteItemAction) {
                    return true
                }
                return false
            } else if self.BOTAOutlineView.outlineView.selectedRowIndexes.count > 0 {
                if action == #selector(addChildItemAction) || action == #selector(changeItemAction) {
                    return true
                }
            }
            if self.BOTAOutlineView.outlineView.clickedRow == -1 {
                if action == #selector(addItemAction) {
                    return true
                } else {
                    return false
                }
            } else {
                let outlineItem : KMBOTAOutlineItem = self.BOTAOutlineView.outlineView.item(atRow: self.BOTAOutlineView.outlineView.clickedRow) as! KMBOTAOutlineItem
                if outlineItem.outline.index > 0 {
                    if action == #selector(demoteItemAction) {
                        return true
                    }
                } else {
                    if action == #selector(demoteItemAction) {
                        return false
                    }
                }
                
                let parentOutline = outlineItem.outline.parent
                let grandparentOutline = parentOutline?.parent
                if grandparentOutline != nil {
                    if action == #selector(addHigherItemAction) {
                        return true
                    } else if action == #selector(promoteItemAction) {
                        return true
                    }
                } else {
                    if action == #selector(addHigherItemAction) {
                        return false
                    } else if action == #selector(promoteItemAction) {
                        return false
                    }
                }
            }
            return true
        }
        
        if (action == #selector(undo)) {
            return self.listView.undoManager?.canUndo ?? false
        }
        if (action == #selector(redo)) {
            return self.listView.undoManager?.canRedo ?? false
        }
        
        if (action == #selector(expandAllComments)) {
            var canExpand = false
            for row in 0..<self.BOTAOutlineView.outlineView.numberOfRows {
                // 检查当前项目是否可以展开
                let item = self.BOTAOutlineView.outlineView.item(atRow: row)
                if self.BOTAOutlineView.outlineView.isExpandable(item) {
                    if !self.BOTAOutlineView.outlineView.isItemExpanded(item) {
                        canExpand = true
                        break
                    }
                }
            }
            return canExpand
        }
        
        if (action == #selector(collapseAllComments)) {
            var canCollapse = false
            for row in 0..<self.BOTAOutlineView.outlineView.numberOfRows {
                let item = self.BOTAOutlineView.outlineView.item(atRow: row)
                if self.BOTAOutlineView.outlineView.isExpandable(item) {
                    if self.BOTAOutlineView.outlineView.isItemExpanded(item) {
                        canCollapse = true
                        break
                    }
                }
            }
            return canCollapse
        }
        
        if (action == #selector(removeAllOutlineItem)) {
            if self.BOTAOutlineView.outlineView.item(atRow: 0) != nil {
                return true
            } else {
                return false
            }
        }
        return true
    }
}

//MARK: - 快捷键
extension KMOutlineViewController {
    @IBAction func delete(_ sender: Any) {
        self.deleteItemAction()
    }
}

//MARK: - undoRedo
extension KMOutlineViewController {
    func moveOutline(outlineItem: KMBOTAOutlineItem, index: NSInteger, parent: KMBOTAOutlineItem!) {
        let tempOutlineView = self.BOTAOutlineView!
        
        let indexTemp = outlineItem.outline.index
        let parentTemp = outlineItem.parent
        
        //元数据移除
        outlineItem.outline.removeFromParent()
        parent.outline.insertChild(outlineItem.outline, at: UInt(index))
        
        //显示数据刷新
        outlineItem.parent?.children.removeObject(outlineItem)
        parent?.children.insert(outlineItem, at: index)
        outlineItem.parent = parent
        tempOutlineView.outlineView.reloadData()
        tempOutlineView.cancelSelect()
        
        //展开
        outlineItem.isItemExpanded = true
        outlineItem.parent?.isItemExpanded = true
        tempOutlineView.outlineView.expandItem(outlineItem)
        tempOutlineView.outlineView.expandItem(outlineItem.parent)
        
        self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
            self.moveOutline(outlineItem: outlineItem, index: NSInteger(indexTemp), parent: parentTemp)
        }
    }
    
    func changeLocation(outlineItem: KMBOTAOutlineItem, destination: CPDFDestination) {
        let tempOutlineView = self.BOTAOutlineView!
        
        let temp = outlineItem.outline.destination
        outlineItem.outline.destination = CPDFDestination(document: destination.document, pageIndex: destination.pageIndex, at: destination.point, zoom: destination.zoom)
        tempOutlineView.outlineView.reloadItem(outlineItem)
        
        self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
            self.changeLocation(outlineItem: outlineItem, destination: temp!)
        }
    }
    
    func renamePDFOutline(outlineItem: KMBOTAOutlineItem!, label: String) {
        let tempOutlineView = self.BOTAOutlineView!
        
        self.view.window?.makeFirstResponder(tempOutlineView.outlineView)
        self.renameTextField.isEditable = false
        if outlineItem.outline.label == label {
            return
        }
        let temp: String = outlineItem.outline.label
        outlineItem.outline.label = label
        tempOutlineView.outlineView.reloadItem(outlineItem)
        
        self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
            self.renamePDFOutline(outlineItem: outlineItem, label: temp)
        }
    }
    
    func deleteOutline(outlineItems: [KMBOTAOutlineItem]) {
        NSApp.mainWindow?.makeFirstResponder(self.BOTAOutlineView)
        let tempOutlineView = self.BOTAOutlineView!
        
        var tempOutlineItems: [KMBOTAOutlineItem] = outlineItems
        tempOutlineItems.sort(){$0.toIndex > $1.toIndex}
        
        for outlineItem in tempOutlineItems {
            outlineItem.outline.removeFromParent()
            let index = outlineItem.parent?.children.firstIndex(of: outlineItem)
            outlineItem.toIndex = index!
            outlineItem.parent?.children.removeObject(outlineItem)
        }
        
        //展开
        for outlineItem in tempOutlineItems {
            outlineItem.parent?.isItemExpanded = true
            tempOutlineView.outlineView.expandItem(outlineItem.parent)
        }
        
        tempOutlineView.outlineView.reloadData()
        //删除需要取消选中
        tempOutlineView.cancelSelect()
        
        //刷新nil数据
        self.updateExtempViewState()
        self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
            self.addOutline(outlineItems: tempOutlineItems)
        }
    }
    
    func addOutline(outlineItems: [KMBOTAOutlineItem]) {
        NSApp.mainWindow?.makeFirstResponder(self.BOTAOutlineView)
        let tempOutlineView = self.BOTAOutlineView!
        //先取消选中
        tempOutlineView.cancelSelect()
        
        var tempOutlineItems: [KMBOTAOutlineItem] = outlineItems
        tempOutlineItems.sort(){$0.toIndex < $1.toIndex}
        
        for outlineItem in tempOutlineItems {
            if outlineItem.outline.label != nil {
                outlineItem.parent?.outline.insertChild(outlineItem.outline, at: UInt(outlineItem.toIndex))
            } else {
                let outline = outlineItem.parent?.outline.insertChild(at: UInt(outlineItem.toIndex))
                outline?.label = outlineItem.label
                outline?.destination = outlineItem.destination
                outlineItem.outline = outline!
            }
            outlineItem.parent?.children.insert(outlineItem, at: outlineItem.toIndex)
        }
        
        if tempOutlineView.data?.children.count == 0 || tempOutlineView.data == nil {
            tempOutlineView.inputData = self.listView.document.outlineRoot()
        } else {
            tempOutlineView.outlineView.reloadData()
        }
        
        //展开
//        DispatchQueue.main.async {
            for outlineItem in tempOutlineItems {
                var tempParent = outlineItem
                while tempParent.parent != nil {
                    tempParent.isItemExpanded = true
                    tempParent = tempParent.parent!
                    tempOutlineView.outlineView.expandItem(tempParent)
                }
                tempOutlineView.outlineView.expandItem(tempParent.parent)
            }
//        }
        
        self.updateExtempViewState()
        self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
            self.deleteOutline(outlineItems: tempOutlineItems)
        }
    }
    
    @IBAction func undo(_ sender: Any) {
        if (self.listView.undoManager?.canUndo ?? false) {
            self.listView.undoManager?.undo()
        }
    }
    
    @IBAction func redo(_ sender: Any) {
        if (self.listView.undoManager?.canRedo ?? false) {
            self.listView.undoManager?.redo()
        }
    }
}