//
//  KMBookmark.swift
//  Cisdem PDFMaster
//
//  Created by lizhe on 2024/2/5.
//

import Cocoa

let kType = "type"
let kChildren = "children"
let kLabel = "label"
let kPageIndex = "pageIndex"
let kBDAlias = "_BDAlias"

enum KMBookmarkType: String {
    case bookmark = "bookmark"
    case folder = "folder"
    case session = "session"
    case setup = "setup"
    case separator = "separator"
    case file = "file"
}

class KMBookmark: NSObject {
    var properties: [String: Any]? {
        get {
            var data: [String: Any] = documentSetup 
            var bdAlias: SKAlias?
            if self.fileURL != nil {
                bdAlias = SKAlias.init(url: self.fileURL)
            }
            let tempSetup = [kType: bookmarkType.rawValue,
                             kBDAlias: self.aliasData ?? (bdAlias != nil ? bdAlias?.data: NSData()),
                             kPageIndex: NSNumber(integerLiteral: Int(pageIndex)),
                             kLabel: self.label
            ] as [String : Any]
            data.merge(tempSetup) { (_, new) in new }
            return data
        }
        set {
            
        }
    }

    var bookmarkType: KMBookmarkType = .bookmark
    var label: String = ""
    var alternateIcon: NSImage =  NSImage()
    var fileURL: URL?
    var pageIndex: UInt = 0
    var pageNumber: NSNumber = 0
    
    var documentSetup: [String: Any] = [:] {
        didSet {
            if documentSetup[kType] != nil {
                self.bookmarkType = KMBookmarkType(rawValue: documentSetup[kType] as! String ) ?? .bookmark
            }
            let string: String = documentSetup[kPageIndex] as? String ?? "0"
            let intValue: UInt = UInt(string) ?? 0
            
            self.aliasData = documentSetup[kBDAlias] as? NSData
            self.pageIndex = intValue
            if documentSetup[kLabel] != nil {
                self.label = documentSetup[kLabel] as! String
            }
        }
    }
    var parent: KMBookmark?
    var children: [KMBookmark] = [] //子
    var aliasData: NSData?
    var icon: NSImage {
        get {
            if let fileURL = self.fileURL {
                return NSWorkspace.shared.icon(forFile: fileURL.path)
            } else {
                return KMFileBookmark.missingFileImage()
            }
        }
        set {
            
        }
    }
    
    static func bookmark(url: URL, pageIndex: UInt, label: String) -> KMBookmark {
        let bookmark = KMBookmark()
        bookmark.fileURL = url
        bookmark.pageIndex = pageIndex
        bookmark.label = label
        return bookmark
    }
    
    static func bookmarks(urls: [URL]) -> [KMBookmark] {
        var bookmarks: [KMBookmark] = []
        for url in urls {
            let bookmark = KMBookmark()
            bookmark.fileURL = url
            bookmarks.append(bookmark)
        }
        return bookmarks
    }
    
    func initWithProperties(properties: NSDictionary) -> KMBookmark {
        
        return KMBookmark()
    }
    
//    - (id)initWithProperties:(NSDictionary *)dictionary {
//        NSString *type = [dictionary objectForKey:TYPE_KEY];
//        if ([type isEqualToString:SEPARATOR_STRING]) {
//            return (id)[[SKSeparatorBookmark alloc] init];
//        } else if ([type isEqualToString:FOLDER_STRING] || [type isEqualToString:SESSION_STRING]) {
//            Class bookmarkClass = [type isEqualToString:FOLDER_STRING] ? [SKFolderBookmark class] : [SKSessionBookmark class];
//            NSMutableArray *newChildren = [NSMutableArray array];
//            SKBookmark *child;
//            for (NSDictionary *dict in [dictionary objectForKey:CHILDREN_KEY]) {
//                if ((child = [[SKBookmark alloc] initWithProperties:dict])) {
//                    [newChildren addObject:child];
//                    [child release];
//                } else
//                    NSLog(@"Failed to read child bookmark: %@", dict);
//            }
//            return (id)[[bookmarkClass alloc] initFolderWithChildren:newChildren label:[dictionary objectForKey:LABEL_KEY]];
//        } else if ([dictionary objectForKey:@"windowFrame"]) {
//            return (id)[[SKFileBookmark alloc] initWithSetup:dictionary label:[dictionary objectForKey:LABEL_KEY]];
//        } else {
//            NSNumber *pageIndex = [dictionary objectForKey:PAGEINDEX_KEY];
//            return (id)[[SKFileBookmark alloc] initWithAliasData:[dictionary objectForKey:ALIASDATA_KEY] pageIndex:(pageIndex ? [pageIndex unsignedIntegerValue] : NSNotFound) label:[dictionary objectForKey:LABEL_KEY]];
//        }
//    }

