//
//  KMBookMarkViewController.swift
//  PDF Master
//
//  Created by lxy on 2022/10/10.
//

import Cocoa
import PDFKit

typealias KMBookMarkViewControllerBookMarkDidChange = (_ controller: KMBookMarkViewController, _ bookMarks: [KMBookMarkItem]) -> Void
class KMBookMarkViewController: NSViewController, NSTextFieldDelegate {

    @IBOutlet weak var addBookButton: NSButton!
    @IBOutlet weak var titleTextField: NSTextField!
    
    @IBOutlet var topSeplineView: NSView!
    
    @IBOutlet weak var bookTableView: NSTableView!
    @IBOutlet weak var emptyView: NSView!
    @IBOutlet weak var bigTipLabel: NSTextField!
    @IBOutlet weak var tipLabel: NSTextField!
    
    var dataSource: [KMBookMarkItem]!
    var renameTextField: NSTextField!
    var renamePDFBook: KMBookMarkItem!
    var renameCellView: KMBookCellView!
    var listView: CPDFListView!
    let pdfView = PDFView.init()
    
    var isLocalEvent: Bool = false //区分外部点击还是内部点击
    
    var selectItems: [KMBookMarkItem] = []
    
    var bookMarkDidChange: KMBookMarkViewControllerBookMarkDidChange?
    
    func dealloc() {
        NotificationCenter.default.removeObserver(self)
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.wantsLayer = true
        self.view.layer?.backgroundColor = NSColor(red: 247.0/255.0, green: 248.0/255.0, blue: 250.0/255.0, alpha: 1).cgColor
        
        self.topSeplineView.wantsLayer = true
        self.topSeplineView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.1).cgColor
        
        self.bookTableView.backgroundColor = NSColor.km_init(hex: "#F7F8FA")
        if #available(macOS 11, *) {
            self.bookTableView.style = NSTableView.Style.plain
        }
        self.bookTableView.allowsMultipleSelection = true
        self.bookTableView.doubleAction = #selector(renameBookAction)
