//
//  KMInfoWindowController.swift
//  PDF Reader Pro
//
//  Created by tangchao on 2023/10/10.
//

import Cocoa

/*
 #define SKInfoWindowFrameAutosaveName @"SKInfoWindow"

 #define LABEL_COLUMN_ID @"label"
 #define VALUE_COLUMN_ID @"value"
 */
  
private let LABEL_COLUMN_ID = "label"
private let VALUE_COLUMN_ID = "value"

private let KMInfoVersionKey = "Version"
private let KMInfoPageCountKey = "PageCount"
private let KMInfoPageSizeKey = "PageSize"
private let KMInfoPageWidthKey = "PageWidth"
private let KMInfoPageHeightKey = "PageHeight"
private let KMInfoKeywordsStringKey = "Keywords"
private let KMInfoEncryptedKey = "Encrypted"
private let KMInfoAllowsPrintingKey = "AllowsPrinting"
private let KMInfoAllowsCopyingKey = "AllowsCopying"
private let KMInfoAllowsDocumentAssemblyKey = "AllowsDocumentAssembly"
private let KMInfoAllowsContentAccessibilityKey = "AllowsContentAccessibility"
private let KMInfoAllowsCommentingKey = "AllowsCommenting"
private let KMInfoAllowsFormFieldEntryKey = "AllowsFormFieldEntry"
private let KMInfoFileNameKey = "FileName"
private let KMInfoFileSizeKey = "FileSize"
private let KMInfoPhysicalSizeKey = "PhysicalSize"
private let KMInfoLogicalSizeKey = "LogicalSize"
private let KMInfoTagsKey = "Tags"
private let KMInfoRatingKey = "Rating"

private let kKMWindowDidBecomeMainNotificationName = Notification.Name(rawValue: "KMWindowDidBecomeMainNotificationName")

typealias KMInfoWindowC = KMInfoWindowController

class KMInfoWindowController: NSWindowController {
    public static let windowDidBecomeMainNotification = kKMWindowDidBecomeMainNotificationName
    
    @IBOutlet weak var summaryTableView: NSTableView!
    @IBOutlet weak var attributesTableView: NSTableView!
    @IBOutlet weak var tabView: NSTabView!
    var info: NSDictionary?
    
    var editDictionary: NSMutableDictionary = NSMutableDictionary()
    
    private var _summaryKeys: [String] = []
    var summaryKeys: [String] {
        get {
            return self._summaryKeys
        }
    }
    private var _attributesKeys: [String] = []
    var attributesKeys: [String] {
        get {
            return self._attributesKeys
        }
    }
    
    private var _labels: [String : String] = [:]
    var labels: [String : String] {
        get {
            return self._labels
        }
    }
    
    private weak var _currentDocument: NSDocument?
    
    deinit {
        NotificationCenter.default.removeObserver(self)
        
        Swift.debugPrint("KMInfoWindowController 已释放")
    }
    
    static let shared = KMInfoWindowController()
    