    static func bookmark(properties: [String: Any]) -> KMBookmark? {
        guard let type: KMBookmarkType = KMBookmarkType(rawValue: properties[kType] as! String) else { return nil}
        
        var bookmark: KMBookmark = KMBookmark()
        if type == .separator {
            bookmark = KMSeparatorBookmark()
        } else if type == .folder || type == .session {
            let bookmarkClass: AnyClass = (type == .folder) ? KMFolderBookmark.self : KMSessionBookmark.self
            guard let childrenProperties = properties[kChildren] as? [[String: Any]] else { return nil }
            var newChildren = [KMBookmark]()
            for dict: [String: Any] in childrenProperties {
                if let child = KMBookmark.bookmark(properties: dict) {
                    newChildren.append(child)
                } else {
                    NSLog("Failed to read child bookmark: \(dict)")
                }
            }
            guard let label = properties[kLabel] as? String else { return nil }
            bookmark = KMFolderBookmark.bookmark(children: newChildren, label: label)
        } else if properties["windowFrame"] != nil {
            guard let label = properties[kLabel] as? String else { return nil }
            bookmark = KMFileBookmark.bookmark(setup: properties, label: label)
            bookmark.label = label
        } else {
            let pageIndex = properties[kPageIndex] as? UInt ?? UInt(NSNotFound)
            let label = properties[kLabel] as? String
            let aliasData = properties[kBDAlias] as? Data

            if aliasData != nil {
                bookmark = KMFileBookmark.bookmark(aliasData: aliasData! as NSData, pageIndex: Int(pageIndex), label: label!)
            }
        }
        return bookmark
    }
    
    static func bookmark(children: [KMBookmark], label: String) -> KMBookmark {
        let bookmark = KMFolderBookmark()
        
        return bookmark
    }
    
    static func bookmark(url: [KMBookmark], label: String) -> KMBookmark {
        let bookmark = KMFolderBookmark()
        
        return bookmark
    }
    
    static func bookmark(aliasData: NSData, pageIndex: Int, label: String) -> KMBookmark {
        let bookmark = KMFileBookmark()
        
        let alias = SKAlias.init(data: aliasData as Data)
        let url = alias?.fileURL
        bookmark.fileURL = url
        bookmark.aliasData = aliasData
        return bookmark
    }
    
    static func bookmark(setup: [String: Any], label: String) -> KMBookmark {
        let pageIndex = setup[kPageIndex] as? UInt ?? UInt(NSNotFound)
        let aliasData = setup[kBDAlias] as? Data
        
        let bookmark = KMFileBookmark.bookmark(aliasData: aliasData! as NSData, pageIndex: Int(pageIndex), label: label)
        return bookmark
    }
    
//    func resolveAliasData(aliasData: Data) -> URL? {
//        var resolvedURL: URL?
//        do {
//            // 尝试创建NSURL实例
//            let aliasURL = URL(dataRepresentation: aliasData, relativeTo: nil)
//            
//            // 解析别名并获取原始路径
//            try aliasURL?.resolveAliasFileURL()
//            
//            // 将NSURL转换为URL
//            if let resolvedFileURL = aliasURL as URL? {
//                resolvedURL = resolvedFileURL
//            }
//        } catch {
//            print("Error resolving alias data:", error)
//        }
//        return resolvedURL
//    }
    
    
//        - (id)initWithProperties:(NSDictionary *)dictionary {
//            NSString *type = [dictionary objectForKey:TYPE_KEY];
//            if ([type isEqualToString:SEPARATOR_STRING]) {
//                return (id)[[SKSeparatorBookmark alloc] init];
//            } else if ([type isEqualToString:FOLDER_STRING] || [type isEqualToString:SESSION_STRING]) {
//                Class bookmarkClass = [type isEqualToString:FOLDER_STRING] ? [SKFolderBookmark class] : [SKSessionBookmark class];
//                NSMutableArray *newChildren = [NSMutableArray array];
//                SKBookmark *child;
//                for (NSDictionary *dict in [dictionary objectForKey:CHILDREN_KEY]) {
//                    if ((child = [[SKBookmark alloc] initWithProperties:dict])) {
//                        [newChildren addObject:child];
//                        [child release];
//                    } else
//                        NSLog(@"Failed to read child bookmark: %@", dict);
//                }
//                return (id)[[bookmarkClass alloc] initFolderWithChildren:newChildren label:[dictionary objectForKey:LABEL_KEY]];
//            } else if ([dictionary objectForKey:@"windowFrame"]) {
//                return (id)[[SKFileBookmark alloc] initWithSetup:dictionary label:[dictionary objectForKey:LABEL_KEY]];
//            } else {
//                NSNumber *pageIndex = [dictionary objectForKey:PAGEINDEX_KEY];
//                return (id)[[SKFileBookmark alloc] initWithAliasData:[dictionary objectForKey:ALIASDATA_KEY] pageIndex:(pageIndex ? [pageIndex unsignedIntegerValue] : NSNotFound) label:[dictionary objectForKey:LABEL_KEY]];
//            }
//        }
    