//        self.bookTableView.selectionHighlightStyle = NSTableView.SelectionHighlightStyle.none;
        
        
        self.refreshUI()
        self.reloadData()
        
        self.initNotification()
    }
    
    func refreshUI() {
        self.titleTextField.font = NSFont.SFProTextSemiboldFont(14.0)
        self.titleTextField.textColor = NSColor.km_init(hex: "#252629")
        self.titleTextField.stringValue = NSLocalizedString("Bookmarks", comment: "")
        self.addBookButton.toolTip = NSLocalizedString("Add Bookmark", comment: "")
        
        
        self.bigTipLabel.font = NSFont.SFProTextRegularFont(14.0)
        self.bigTipLabel.textColor = NSColor.km_init(hex: "#616469")
        self.bigTipLabel.stringValue = NSLocalizedString("No Bookmarks", comment: "")
        
        let title = NSLocalizedString("To create a bookmark, please right-click on the selected page and choose \"Add Bookmark\", or click \"Add\" button 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")])
    }
    
    func reloadData() {
        let array = self.listView.document.bookmarks() ?? [CPDFBookmark]()
        var bookMarks: [KMBookMarkItem] = []
        for bookMark in array {
            let item = KMBookMarkItem()
            item.bookMark = bookMark
            item.index = UInt(bookMark.pageIndex)
            item.label = bookMark.label
            bookMarks.append(item)
        }
        self.dataSource = bookMarks
        
        self.dataSource.sort(){$0.bookMark.pageIndex < $1.bookMark.pageIndex}
        self.bookTableView.reloadData()
        
        self.updateAddBookMarkState()
    }
    
    func addBookMarkAndEdit(newBookMark: KMBookMarkItem) {

        _ = self.dataSource.contains { KMBookMarkItem in
            if KMBookMarkItem.bookMark == newBookMark.bookMark {
                let index = KMOCToolClass.arrayIndex(of: self.dataSource, annotation: KMBookMarkItem)
                self.didSelectItem(row: index, event: NSEvent())
                self.renameBookWithRow(row: index)
                return true
            }
            return false
        }
    }
    
    func initNotification() {
        NotificationCenter.default.addObserver(self, selector: #selector(KMPDFViewCurrentPageDidChangedNotification), name: NSNotification.Name.init(rawValue: "KMPDFViewCurrentPageDidChanged"), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(documentPageCountChangedNotification), name: NSNotification.Name.init(rawValue: "CPDFDocumentPageCountChangedNotification"), object: nil)
    }
    
    func removeNotification() {
        NotificationCenter.default.removeObserver(self)
    }
    
    private func addMenuTitle(view: NSView, event: NSEvent) {
        let menus : NSMenu = NSMenu(title: "")
        menus.delegate = self
        let addItem = self.menuItemWithTitle(title: NSLocalizedString("Rename", comment: ""), action: #selector(renameBookAction))
        let addChildItem = self.menuItemWithTitle(title: NSLocalizedString("Change Destination", comment: ""), action: #selector(changeLocationAction))
        let addHigherItem = self.menuItemWithTitle(title: NSLocalizedString("Delete", comment: ""), action: #selector(deleteBookAction))

        menus.addItem(addItem)
        menus.addItem(addChildItem)
        menus.addItem(addHigherItem)

        let point = view.convert(event.locationInWindow, from: nil)
        menus.popUp(positioning: nil, at: point, in: view)
//        self.bookTableView.menu = menus
    }

    func menuItemWithTitle(title:String, action:Selector?) -> NSMenuItem {
        let menuItem = NSMenuItem.init(title: title as String, action: action, keyEquivalent: "")
        menuItem.target = self
        return menuItem
    }
    
    //MARK: Menu Action
    
    @objc func renameBookAction() {
        if self.bookTableView.selectedRowIndexes.count == 1 {
            self.renameBookWithRow(row: self.bookTableView.selectedRowIndexes.first!)
        } else {
            __NSBeep()
        }
    }
    
    
    @IBAction func escButtonAction(_ sender: Any) {
        self.bookTableView.deselectAll(nil)
    }
    
    @IBAction func addBookmarkAction(_ sender: Any) {
        if self.listView.document.bookmark(forPageIndex: UInt(self.listView.currentPageIndex)) == nil {
            let label = "\(NSLocalizedString("Page", comment:"")) \(self.listView.currentPageIndex + 1)"
            let bookMark = KMBookMarkItem()
            bookMark.label = label
            bookMark.index = UInt(self.listView.currentPageIndex)
            self.addBookMark(bookMarks: [bookMark])
        } else {
            let bookMark = self.listView.document.bookmark(forPageIndex: UInt(self.listView.currentPageIndex))
            self.dataSource.contains { KMBookMarkItem in
                if KMBookMarkItem.bookMark == bookMark {
                    let index = Int(KMBookMarkItem.index)
                    self.bookTableView.selectRowIndexes(IndexSet(integer: index), byExtendingSelection: false)
                    self.didSelectItem(row: index, event: NSEvent())
                    return true
                }
                return false
            }
        }
    }
    
    @objc func changeLocationAction() {
        if self.bookTableView.selectedRowIndexes.count == 1 {
            let item = self.dataSource[self.bookTableView.selectedRowIndexes.first!]
            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 location", comment: "")
            alter.addButton(withTitle: NSLocalizedString("YES", comment:""))
            alter.addButton(withTitle: NSLocalizedString("NO", comment:""))
            let modlres = alter.runModal()
            if modlres == NSApplication.ModalResponse.alertFirstButtonReturn {
                let bookMark = KMBookMarkItem()
                bookMark.bookMark = item.bookMark
                bookMark.label = item.label
                bookMark.index = UInt(self.listView.currentPageIndex)
                self.changeLocation(oldBookMark: item,
                                    newBookMark: bookMark)
            }
        } else {
            __NSBeep()
        }
    }
    
    @objc func deleteBookAction() {
        if self.bookTableView.selectedRowIndexes.count != 0 {
            var bookMarks:[KMBookMarkItem] = []
            for index in self.bookTableView.selectedRowIndexes {
                let item = self.dataSource[index]
                bookMarks.append(item)
            }
            self.deleteBookMark(bookMarks: bookMarks)
        } else {
            __NSBeep()
        }
    }
    
    private func renameBookWithRow(row: Int) {
        self.renamePDFBook = self.dataSource[row]
        self.renameCellView = self.bookTableView.view(atColumn: 0, row: row, makeIfNecessary: true) as! KMBookCellView
        self.renameTextField = self.renameCellView.bookTitle
        self.renameTextField.delegate = self
        self.renameTextField.isEditable = true
        self.renameTextField.becomeFirstResponder()
    }
    
    //MARK: Noti
    @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 {
                    var containSelIndex:Bool = false
                    for (index, value) in self.dataSource.enumerated() {
                        if value.bookMark == self.listView.document.bookmark(forPageIndex: UInt(self.listView.currentPageIndex)) {
                            containSelIndex = true
                            self.didSelectItem(row: index, event: NSEvent())
                            break
                        }
                    }
                    if !containSelIndex {
                        self.cancelSelect()
                    }
                }
                isLocalEvent = false
            }
            self.updateAddBookMarkState()
        }
    }
    
    @objc func documentPageCountChangedNotification(notification: NSNotification) {
        if notification.object is CPDFDocument {
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { [unowned self] in
                let pdfdocument : CPDFDocument = notification.object as! CPDFDocument
                if pdfdocument.isEqual(self.listView.document) {
                    self.reloadData()
                }
            }
        }
    }
    
    //MARK: - NSTextFieldDelegate
    func controlTextDidEndEditing(_ obj: Notification) {
        if (self.renameTextField.isEqual(obj.object)) {
            let textField : NSTextField = obj.object as! NSTextField
            self.renamePDFBook(bookmark: self.renamePDFBook, label: textField.stringValue)
            self.renameTextField.isEditable = false
        }
    }
}