    convenience init() {
        self.init(windowNibName: "InfoWindow")
        
        self.info = nil;
        self._summaryKeys = [KMInfoFileNameKey,
                             KMInfoFileSizeKey,
                             KMInfoPageSizeKey,
                             KMInfoPageCountKey,
                             KMInfoVersionKey,
                             "",
                             KMInfoEncryptedKey,
                             KMInfoAllowsPrintingKey,
                             KMInfoAllowsCopyingKey,
                             KMInfoAllowsDocumentAssemblyKey,
                             KMInfoAllowsContentAccessibilityKey,
                             KMInfoAllowsCommentingKey,
                             KMInfoAllowsFormFieldEntryKey]
        
        self._attributesKeys = [PDFDocumentAttribute.titleAttribute.rawValue,
                                PDFDocumentAttribute.authorAttribute.rawValue,
                                PDFDocumentAttribute.subjectAttribute.rawValue,
                                PDFDocumentAttribute.creatorAttribute.rawValue,
                                PDFDocumentAttribute.producerAttribute.rawValue,
                                PDFDocumentAttribute.creationDateAttribute.rawValue,
                                PDFDocumentAttribute.modificationDateAttribute.rawValue,
                                KMInfoKeywordsStringKey]
        self._labels = [KMInfoFileNameKey : KMLocalizedString("File name:", ""),
                        KMInfoFileSizeKey : KMLocalizedString("File size:", ""),
                        KMInfoPageSizeKey : KMLocalizedString("Page size:", ""),
                        KMInfoPageCountKey : KMLocalizedString("Page count:", ""),
                        KMInfoVersionKey : KMLocalizedString("PDF Version:", ""),
                       KMInfoEncryptedKey : KMLocalizedString("Encrypted:", ""),
                  KMInfoAllowsPrintingKey : KMLocalizedString("Printing:", ""),
                    KMInfoAllowsCopyingKey : KMLocalizedString("Content Copying:", ""),
           KMInfoAllowsDocumentAssemblyKey : KMLocalizedString("Document Assembly:", ""),
       KMInfoAllowsContentAccessibilityKey : KMLocalizedString("Content Copying for Accessibility:", ""),
                 KMInfoAllowsCommentingKey : KMLocalizedString("Commenting:", ""),
             KMInfoAllowsFormFieldEntryKey : KMLocalizedString("Filling of form fields:", ""),
                        PDFDocumentAttribute.titleAttribute.rawValue : KMLocalizedString("Title:", ""),
                        PDFDocumentAttribute.authorAttribute.rawValue : KMLocalizedString("Author:", ""),
                        PDFDocumentAttribute.subjectAttribute.rawValue : KMLocalizedString("Subject:", ""),
                        PDFDocumentAttribute.creatorAttribute.rawValue : KMLocalizedString("Content Creator:", ""),
                        PDFDocumentAttribute.producerAttribute.rawValue : KMLocalizedString("PDF Producer:", ""),
                        PDFDocumentAttribute.creationDateAttribute.rawValue : KMLocalizedString("Creation date:", ""),
                        PDFDocumentAttribute.modificationDateAttribute.rawValue : KMLocalizedString("Modification date:", ""),
                   KMInfoKeywordsStringKey : KMLocalizedString("Keywords:", "")]
    }
    
    override func loadWindow() {
        super.loadWindow()
        
        self.window?.localizeStrings(fromTable: self.windowNibName)
    }