    func insert(child: KMBookmark, atIndex: Int) {
        children.insert(child, at: Int(atIndex))
        child.parent = self
    }
    
    func isDescendant(of bookmark: KMBookmark?) -> Bool {
        if bookmark == nil {
            return false
        }
        
        if self == bookmark {
            return true
        }
        for child in bookmark!.children {
            if self.isDescendant(of: child) {
                return true
            }
        }
        return false
    }
    
    func isDescendant(ofArray bookmarks: [KMBookmark]) -> Bool {
        for bm in bookmarks {
            if self.isDescendant(of: bm) {
                return true
            }
        }
        return false
    }
    
    func removeObjectFromChildren(index: Int) {
        children[index].parent = nil
        children.remove(at: index)
    }
    
    func open() {
        var tempFileURL = fileURL
        if tempFileURL == nil {
            if documentSetup[kBDAlias] != nil {
                tempFileURL = SKAlias.init(data: documentSetup[kBDAlias] as? Data).fileURL
            }
            if tempFileURL == nil && documentSetup[KMDocumentSetupFileNameKey] != nil {
                tempFileURL = NSURL.fileURL(withPath: documentSetup[KMDocumentSetupFileNameKey] as? String ?? "")
            }
        }
        
        guard let tempFileURL = tempFileURL else { return }
        NSDocumentController.shared.openDocument(withContentsOf: tempFileURL, display: true, completionHandler: { document, result, error in
            if (error != nil) {
                NSApp.presentError(error!)
            } else {
                if self.pageIndex > 1 {
                    (document as! KMMainDocument).mainViewController?.setPageNumber = self.pageIndex + 1
                }
            }
        })
    }
    
    func objectOfChidren(index: Int) -> KMBookmark{
        return children[index]
    }
}

//MARK: Folder
class KMFolderBookmark: KMBookmark {
    static func folderBookmark(label: String) -> KMFolderBookmark {
        let bookmark = KMFolderBookmark()
        bookmark.label = label
        return bookmark
    }
    
    override var bookmarkType: KMBookmarkType {
        get {
            return .folder
        }
        set {
            
        }
    }
    
    override func initWithProperties(properties: NSDictionary) -> KMBookmark {
        return KMFolderBookmark()
    }
    
    override var icon: NSImage {
        get {
            return NSImage(named: NSImage.folderName)!
        }
        set {
            
        }
    }
    
    override var alternateIcon: NSImage {
        get {
            return NSImage(named: NSImage.multipleDocumentsName)!
        }
        set {
            
        }
    }
    
    override func open() {
        var count = children.count
        while (count != 0) {
            count = count - 1
            children[count].open()
        }
    }
}

class KMRootBookmark: KMFolderBookmark {
    static func bookmarkRoot(childrenProperties: NSArray) -> KMRootBookmark {
        let rootBookmark = KMRootBookmark()
        var childs: [KMBookmark] = []
        for setup in childrenProperties {
            let bookmark = KMBookmark.bookmark(properties: setup as! [String : Any])
            bookmark?.documentSetup = setup as! [String : Any]
            bookmark?.parent = rootBookmark
            if bookmark != nil {
                childs.append(bookmark!)
            }
        }
        rootBookmark.children = childs
        rootBookmark.label = NSLocalizedString("Bookmarks Menu", comment: "")
        return rootBookmark
    }
    