extension KMBookMarkViewController : NSTableViewDelegate,NSTableViewDataSource {
    func numberOfRows(in tableView: NSTableView) -> Int {
        let count = self.dataSource?.count ?? 0
        if count == 0 {
            self.emptyView.isHidden = false
        } else {
            self.emptyView.isHidden = true
        }
        return count
    }
    
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        if row < self.dataSource.count {
            let item: KMBookMarkItem = self.dataSource[row]
            let cell : KMBookCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMBookCellView"), owner: self) as! KMBookCellView
            cell.bookTitle.stringValue = item.bookMark.label
            return cell
        }
        return nil
    }
    
    func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
        let rowView = KMBookMarkTableRowView()
        if row < self.dataSource.count {
            rowView.model = self.dataSource[row]
            
            rowView.mouseDownAction = { [unowned self] (view, event) in
                self.didSelectItem(row: row, event: event)
            }
            
            rowView.rightMouseDownAction = { [unowned self] (view, event) in
                if !KMOCToolClass.arrayContains(self.selectItems, annotation: rowView.model) ||
                    self.selectItems.count == 1 {
                    self.selectIndex(index: row)
                }
                
                if self.bookTableView.rowView(atRow: row, makeIfNecessary: false) != nil {
                    let tempView = self.bookTableView.rowView(atRow: row, makeIfNecessary: false)
                    self.addMenuTitle(view: tempView!, event: event)
                }
            }
            
            rowView.hoverCallback = { [unowned self] (mouseEntered, mouseBox) in
                self.bookTableView.enumerateAvailableRowViews { view, row in
                    if view is KMBookMarkTableRowView {
                        (view as? KMBookMarkTableRowView)?.model.hover = false
                        (view as? KMBookMarkTableRowView)?.reloadData()
                    }
                }
                if mouseEntered {
                    rowView.model.hover = true
                } else {
                    rowView.model.hover = false
                }
            }
        }
        return rowView
    }
    
    func tableView(_ tableView: NSTableView, shouldSelect tableColumn: NSTableColumn?) -> Bool {
        self.isLocalEvent = true
        return true
    }
    
    func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
        self.isLocalEvent = true
        return true
    }
    
    func tableViewSelectionDidChange(_ notification: Notification) {
//        if self.bookTableView.selectedRow == -1 {
//            self.cancelSelect()
//        }
    }
    
    func selectIndex(index: Int) {
        self.bookTableView.selectRowIndexes(IndexSet(integer: IndexSet.Element(index)), byExtendingSelection: false)
        self.didSelectItem(row: index, event: NSEvent(), needJump: false)
    }

    func didSelectItem(row: Int, event: NSEvent?, needJump: Bool = true) {
        //当选中一个时
        if self.bookTableView.selectedRowIndexes.count == 1 || (event != nil &&
                                                                (!event!.modifierFlags.contains(NSEvent.ModifierFlags.command) &&
                                                                !event!.modifierFlags.contains(NSEvent.ModifierFlags.shift))) {
            self.bookTableView.selectRowIndexes(IndexSet(integer: IndexSet.Element(row)), byExtendingSelection: false)
        }
        
        //原始数据置空
        for model in self.selectItems {
            self.dataSource.contains { KMBookMarkItem in
                if KMBookMarkItem.bookMark == model.bookMark {
                    let index = KMOCToolClass.arrayIndex(of: self.dataSource, annotation: KMBookMarkItem)
                    if index != nil {
                        KMBookMarkItem.select = false
                        if self.bookTableView.rowView(atRow: index, makeIfNecessary: false) != nil {
                            let rowView: KMBookMarkTableRowView = self.bookTableView.rowView(atRow: index, makeIfNecessary: false) as! KMBookMarkTableRowView
                            rowView.reloadData()
                        }
                    }
                    return true
                }
                return false
            }
        }
    
        //获取最新数据
        var items: [KMBookMarkItem] = []
        for index in self.bookTableView.selectedRowIndexes {
            if index < self.dataSource.count {
                let model: KMBookMarkItem = self.dataSource[index]
                model.select = true
                if self.bookTableView.rowView(atRow: index, makeIfNecessary: false) != nil {
                    let rowView: KMBookMarkTableRowView = self.bookTableView.rowView(atRow: index, makeIfNecessary: false) as! KMBookMarkTableRowView
                    rowView.reloadData()
                }
                items.append(model)
            }
        }
        self.selectItems = items
        
        //刷新数据
        if needJump {
            self.updateListViewData()
        }
    }
    
    func updateListViewData() {
        if self.bookTableView.selectedRowIndexes.count == 1 &&
            self.bookTableView.selectedRowIndexes.first! < self.dataSource.count {
            let index = self.bookTableView.selectedRowIndexes.first
            let selectBookMark = self.dataSource[index!]
            self.listView.go(toPageIndex: selectBookMark.bookMark.pageIndex, animated: true)
        }
    }
    
    func cancelSelect() {
        self.bookTableView.deselectAll(nil)
        for model in self.selectItems {
            model.select = false
            let index = self.dataSource.firstIndex(of: model)
            if index != nil {
                if (self.bookTableView.rowView(atRow: index!, makeIfNecessary: false) != nil) {
                    let rowView: KMBookMarkTableRowView = self.bookTableView.rowView(atRow: index!, makeIfNecessary: false) as! KMBookMarkTableRowView
                    rowView.reloadData()
                }
            }
        }
    }
    
    func updateAddBookMarkState() {
//        self.addBookButton.isEnabled = self.canAddBorkMark()
    }
    
    func canAddBorkMark() -> Bool {
        if self.listView.document.bookmarks() != nil && self.listView.document.bookmarks()?.count != 0 {
            for bookMark in self.listView.document.bookmarks() {
                if bookMark.pageIndex == self.listView.currentPageIndex {
                    return false
                }
            }
        }
        return true
    }
}