    override func windowDidLoad() {
        super.windowDidLoad()
        
        self.window?.title = NSLocalizedString("Document Info", comment: "")
    
        self.updateForDocument(NSApp.mainWindow?.windowController?.document as? NSDocument)
        
        self.summaryTableView.selectionHighlightStyle = .none
        self.attributesTableView.selectionHighlightStyle = .none

        for item in self.tabView.tabViewItems {
            if item.isEqual(to: self.tabView.tabViewItems.first) {
                item.label = NSLocalizedString("Summary", comment: "")
            } else {
                item.label = NSLocalizedString("Attributes", comment: "")
            }
        }
        self.tabView.selectTabViewItem(at: 1)

        NotificationCenter.default.addObserver(self, selector: #selector(_handleViewFrameDidChangeNotification), name: NSView.frameDidChangeNotification, object: self.attributesTableView.enclosingScrollView)
        NotificationCenter.default.addObserver(self, selector: #selector(_handleWindowDidBecomeMainNotification), name: NSWindow.didBecomeMainNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(_handleWindowDidResignMainNotification), name: NSWindow.didResignMainNotification, object: nil)
//    //    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handlePDFDocumentInfoDidChangeNotification:)
//    //                                                 name: SKPDFPageBoundsDidChangeNotification object: nil];
//    //    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleDocumentFileURLDidChangeNotification:)
//    //                                                 name: SKDocumentFileURLDidChangeNotification object: nil];
        NotificationCenter.default.addObserver(self, selector: #selector(_handlePDFDocumentInfoDidChangeNotification), name: NSNotification.Name.PDFDocumentDidUnlock, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(_handleWindowWillCloseNotification), name: NSWindow.willCloseNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(_km_handleWindowDidBecomeMainNotification), name: KMInfoWindowC.windowDidBecomeMainNotification, object: nil)
    }
    
    func updateForDocument(_ doc: NSDocument?) {
        self._currentDocument = doc

        let info = self._infoForDocument(doc)
        var dic = NSMutableDictionary(dictionary: info)
        for key in self.editDictionary.allKeys {
            dic.setObject(self.editDictionary[key], forKey: key as! NSCopying)
        }
        self.info = dic
        self.summaryTableView.reloadData()
        self.attributesTableView.reloadData()
    }
    
    public func info(for document: NSDocument) -> NSDictionary {
        return self._infoForDocument(document)
    }
    
    // MARK: - Private Methods
     
    private func _infoForDocument(_ doc: NSDocument?) -> NSDictionary {
        var dictionary: NSMutableDictionary = NSMutableDictionary()
        var logicalSize: Int64 = 0
        var physicalSize: Int64 = 0
        
        if let data = doc, data.isKind(of: KMMainDocument.self) {
            if let pdfDoc = (data as! KMMainDocument).mainViewController?.document {
                dictionary.addEntries(from: pdfDoc.documentAttributes())
                dictionary.setValue(String(format: "%ld.%ld", pdfDoc.majorVersion, pdfDoc.minorVersion), forKey: KMInfoVersionKey)
                dictionary.setValue(NSNumber(value: pdfDoc.pageCount), forKey: KMInfoPageCountKey)
                if (pdfDoc.pageCount > 0) {
                    let page: CPDFPage = pdfDoc.page(at:0)!
                    let cropSize = page.bounds(for: .cropBox).size
                    let mediaSize = page.bounds(for: .mediaBox).size
                    dictionary.setValue(KMSizeString(cropSize, mediaSize), forKey: KMInfoPageSizeKey)
                    dictionary.setValue(NSNumber(value: cropSize.width), forKey: KMInfoPageWidthKey)
                    dictionary.setValue(NSNumber(value: cropSize.height), forKey: KMInfoPageHeightKey)
                }
                if let keyworks = dictionary.value(forKey: CPDFDocumentAttribute.keywordsAttribute.rawValue) {
                    if (keyworks as AnyObject).isKind(of: NSArray.self) {
                        dictionary.setValue(keyworks, forKey: KMInfoKeywordsStringKey)
                    }
                }
                dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.isEncrypted), forKey: KMInfoEncryptedKey)
                dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsPrinting), forKey: KMInfoAllowsPrintingKey)
                dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsCopying), forKey: KMInfoAllowsCopyingKey)
                
//                dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsContentAccessibility), forKey: KMInfoAllowsContentAccessibilityKey)
                dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsCommenting), forKey: KMInfoAllowsCommentingKey)
                dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsFormFieldEntry), forKey: KMInfoAllowsDocumentAssemblyKey)
                dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsFormFieldEntry), forKey: KMInfoAllowsFormFieldEntryKey)
            }
        }
        
        dictionary.setValue(doc?.fileURL?.path.lastPathComponent, forKey: KMInfoFileNameKey)