    override var icon: NSImage {
        get {
            var iconImage = NSImage()
            let iconSize = NSSize(width: 16.0, height: 16.0)
            let drawingHandler: (NSRect) -> Bool = { rect in
                NSColor(calibratedWhite: 0.0, alpha: 0.2).set()
                NSBezierPath(rect: NSRect(x: 1.0, y: 1.0, width: 14.0, height: 13.0)).fill()
                NSGraphicsContext.saveGraphicsState()
                let path = NSBezierPath()
                path.move(to: NSPoint(x: 2.0, y: 2.0))
                path.line(to: NSPoint(x: 2.0, y: 15.0))
                path.line(to: NSPoint(x: 7.0, y: 15.0))
                path.line(to: NSPoint(x: 7.0, y: 13.0))
                path.line(to: NSPoint(x: 14.0, y: 13.0))
                path.line(to: NSPoint(x: 14.0, y: 2.0))
                path.close()
                NSColor.white.set()
                NSShadow.setShadowWith(NSColor(calibratedWhite: 0.0, alpha: 0.33333), blurRadius: 2.0, yOffset: -1.0)
                path.fill()
                NSGraphicsContext.restoreGraphicsState()
                NSColor(calibratedRed: 0.162, green: 0.304, blue: 0.755, alpha: 1.0).set()
                NSRect(x: 2.0, y: 13.0, width: 5.0, height: 2.0).fill()
                NSColor(calibratedRed: 0.894, green: 0.396, blue: 0.202, alpha: 1.0).set()
                NSRect(x: 3.0, y: 4.0, width: 1.0, height: 1.0).fill()
                NSRect(x: 3.0, y: 7.0, width: 1.0, height: 1.0).fill()
                NSRect(x: 3.0, y: 10.0, width: 1.0, height: 1.0).fill()
                NSColor(calibratedWhite: 0.6, alpha: 1.0).set()
                NSRect(x: 5.0, y: 4.0, width: 1.0, height: 1.0).fill()
                NSRect(x: 5.0, y: 7.0, width: 1.0, height: 1.0).fill()
                NSRect(x: 5.0, y: 10.0, width: 1.0, height: 1.0).fill()
                for i in 0..<7 {
                    for j in 0..<3 {
                        NSColor(calibratedWhite: 0.45 + 0.1 * CGFloat(Float(arc4random()) / Float(UInt32.max)), alpha: 1.0).set()
                        NSRect(x: 6.0 + CGFloat(i), y: 4.0 + CGFloat(3 * j), width: 1.0, height: 1.0).fill()
                    }
                }
                let gradient = NSGradient(starting: NSColor(calibratedWhite: 0.0, alpha: 0.1), ending: NSColor(calibratedWhite: 0.0, alpha: 0.0))
                gradient?.draw(in: NSRect(x: 2.0, y: 2.0, width: 12.0, height: 11.0), angle: 90.0)
                return true
            }
            iconImage = NSImage(size: iconSize, flipped: false, drawingHandler: drawingHandler)
            
            return iconImage
        }
        set {
            
        }
    }
}

//MARK: Session
class KMSessionBookmark: KMFolderBookmark {
    override var bookmarkType: KMBookmarkType {
        get {
            return .session
        }
        set {
            
        }
    }
    
    static func bookmarkSession(setups: NSArray, label: String) -> KMSessionBookmark {
        let bookmark = KMSessionBookmark()
        var childs: [KMBookmark] = []
        for setup in setups {
            let bookmark = KMBookmark()
            bookmark.documentSetup = setup as! [String : Any]
            childs.append(bookmark)
        }
        bookmark.children = childs
        bookmark.label = label
        return bookmark
    }
    
    override func initWithProperties(properties: NSDictionary) -> KMBookmark {
        return KMSessionBookmark()
    }
    
    override var icon: NSImage {
        get {
            return NSImage(named: NSImage.multipleDocumentsName)!
        }
        set {
            
        }
    }
}

//MARK: Separator
class KMSeparatorBookmark: KMBookmark {
    override var bookmarkType: KMBookmarkType {
        get {
            return .separator
        }
        set {
            
        }
    }
    
    override func initWithProperties(properties: NSDictionary) -> KMBookmark {
        return KMSeparatorBookmark()
    }
}

//MARK: File
class KMFileBookmark: KMBookmark {
    override var bookmarkType: KMBookmarkType {
        get {
            return .file
        }
        set {
            
        }
    }
    
    static func missingFileImage() -> NSImage {
        let image: NSImage = {
            let size = NSSize(width: 16.0, height: 16.0)
            return NSImage(size: size, flipped: false, drawingHandler: { rect in
                let genericDocImage = NSWorkspace.shared.icon(forFileType: NSFileTypeForHFSTypeCode(OSType(kGenericDocumentIcon)))
                let questionMark = NSWorkspace.shared.icon(forFileType: NSFileTypeForHFSTypeCode(OSType(kQuestionMarkIcon)))
                genericDocImage.draw(in: rect, from: .zero, operation: .copy, fraction: 0.7)
                questionMark.draw(in: NSRect(x: 3.0, y: 2.0, width: 10.0, height: 10.0), from: .zero, operation: .sourceOver, fraction: 0.7)
                return true
            })
        }()
        return image
    }
    
    override var icon: NSImage {
        get {
            if let fileURL = self.fileURL {
                return NSWorkspace.shared.icon(forFile: fileURL.path)
            } else {
                return KMFileBookmark.missingFileImage()
            }
        }
        set {
            
        }
    }

}



class AliasData: Codable {
    
}