//MARK: undoRedo
extension KMBookMarkViewController {
    func changeLocation(oldBookMark: KMBookMarkItem, newBookMark: KMBookMarkItem) {
        self.listView.document.removeBookmark(forPageIndex: oldBookMark.index)
        self.listView.document.addBookmark(newBookMark.label, forPageIndex: newBookMark.index)
        
        self.reloadData()
        self.listView.setNeedsDisplayForVisiblePages()
        
        self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
            self.changeLocation(oldBookMark: newBookMark, newBookMark: oldBookMark)
        }
    }
    
    func renamePDFBook(bookmark : KMBookMarkItem! , label:String) {
        if bookmark.bookMark.label == label {
            return
        }
        let temp = bookmark.bookMark.label
        
        bookmark.bookMark.label = label
        self.reloadData()
        
        var indexSet = IndexSet()
        indexSet.insert(self.bookTableView.row(for: self.renameCellView))
        self.bookTableView.selectRowIndexes(indexSet, byExtendingSelection: false)
        
        self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
            bookmark.bookMark.label = label
            self.renamePDFBook(bookmark: bookmark, label: temp ?? bookmark.label)
        }
    }
    
    func deleteBookMark(bookMarks: [KMBookMarkItem]) {
        for bookMark in bookMarks {
            if self.listView.document.removeBookmark(forPageIndex: bookMark.index) {
                KMPrint("删除标签成功")
            }
        }
        self.listView.setNeedsDisplayForVisiblePages()
        self.reloadData()
        
        //undo redo
        var saveBooks:[KMBookMarkItem] = bookMarks
        self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
            saveBooks.sort(){$0.index > $1.index}
            self.addBookMark(bookMarks: bookMarks)
        }
        
        guard let callBack = bookMarkDidChange else { return }
        
        callBack(self, bookMarks)
    }
    
    func addBookMark(bookMarks: [KMBookMarkItem]) {
        for bookMark in bookMarks {
            self.listView.document.addBookmark(bookMark.label, forPageIndex: UInt(bookMark.index))
        }
        self.listView.setNeedsDisplayForVisiblePages()
        self.reloadData()
        
        if bookMarks.count == 1 {
            DispatchQueue.main.async {
                if self.listView.document.bookmark(forPageIndex: UInt(bookMarks.first!.index)) != nil {
                    let item = KMBookMarkItem()
                    item.bookMark = self.listView.document.bookmark(forPageIndex: UInt(bookMarks.first!.index))
                    item.label = item.bookMark.label
                    item.index = UInt(item.bookMark.pageIndex)
                    self.addBookMarkAndEdit(newBookMark: item)
                }
            }
        }
        
        //undo redo
        var saveBooks:[KMBookMarkItem] = bookMarks
        self.listView.undoManager?.registerUndo(withTarget: self) { [unowned self] targetType in
            saveBooks.sort(){$0.index > $1.index}
            self.deleteBookMark(bookMarks: saveBooks)
        }
        
        guard let callBack = bookMarkDidChange else { return }
        
        callBack(self, bookMarks)
    }
    
    @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()
        }
    }
}