//        [dictionary setValue:SKFileSizeStringForFileURL([doc fileURL], &physicalSize, &logicalSize) forKey:SKInfoFileSizeKey];
        dictionary.setValue(NSNumber(value: physicalSize), forKey: KMInfoPhysicalSizeKey)
        dictionary.setValue(NSNumber(value: logicalSize), forKey: KMInfoLogicalSizeKey)
        return dictionary
    }
    
    @objc private func _handleViewFrameDidChangeNotification(_ sender: Notification) {
        self.attributesTableView.noteHeightOfRows(withIndexesChanged: IndexSet(integer: self.attributesKeys.count-1))
    }
    
    @objc private func _handleWindowDidBecomeMainNotification(_ sender: Notification) {
        self.editDictionary.removeAllObjects()
        self.updateForDocument((sender.object as? NSWindow)?.windowController?.document as? NSDocument)
    }
    
    @objc private func _handleWindowDidResignMainNotification(_ sender: Notification) {
        self.editDictionary.removeAllObjects()
        self.updateForDocument(nil)
    }
    
    @objc private func _handlePDFDocumentInfoDidChangeNotification(_ sender: Notification) {
        guard let doc = NSApp.mainWindow?.windowController?.document else {
            return
        }
        
        if (doc.isKind(of: KMMainDocument.self)) {
            guard let pdfDocument = (doc as! KMMainDocument).mainViewController?.document else {
                return
            }
            if (pdfDocument.isEqual(to: sender.object)) {
                self.editDictionary.removeAllObjects()
                self.updateForDocument((doc as! NSDocument))
            }
        }
    }
    
    @objc private func _handleWindowWillCloseNotification(_ sender: Notification) {
        if let object = sender.object as? AnyObject, object.isEqual(self.window) {
            if (self.editDictionary.count > 0) {
                if let windowController = NSApp.mainWindow?.windowController, windowController.isKind(of: KMBrowserWindowController.self) {
                    let document = (windowController as! KMBrowserWindowController).document as? KMMainDocument
                    var dic: NSMutableDictionary = NSMutableDictionary(dictionary: document?.mainViewController?.document?.documentAttributes() ?? [:])
                    var isSave = false
                    for key in self.editDictionary.allKeys {
                        if let data = key as? NSString, data.isEqual(to: KMInfoKeywordsStringKey) {
                            let string = self.editDictionary[key]
                            if let data = string as? NSString, data.isEqual(to: dic[PDFDocumentAttribute.keywordsAttribute.rawValue]) {
                                isSave = true
                                dic.setObject(string as Any, forKey: CPDFDocumentAttribute.keywordsAttribute.rawValue as NSCopying)
                            }
                        } else {
                            if let data = dic[key] as? NSString, data.isEqual(to: self.editDictionary[key]) == false {
                                isSave = true
                                dic.setObject(self.editDictionary[key] as Any, forKey: key as! NSCopying)
                            }
                        }
                    }
                    if (isSave) {
                        document?.mainViewController?.document?.setDocumentAttributes(dic as? [CPDFDocumentAttribute : Any])
                        document?.save(nil)
                    }
                }
            }
            self.editDictionary.removeAllObjects()
        }
    }
    
    @objc private func _km_handleWindowDidBecomeMainNotification(_ sender: Notification) {
        self.editDictionary.removeAllObjects()
        self.updateForDocument(sender.object as? NSDocument)
    }
    
    /*
     - (void)handlePDFDocumentInfoDidChangeNotification:(NSNotification *)notification {
         [self.editDictionary removeAllObjects];
         NSDocument *doc = [[[NSApp mainWindow] windowController] document];
         if ([doc isKindOfClass:[KMMainDocument class]]) {
             CPDFDocument *pdfDocument = ((KMMainDocument *)doc).mainViewController.document;
             if ([pdfDocument isEqual:notification.object]) {
                 [self updateForDocument:doc];
             }
         }
     }

     - (void)handleDocumentFileURLDidChangeNotification:(NSNotification *)notification {
         [self.editDictionary removeAllObjects];
         NSDocument *doc = [[[NSApp mainWindow] windowController] document];
         if ([doc isEqual:[notification object]])
             [self updateForDocument:doc];
     }
     */

    override func showWindow(_ sender: Any?) {
        super.showWindow(sender)
        
        self.editDictionary.removeAllObjects()
        self.attributesTableView.reloadData()
    }
    /*

     #define BYTE_FACTOR 1024
     #define BYTE_FACTOR_F 1024.0f
     #define BYTE_SHIFT 10

     static NSString *SKFileSizeStringForFileURL(NSURL *fileURL, unsigned long long *physicalSizePtr, unsigned long long *logicalSizePtr) {
         if (fileURL == nil)
             return @"";
         
         FSRef fileRef;
         FSCatalogInfo catalogInfo;
         unsigned long long size, logicalSize = 0;
         BOOL gotSize = NO, isDir = NO;
         NSMutableString *string = [NSMutableString string];
         
         Boolean gotRef = CFURLGetFSRef((CFURLRef)fileURL, &fileRef);
         if (gotRef && noErr == FSGetCatalogInfo(&fileRef, kFSCatInfoDataSizes | kFSCatInfoRsrcSizes | kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL, NULL)) {
             size = catalogInfo.dataPhysicalSize + catalogInfo.rsrcPhysicalSize;
             logicalSize = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
             isDir = (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0;
             gotSize = YES;
         }
         
         if (gotSize == NO) {
             // this seems to give the logical size
             NSDictionary *fileAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:NULL];
             logicalSize = size = [[fileAttrs objectForKey:NSFileSize] unsignedLongLongValue];
             isDir = [[fileAttrs fileType] isEqualToString:NSFileTypeDirectory];
         }
         
         if (isDir) {
             NSString *path = [fileURL path];
             unsigned long long componentSize;
             unsigned long long logicalComponentSize;
             for (NSString *file in [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:path error:NULL]) {
                 SKFileSizeStringForFileURL([NSURL fileURLWithPath:[path stringByAppendingPathComponent:file]], &componentSize, &logicalComponentSize);
                 size += componentSize;
                 logicalSize += logicalComponentSize;
             }
         }
         
         if (physicalSizePtr)
             *physicalSizePtr = size;
         if (logicalSizePtr)
             *logicalSizePtr = logicalSize;
         
         if (size < BYTE_FACTOR) {
             [string appendFormat:@"%qu %@", size, NSLocalizedString(@"bytes", @"size unit")];
         } else {
             #define numUnits 6
             NSString *units[numUnits] = {NSLocalizedString(@"kB", @"size unit"), NSLocalizedString(@"MB", @"size unit"), NSLocalizedString(@"GB", @"size unit"), NSLocalizedString(@"TB", @"size unit"), NSLocalizedString(@"PB", @"size unit"), NSLocalizedString(@"EB", @"size unit")};
             NSUInteger i;
             for (i = 0; i < numUnits; i++, size >>= BYTE_SHIFT) {
                 if ((size >> BYTE_SHIFT) < BYTE_FACTOR || i == numUnits - 1) {
                     [string appendFormat:@"%.1f %@", size / BYTE_FACTOR_F, units[i]];
                     break;
                 }
             }
         }
         
         NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
         
         [formatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
         [formatter setNumberStyle:NSNumberFormatterDecimalStyle];
         [string appendFormat:@" (%@ %@)", [formatter stringFromNumber:[NSNumber numberWithUnsignedLongLong:logicalSize]], NSLocalizedString(@"bytes", @"size unit")];
         
         return string;
     }

     - (NSArray *)keys {
         return [attributesKeys arrayByAddingObjectsFromArray:summaryKeys];
     }
     */
}

extension KMInfoWindowController: NSTableViewDelegate, NSTableViewDataSource {
    func numberOfRows(in tableView: NSTableView) -> Int {
        if (tableView.isEqual(to: self.summaryTableView)) {
            return self.summaryKeys.count
        } else if (tableView.isEqual(to: self.attributesTableView)) {
            return self.attributesKeys.count
        }
        return 0
    }
    
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        var shortDateFormatter = DateFormatter()
        shortDateFormatter.dateStyle = .short
        shortDateFormatter.timeStyle = .none
        
        var keys: [String]?
        if (tableView.isEqual(to: self.summaryTableView)) {
            keys = self.summaryKeys
        } else if (tableView.isEqual(to: self.attributesTableView)) {
            keys = self.attributesKeys
        }
        
        let key = keys![row]
        let tcID = tableColumn?.identifier.rawValue
        var value: Any?
        if (key.isEmpty) {
            value = ""
        } else {
            if let data = tcID, data == LABEL_COLUMN_ID {
                value = self.labels[key] != nil ? self.labels[key] : key.appending(":")
            } else if let data = tcID, data == VALUE_COLUMN_ID {
                value = self.info?.object(forKey: key)
                if (value == nil) {
                    value = "-"
                } else if let data = value as? NSDate {
                    value = shortDateFormatter.string(from: data as Date)
                } else if let data = value as? NSNumber {
                    if (key == KMInfoEncryptedKey) {
                        value = data.boolValue ? KMLocalizedString("Yes", "") : KMLocalizedString("No", "")
                    } else {
                        value = key == KMInfoPageCountKey ? data.stringValue : (data.boolValue ? KMLocalizedString("Allowed", "") : KMLocalizedString("Not Allowed", ""))
                    }
                }
            }
        }
        