extension KMBookMarkViewController: NSMenuDelegate, NSMenuItemValidation {
    func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
        let action = menuItem.action
        if (action == #selector(undo)) {
            return self.listView.undoManager?.canUndo ?? false
        }
        if (action == #selector(redo)) {
            return self.listView.undoManager?.canRedo ?? false
        }
        
        if action == #selector(renameBookAction) ||
            action == #selector(changeLocationAction) ||
            action == #selector(deleteBookAction) {
            if self.bookTableView.selectedRowIndexes.count > 1 {
                if action == #selector(changeLocationAction) {
                    return false
                } else if action == #selector(renameBookAction) {
                    return false
                }
            } else if self.bookTableView.selectedRowIndexes.count == 1 {
                return true
            } else {
                if self.bookTableView.selectedRowIndexes.count == 0 {
                    if action == #selector(changeLocationAction) {
                        //                    if self.listView.document.bookmark(forPageIndex: UInt(self.listView.currentPageIndex)) != nil {
                        //                        return false
                        //                    }
                    }
                } else {
                    return false
                }
            }
        }
        return true
    }
}


class KMBookMarkItem: NSObject {
    var label: String = ""
    var index: UInt = 0
    var bookMark: CPDFBookmark = CPDFBookmark()
    var select: Bool = false
    var hover: Bool = false
}