        let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: tcID ?? ""), owner: self) as? NSTableCellView
        cellView?.textField?.stringValue = (value as? String) ?? ""
        if (tableView.isEqual(to: self.attributesTableView) && tableColumn?.identifier.rawValue == VALUE_COLUMN_ID) {
            if (row == 0 || row == 1 || row == 2 || row == 3 || row == 7) {
                cellView?.textField?.bezelStyle = .squareBezel
                cellView?.textField?.isEditable = true
                cellView?.textField?.isSelectable = true
                cellView?.textField?.isBordered = true
                cellView?.textField?.isBezeled = true
                cellView?.textField?.drawsBackground = true
                cellView?.textField?.delegate = self
                
                if (row == 1) {
                    let cell = tableView.view(atColumn: 1, row: 0, makeIfNecessary: true) as? NSTableCellView
                    cell?.textField?.nextKeyView = cellView!.textField
                } else if (row == 2) {
                    let cell = tableView.view(atColumn: 1, row: 1, makeIfNecessary: true) as? NSTableCellView
                    cell?.textField?.nextKeyView = cellView!.textField
                } else if (row == 3) {
                    let cell = tableView.view(atColumn: 1, row: 2, makeIfNecessary: true) as? NSTableCellView
                    cell?.textField?.nextKeyView = cellView!.textField
                } else if (row == 7) {
                    let cell = tableView.view(atColumn: 1, row: 3, makeIfNecessary: true) as? NSTableCellView
                    cell?.textField?.nextKeyView = cellView!.textField
                }
            } else {
                cellView?.textField?.isEditable = false
                cellView?.textField?.isSelectable = false
                cellView?.textField?.isBordered = false
                cellView?.textField?.isBezeled = false
                cellView?.textField?.drawsBackground = false
                cellView?.textField?.delegate = nil;
            }
            cellView?.textField?.tag = row
        }
    
        return cellView
    }
    
    func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
        var rowHeight = tableView.rowHeight
        if (tableView.isEqual(to: self.attributesTableView) && row == tableView.numberOfRows-1) {
            
            //        }
            //        if ([tv isEqual:attributesTableView] && row == [tv numberOfRows] - 1)
            rowHeight = fmax(rowHeight, NSHeight(tableView.enclosingScrollView?.bounds ?? NSZeroRect) - tableView.numberOfRows.cgFloat * (rowHeight + tableView.intercellSpacing.height) + rowHeight)
        }
        return rowHeight
    }
    /*
     - (id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
         static NSDateFormatter *shortDateFormatter = nil;
         if(shortDateFormatter == nil) {
             shortDateFormatter = [[NSDateFormatter alloc] init];
             [shortDateFormatter setDateStyle:NSDateFormatterShortStyle];
             [shortDateFormatter setTimeStyle:NSDateFormatterNoStyle];
         }
         NSArray *keys = nil;
         if ([tv isEqual:summaryTableView])
             keys = summaryKeys;
         else if ([tv isEqual:attributesTableView])
             keys = attributesKeys;
         NSString *key = [keys objectAtIndex:row];
         NSString *tcID = [tableColumn identifier];
         id value = nil;
         if ([key length]) {
             if ([tcID isEqualToString:LABEL_COLUMN_ID]) {
                 value = [labels objectForKey:key] ?: [key stringByAppendingString:@":"];
             } else if ([tcID isEqualToString:VALUE_COLUMN_ID]) {
                 value = [info objectForKey:key];
                 if (value == nil)
                     value = @"-";
                 else if ([value isKindOfClass:[NSDate class]])
                     value = [shortDateFormatter stringFromDate:value];
                 else if ([value isKindOfClass:[NSNumber class]]){
                     if ([key isEqualToString:SKInfoEncryptedKey]) {
                         value = [value boolValue] ? NSLocalizedString(@"Yes", @"") : NSLocalizedString(@"No", @"");
                     } else {
                         value = ([key isEqualToString:SKInfoPageCountKey] ? [value stringValue] : ([value boolValue] ? NSLocalizedString(@"Allowed", @"") : NSLocalizedString(@"Not Allowed", @"")));
                     }
                 }
             }
         }
         return value;
     }

     - (void)tableView:(NSTableView *)tv willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
         if ([tv isEqual:attributesTableView] && [[tableColumn identifier] isEqualToString:LABEL_COLUMN_ID])
             [cell setLineBreakMode:row == [tv numberOfRows] - 1 ? NSLineBreakByWordWrapping : NSLineBreakByTruncatingTail];
     }

     - (CGFloat)tableView:(NSTableView *)tv heightOfRow:(NSInteger)row {
         CGFloat rowHeight = [tv rowHeight];
         if ([tv isEqual:attributesTableView] && row == [tv numberOfRows] - 1)
             rowHeight = fmax(rowHeight, NSHeight([[tv enclosingScrollView] bounds]) - [tv numberOfRows] * (rowHeight + [tv intercellSpacing].height) + rowHeight);
         return rowHeight;
     }

     - (BOOL)tableView:(NSTableView *)tv shouldSelectRow:(NSInteger)row {
         return YES;
     }

     - (void)tabView:(NSTabView *)tabView willSelectTabViewItem:(nullable NSTabViewItem *)tabViewItem
     {
         if ([tabView indexOfTabViewItem:tabViewItem] == 1) {
             if (self.currentDocument) {
                 NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary:[self infoForDocument:self.currentDocument]];
                 for (NSString *key in self.editDictionary.allKeys) {
                     [dic setObject:self.editDictionary[key] forKey:key];
                 }
                 [self setInfo:dic];
             }
             
             [self.attributesTableView reloadData];
         } else {
             [self.summaryTableView reloadData];
         }
     }
     */
}

extension KMInfoWindowController: NSTextFieldDelegate {
    func controlTextDidChange(_ obj: Notification) {
        if let textField = obj.object as? NSTextField {
            if (textField.tag == 0) {
                self.editDictionary.setObject(textField.stringValue, forKey: PDFDocumentAttribute.titleAttribute.rawValue as NSCopying)
            } else if (textField.tag == 1) {
                self.editDictionary.setObject(textField.stringValue, forKey: PDFDocumentAttribute.authorAttribute.rawValue as NSCopying)
            } else if (textField.tag == 2) {
                self.editDictionary.setObject(textField.stringValue, forKey: PDFDocumentAttribute.subjectAttribute.rawValue as NSCopying)
            } else if (textField.tag == 3) {
                self.editDictionary.setObject(textField.stringValue, forKey: PDFDocumentAttribute.creatorAttribute.rawValue as NSCopying)
            } else if (textField.tag == 7) {
                self.editDictionary.setObject(textField.stringValue, forKey: KMInfoKeywordsStringKey as NSCopying)
            }
        }
    }
}

private let CM_PER_POINT = 0.035277778
private let INCH_PER_POINT = 0.013888889
private func KMSizeString(_ size: NSSize, _ altSize: NSSize) -> String {
    var useMetric = false
    if let data = ((NSLocale.current as NSLocale).object(forKey: .usesMetricSystem) as? NSNumber) {
        useMetric = data.boolValue
    }
    
    let factor = useMetric ? CM_PER_POINT : INCH_PER_POINT
    let units = useMetric ? KMLocalizedString("cm", "size unit") : KMLocalizedString("in", "size unit")
    if (NSEqualSizes(size, altSize)) {
        return String(format: "%.2f x %.2f %@", size.width * factor, size.height * factor, units)
    } else {
        return String(format: "%.2f x %.2f %@  (%.2f x %.2f %@)", size.width * factor, size.height * factor, units, altSize.width * factor, altSize.height * factor, units)
    }
}