//
//  KMPreferenceManager.swift
//  PDF Reader Pro
//
//  Created by tangchao on 2023/2/3.
//

import Cocoa

enum KMPreferenceGroup: String {
case general, display, markup, infomation, other
}

private let KMPreferenceInfoKey: String = "KMPreferenceInfoKey"

typealias KMPreferenceKey = String
/// general
/// files
private let KMOpenLastUnclosedDocumentWhenAppStartKey: KMPreferenceKey = "KMOpenLastUnclosedDocumentWhenAppStartKey"
private let KMOpenLastUnlockedDocumentWhenAppStartKey: KMPreferenceKey = "KMOpenLastUnlockedDocumentWhenAppStartKey"
private let KMDocumentMaximunDisplayNumberKey: KMPreferenceKey = "KMDocumentMaximunDisplayNumberKey"
private let KMAutoSaveKey: KMPreferenceKey = "KMAutoSaveKey"
private let KMAutoSavePerNumberMinuteKey: KMPreferenceKey = "KMAutoSavePerNumberMinuteKey"
private let KMCloseFilePromptTypeKey: KMPreferenceKey = "KMCloseFilePromptTypeKey"
/// open image file
private let KMOpenImageFileTypeKey: KMPreferenceKey = "KMOpenImageFileTypeKey"
private let KMSetDefaultPDFReaderKey: KMPreferenceKey = "KMSetDefaultPDFReaderKey"
/// save password
private let KMSavePasswordTypeKey: KMPreferenceKey = "KMSavePasswordTypeKey"
private let KMGeneralAuthorNameKey: KMPreferenceKey = "KMGeneralAuthorNameKey"

// Tip: 新补充

private let KMOpenDocumentTypeKey: KMPreferenceKey = "KMOpenDocumentTypeKey"
private let KMGeneralShowInMenuBarKey: KMPreferenceKey = "KMGeneralShowInMenuBarKey"
private let KMOpenFileTypeKey: KMPreferenceKey = "KMOpenFileTypeKey"
private let KMShowLeftSideBarKey: KMPreferenceKey = "KMShowLeftSideBarKey"
private let KMRememberSnapshotKey: KMPreferenceKey = "KMRememberSnapshotKey"
private let KMRevertInitPDFViewSettingTypeKey: KMPreferenceKey = "KMRevertInitPDFViewSettingTypeKey"
private let KMAutoSaveNoteBackupKey: KMPreferenceKey = "KMAutoSaveNoteBackupKey"
private let KMKeepSnapshotWindowToTopKey: KMPreferenceKey = "KMKeepSnapshotWindowToTopKey"

/// display
private let KMViewPageDisplayTypeKey: KMPreferenceKey = "KMViewPageDisplayTypeKey"
private let KMViewZoomScaleTypeKey: KMPreferenceKey = "KMViewZoomScaleTypeKey"
private let KMLeftSideDisplayTypeKey: KMPreferenceKey = "KMLeftSideDisplayTypeKey"
private let KMShowOutlineListKey: KMPreferenceKey = "KMShowOutlineListKey"
private let KMLeftSideExpandTypeKey: KMPreferenceKey = "KMLeftSideExpandTypeKey"
private let KMPropertyPanelExpandTypeKey: KMPreferenceKey = "KMPropertyPanelExpandTypeKey"
private let KMDisplayBackgroundNormalColorKey: KMPreferenceKey = "KMDisplayBackgroundNormalColorKey"
private let KMDisplayBackgroundFullScreenColorKey: KMPreferenceKey = "KMDisplayBackgroundFullScreenColorKey"
private let KMHighlightFormsKey: KMPreferenceKey = "KMHighlightFormsKey"
private let KMDisplayFieldHighlightingColorKey: KMPreferenceKey = "KMDisplayFieldHighlightingColorKey"
private let KMDisplayRequiredFieldHighlightingColorKey: KMPreferenceKey = "KMDisplayRequiredFieldHighlightingColorKey"
private let KMPageIndicatorTypeKey: KMPreferenceKey = "KMPageIndicatorTypeKey"
private let KMHighlightLinksKey: KMPreferenceKey = "KMHighlightLinksKey"

// Tip: 新补充

private let KMThumbPageSizeKey: KMPreferenceKey = "KMThumbPageSizeKey"
private let KMThumbSnapshotSizeKey: KMPreferenceKey = "KMThumbSnapshotSizeKey"
private let KMDiscreteSizeKey: KMPreferenceKey = "KMDiscreteSizeKey"
private let KMOutlineFontSizeKey: KMPreferenceKey = "KMOutlineFontSizeKey"
private let KMGreekThresholdKey: KMPreferenceKey = "KMGreekThresholdKey"
private let KMAntiAliasTextKey: KMPreferenceKey = "KMAntiAliasTextKey"
private let KMReadBarColorKey: KMPreferenceKey = "KMReadBarColorKey"
private let KMInvertBarKey: KMPreferenceKey = "KMInvertBarKey"

/// markup
private let KMMarkupColorHighlightKey: KMPreferenceKey = "KMMarkupColorHighlightKey"
private let KMMarkupColorStrikthroughKey: KMPreferenceKey = "KMMarkupColorStrikthroughKey"
private let KMMarkupColorUnderlineKey: KMPreferenceKey = "KMMarkupColorUnderlineKey"
private let KMMarkupColorPenKey: KMPreferenceKey = "KMMarkupColorPenKey"
private let KMMarkupColorTextKey: KMPreferenceKey = "KMMarkupColorTextKey"
private let KMMarkupColorNoteKey: KMPreferenceKey = "KMMarkupColorNoteKey"
private let KMMarkupColorRectangleFillKey: KMPreferenceKey = "KMMarkupColorRectangleFillKey"
private let KMMarkupColorRectangleBorderKey: KMPreferenceKey = "KMMarkupColorRectangleBorderKey"
private let KMMarkupColorCircleFillKey: KMPreferenceKey = "KMMarkupColorCircleFillKey"
private let KMMarkupColorCircleBorderKey: KMPreferenceKey = "KMMarkupColorCircleBorderKey"
private let KMMarkupColorLineKey: KMPreferenceKey = "KMMarkupColorLineKey"
private let KMMarkupColorArrowKey: KMPreferenceKey = "KMMarkupColorArrowKey"
private let KMMarkupFontTextStringKey: KMPreferenceKey = "KMMarkupFontTextStringKey"
private let KMMarkupFontTextAligmentKey: KMPreferenceKey = "KMMarkupFontTextAligmentKey"
private let KMMarkupFontNoteStringKey: KMPreferenceKey = "KMMarkupFontNoteStringKey"

// Tip: 新补充

private let KMMarkupTextFontSizeKey: KMPreferenceKey = "KMMarkupTextFontSizeKey"
private let KMMarkupNoteFontSizeKey: KMPreferenceKey = "KMMarkupNoteFontSizeKey"

//private let KMFreeTextNoteLineWidthKey: KMPreferenceKey = "KMFreeTextNoteLineWidthKey"
//private let KMFreeTextNoteLineStyleKey: KMPreferenceKey = "KMFreeTextNoteLineStyleKey"
//private let KMFreeTextNoteDashPatternKey: KMPreferenceKey = "KMFreeTextNoteDashPatternKey"
//private let KMCircleNoteLineWidthKey: KMPreferenceKey = "KMCircleNoteLineWidthKey"
//private let KMCircleNoteLineStyleKey: KMPreferenceKey = "KMCircleNoteLineStyleKey"
private let KMCircleNoteDashPatternKey: KMPreferenceKey = "KMCircleNoteDashPatternKey"
//
//private let KMSquareNoteLineWidthKey: KMPreferenceKey = "KMSquareNoteLineWidthKey"
//private let KMSquareNoteLineStyleKey: KMPreferenceKey = "KMSquareNoteLineStyleKey"
//private let KMSquareNoteDashPatternKey: KMPreferenceKey = "KMSquareNoteDashPatternKey"
//private let KMLineNoteLineWidthKey: KMPreferenceKey = "KMLineNoteLineWidthKey"
//private let KMLineNoteLineStyleKey: KMPreferenceKey = "KMLineNoteLineStyleKey"
//private let KMLineNoteDashPatternKey: KMPreferenceKey = "KMLineNoteDashPatternKey"
//private let KMLineNoteStartLineStyleKey: KMPreferenceKey = "KMLineNoteStartLineStyleKey"
//private let KMLineNoteEndLineStyleKey: KMPreferenceKey = "KMLineNoteEndLineStyleKey"
//
//private let KMInkNoteLineWidthKey: KMPreferenceKey = "KMInkNoteLineWidthKey"
//private let KMInkNoteLineStyleKey: KMPreferenceKey = "KMInkNoteLineStyleKey"
//private let KMInkNoteDashPatternKey: KMPreferenceKey = "KMInkNoteDashPatternKey"

/// 偏好设置已改变
private let KMPreferenceDidChangeNotificationName = "KMPreferenceDidChangeNotificationName"

private let KMDefaultFontName = "Helvetica-Oblique"

typealias KMPreference = KMPreferenceManager

@objcMembers class KMPreferenceManager: NSObject {
    static let shared = KMPreferenceManager()
    
    private var generalGroupKeys: Array<KMPreferenceKey> = []
    private var displayGroupKeys: Array<KMPreferenceKey> = []
    private var markupGroupKeys: Array<KMPreferenceKey> = []
    
    public static let didChangeNotification = Notification.Name(KMPreferenceDidChangeNotificationName)
    
    /// general
    /// files
    public static let openLastUnclosedDocumentWhenAppStartKey   = KMOpenLastUnclosedDocumentWhenAppStartKey
    public static let openLastUnlockedDocumentWhenAppStartKey   = KMOpenLastUnlockedDocumentWhenAppStartKey
    public static let documentMaximunDisplayNumberKey           = KMDocumentMaximunDisplayNumberKey
    public static let autoSaveKey                               = KMAutoSaveKey
    public static let autoSavePerNumberMinuteKey                = KMAutoSavePerNumberMinuteKey
    public static let closeFilePromptTypeKey                    = KMCloseFilePromptTypeKey
    /// open image file
    public static let openImageFileTypeKey                      = KMOpenImageFileTypeKey
    public static let setDefaultPDFReaderKey                    = KMSetDefaultPDFReaderKey
    /// save password
    public static let savePasswordTypeKey                       = KMSavePasswordTypeKey
    public static let generalAuthorNameKey                      = KMGeneralAuthorNameKey
    
    // Tip: 新补充
    public static let openDocumentTypeKey                       = KMOpenDocumentTypeKey
    public static let showInMenuBarKey                          = KMGeneralShowInMenuBarKey
    public static let openFileTypeKey                           = KMOpenFileTypeKey
    public static let showLeftSideBarKey                        = KMShowLeftSideBarKey
    public static let rememberSnapshotKey                       = KMRememberSnapshotKey
    public static let revertInitPDFViewSettingTypeKey           = KMRevertInitPDFViewSettingTypeKey
    public static let autoSaveNoteBackupKey                     = KMAutoSaveNoteBackupKey
    public static let keepSnapshotWindowToTopKey                = KMKeepSnapshotWindowToTopKey
    
    /// display
    public static let viewPageDisplayTypeKey                    = KMViewPageDisplayTypeKey
    public static let viewZoomScaleTypeKey                      = KMViewZoomScaleTypeKey
    public static let leftSideDisplayTypeKey                    = KMLeftSideDisplayTypeKey
    public static let showOutlineListKey                        = KMShowOutlineListKey
    public static let leftSideExpandTypeKey                     = KMLeftSideExpandTypeKey
    public static let propertyPanelExpandTypeKey                = KMPropertyPanelExpandTypeKey
    public static let displayBackgroundNormalColorKey           = KMDisplayBackgroundNormalColorKey
    public static let displayBackgroundFullScreenColorKey       = KMDisplayBackgroundFullScreenColorKey
    public static let highlightFormsKey                         = KMHighlightFormsKey
    public static let displayFieldHighlightingColorKey          = KMDisplayFieldHighlightingColorKey
    public static let displayRequiredFieldHighlightingColorKey  = KMDisplayRequiredFieldHighlightingColorKey
    public static let pageIndicatorTypeKey                      = KMPageIndicatorTypeKey
    public static let highlightLinksKey                         = KMHighlightLinksKey
    
    // Tip: 新补充
    public static let thumbPageSizeKey                          = KMThumbPageSizeKey
    public static let thumbSnapshotSizeKey                      = KMThumbSnapshotSizeKey
    public static let discreteSizeKey                           = KMDiscreteSizeKey
    public static let outlineFontSizeKey                        = KMOutlineFontSizeKey
    public static let greekThresholdKey                         = KMGreekThresholdKey
    public static let antiAliasTextKey                          = KMAntiAliasTextKey
    public static let readBarColorKey                           = KMReadBarColorKey
    public static let invertBarKey                              = KMInvertBarKey
    
    /// markup
    public static let markupColorHighlightKey                   = KMMarkupColorHighlightKey
    public static let markupColorStrikthroughKey                = KMMarkupColorStrikthroughKey
    public static let markupColorUnderlineKey                   = KMMarkupColorUnderlineKey
    public static let markupColorPenKey                         = KMMarkupColorPenKey
    public static let markupColorTextKey                        = KMMarkupColorTextKey
    public static let markupColorNoteKey                        = KMMarkupColorNoteKey
    public static let markupColorRectangleFillKey               = KMMarkupColorRectangleFillKey
    public static let markupColorRectangleBorderKey             = KMMarkupColorRectangleBorderKey
    public static let markupColorCircleFillKey                  = KMMarkupColorCircleFillKey
    public static let markupColorCircleBorderKey                = KMMarkupColorCircleBorderKey
    public static let markupColorLineKey                        = KMMarkupColorLineKey
    public static let markupColorArrowKey                       = KMMarkupColorArrowKey
    public static let markupFontTextStringKey                   = KMMarkupFontTextStringKey
    public static let markupFontTextAligmentKey                 = KMMarkupFontTextAligmentKey
    public static let markupFontNoteStringKey                   = KMMarkupFontNoteStringKey
    
    // Tip: - 新补充
    
    public static let markupTextFontSizeKey                     = KMMarkupTextFontSizeKey
    public static let markupNoteFontSizeKey                     = KMMarkupNoteFontSizeKey
    
    public static let freeTextNoteLineWidthKey                  = KMFreeTextNoteLineWidthKey
    public static let freeTextNoteLineStyleKey                  = KMFreeTextNoteLineStyleKey
    public static let freeTextNoteDashPatternKey                = KMFreeTextNoteDashPatternKey
    public static let circleNoteLineWidthKey                    = KMCircleNoteLineWidthKey
    public static let circleNoteLineStyleKey                    = KMCircleNoteLineStyleKey
    public static let circleNoteDashPatternKey                  = KMCircleNoteDashPatternKey
    
    public static let squareNoteLineWidthKey                    = KMSquareNoteLineWidthKey
    public static let squareNoteLineStyleKey                    = KMSquareNoteLineStyleKey
    public static let squareNoteDashPatternKey                  = KMSquareNoteDashPatternKey
    public static let lineNoteLineWidthKey                      = KMLineNoteLineWidthKey
    public static let lineNoteLineStyleKey                      = KMLineNoteLineStyleKey
    public static let lineNoteDashPatternKey                    = KMLineNoteDashPatternKey
    public static let lineNoteStartLineStyleKey                 = KMLineNoteStartLineStyleKey
    public static let lineNoteEndLineStyleKey                   = KMLineNoteEndLineStyleKey
    public static let inkNoteLineWidthKey                       = KMInkNoteLineWidthKey
    public static let inkNoteLineStyleKey                       = KMInkNoteLineStyleKey
    public static let inkNoteDashPatternKey                     = KMInkNoteDashPatternKey
    
    override init() {
        super.init()
        
        /// 初始化
        generalGroupKeys = self.getDefaultKeys(for: .general)
        displayGroupKeys = self.getDefaultKeys(for: .display)
        markupGroupKeys = self.getDefaultKeys(for: .markup)
        let info = UserDefaults.standard.value(forKey: KMPreferenceInfoKey)
        if (info == nil) {
            let info = self.getDefaultInfo()
            UserDefaults.standard.set(info, forKey: KMPreferenceInfoKey)
            UserDefaults.standard.synchronize()
        }
    }
    
    // MARK: 保存/获取数据
    public func setData(data: Any,forKey key: KMPreferenceKey) -> Bool {
        return self.setData(data: data, forKey: key, in: findGroup(forKey: key))
    }
    
    public func setData(data: Any,forKey key: KMPreferenceKey, in group: KMPreferenceGroup) -> Bool {
        var info: [String : Any]?
        if (UserDefaults.standard.value(forKey: KMPreferenceInfoKey) == nil) {
            info = self.getDefaultInfo()
        } else {
            info = (UserDefaults.standard.value(forKey: KMPreferenceInfoKey) as? [String : Any]) ?? [:]
        }
        
        var groupInfo: [String : Any] = info?[group.rawValue] as? [String : Any] ?? [:]
        groupInfo.updateValue(data, forKey: key)
        
        info?.updateValue(groupInfo, forKey: group.rawValue)
        UserDefaults.standard.set(info, forKey: KMPreferenceInfoKey)
        UserDefaults.standard.synchronize()
        
        if self._grouping {
            self._grouping_objects.insert(group)
            self._grouping_infos[key] = data
        } else {
            self.postNotification(object: [group], userInfo: [key : data])
        }
        
        return true
    }
    
    public func getData(forKey key: KMPreferenceKey) -> Any {
        var info: [String : Any]?
        if (UserDefaults.standard.value(forKey: KMPreferenceInfoKey) == nil) {
            info = self.getDefaultInfo()
        } else {
            info = (UserDefaults.standard.value(forKey: KMPreferenceInfoKey) as? [String : Any]) ?? [:]
        }
        
        let groupInfo: [String : Any] = info?[findGroup(forKey: key).rawValue] as? [String : Any] ?? [:]
        return groupInfo[key] as Any
    }
    
    public func resetData(_ group: KMPreferenceGroup) {
        var info: [String : Any]?
        if (UserDefaults.standard.value(forKey: KMPreferenceInfoKey) == nil) {
            info = self.getDefaultInfo()
        } else {
            info = (UserDefaults.standard.value(forKey: KMPreferenceInfoKey) as? [String : Any]) ?? [:]
        }
        
        let groupInfo = self.getDefaultInfo()[group.rawValue]
        info?.updateValue(groupInfo as Any, forKey: group.rawValue)
        UserDefaults.standard.set(info, forKey: KMPreferenceInfoKey)
        UserDefaults.standard.synchronize()
        
        self.resetDataToPDFView()
        self._resetDataToUserDefault(group: group)
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
            let info: [String : Any]? = UserDefaults.standard.value(forKey: KMPreferenceInfoKey) as? [String : Any]
            var groupInfo: [KMPreferenceKey : Any]?
            if (info != nil) {
                groupInfo = info![group.rawValue] as? [KMPreferenceKey : Any]
            }
            self.postNotification(object: [group], userInfo: groupInfo != nil ? groupInfo : [:])
        }
    }
    
    public func resetAllData() {
        let info = self.getDefaultInfo()
        UserDefaults.standard.set(info, forKey: KMPreferenceInfoKey)
        UserDefaults.standard.synchronize()
        
        self.resetDataToPDFView()
        self._resetDataToUserDefault(group: .general)
        self._resetDataToUserDefault(group: .display)
        self._resetDataToUserDefault(group: .markup)
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
            var groppInfos: [KMPreferenceKey : Any] = [:]
            for groupInfo in info.values {
                for (key, value) in groupInfo {
                    groppInfos.updateValue(value, forKey: key)
                }
            }
            
            self.postNotification(object: [KMPreferenceGroup.general, KMPreferenceGroup.display, KMPreferenceGroup.markup, KMPreferenceGroup.infomation, KMPreferenceGroup.other], userInfo: groppInfos)
        }
    }
    
    // MARK: 注册 key
    public func register(_ key: KMPreferenceKey, to group: KMPreferenceGroup) -> Bool {
        if (group == .general) {
            if (self.generalGroupKeys.contains(key)) {
                return false
            }
            self.generalGroupKeys.append(key)
        }
        return true
    }
    
    // MARK: -
    // MARK: 发布通知
    
    private func postNotification(object: [KMPreferenceGroup]?, userInfo: [KMPreferenceKey : Any]?) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
            NotificationCenter.default.post(name: KMPreferenceManager.didChangeNotification, object: object, userInfo: userInfo)
        }
    }
    
    // MARK: 获取信息
    private func findGroup(forKey key: KMPreferenceKey) -> KMPreferenceGroup {
        if (self.generalGroupKeys.contains(key)) {
            return .general
        } else if (self.displayGroupKeys.contains(key)) {
            return .display
        } else if (self.markupGroupKeys.contains(key)) {
            return .markup
        }
        return .other
    }
    
    private func getDefaultKeys(for group: KMPreferenceGroup) -> Array<KMPreferenceKey> {
        if (group == .general) {
            return [KMOpenLastUnclosedDocumentWhenAppStartKey,
                    KMOpenLastUnlockedDocumentWhenAppStartKey,
                    KMDocumentMaximunDisplayNumberKey,
                    KMAutoSaveKey,
                    KMAutoSavePerNumberMinuteKey,
                    KMCloseFilePromptTypeKey,
                    KMOpenImageFileTypeKey,
                    KMSetDefaultPDFReaderKey,
                    KMSavePasswordTypeKey,
                    KMGeneralAuthorNameKey,
                    // Tip: 新补充
                    KMOpenDocumentTypeKey, KMGeneralShowInMenuBarKey, KMOpenFileTypeKey, KMShowLeftSideBarKey,
                    KMRememberSnapshotKey, KMRevertInitPDFViewSettingTypeKey, KMAutoSaveNoteBackupKey, KMKeepSnapshotWindowToTopKey]
        } else if (group == .display) {
            return [KMViewPageDisplayTypeKey,
                    KMViewZoomScaleTypeKey,
                    KMLeftSideDisplayTypeKey,
                    KMShowOutlineListKey,
                    KMLeftSideExpandTypeKey,
                    KMPropertyPanelExpandTypeKey,
                    KMDisplayBackgroundNormalColorKey,
                    KMDisplayBackgroundFullScreenColorKey,
                    KMHighlightFormsKey,
                    KMDisplayFieldHighlightingColorKey,
                    KMDisplayRequiredFieldHighlightingColorKey,
                    KMPageIndicatorTypeKey,
                    KMHighlightLinksKey,
                    // Tip: 新补充
                    KMThumbPageSizeKey, KMThumbSnapshotSizeKey, KMDiscreteSizeKey, KMOutlineFontSizeKey,
                    KMGreekThresholdKey, KMAntiAliasTextKey, KMReadBarColorKey, KMInvertBarKey]
        } else if (group == .markup) {
            return [KMMarkupColorHighlightKey,
                    KMMarkupColorUnderlineKey,
                    KMMarkupColorStrikthroughKey,
                    KMMarkupColorPenKey,
                    KMMarkupColorLineKey,
                    KMMarkupColorArrowKey,
                    KMMarkupColorNoteKey,
                    KMMarkupColorTextKey,
                    KMMarkupColorCircleFillKey,
                    KMMarkupColorCircleBorderKey,
                    KMMarkupColorRectangleFillKey,
                    KMMarkupColorRectangleBorderKey,
                    KMMarkupFontTextStringKey,
                    KMMarkupFontTextAligmentKey,
                    KMMarkupFontNoteStringKey,
                    // 新补充
                    KMMarkupTextFontSizeKey, KMMarkupNoteFontSizeKey,
                    KMFreeTextNoteLineWidthKey, KMFreeTextNoteLineStyleKey, KMFreeTextNoteDashPatternKey,
                    KMCircleNoteLineWidthKey, KMCircleNoteLineStyleKey, KMCircleNoteDashPatternKey,
                    KMSquareNoteLineWidthKey, KMSquareNoteLineStyleKey, KMSquareNoteDashPatternKey,
                    KMLineNoteLineWidthKey, KMLineNoteLineStyleKey, KMLineNoteDashPatternKey, KMLineNoteStartLineStyleKey, KMLineNoteEndLineStyleKey,
                    KMInkNoteLineWidthKey, KMInkNoteLineStyleKey, KMInkNoteDashPatternKey]
        }
        return []
    }
    
    private func getDefaultInfo() -> Dictionary<String, Dictionary<String, Any>> {
        return [KMPreferenceGroup.general.rawValue : [KMOpenLastUnclosedDocumentWhenAppStartKey : false,
                                                      KMOpenLastUnlockedDocumentWhenAppStartKey : true,
                                                              KMDocumentMaximunDisplayNumberKey : 10,
                                                                                  KMAutoSaveKey : true,
                                                                   KMAutoSavePerNumberMinuteKey : 5,
                                                                       KMCloseFilePromptTypeKey : 0,
                                                                         KMOpenImageFileTypeKey : 0,
                                                                       KMSetDefaultPDFReaderKey : true,
                                                                          KMSavePasswordTypeKey : 2,
                                                                         KMGeneralAuthorNameKey : NSFullUserName(),
                                                      // Tip: 新补充
                                                                          KMOpenDocumentTypeKey : self.openDocumentTypeDefaultValue.rawValue,
                                                                      KMGeneralShowInMenuBarKey : self.showInMenuBarDefaultValue,
                                                                              KMOpenFileTypeKey : self.openFileTypeDefaultValue.rawValue,
                                                                           KMShowLeftSideBarKey : self.showLeftSideBarDefaultValue,
                                                                          KMRememberSnapshotKey : self.rememberSnapshotDefaultValue,
                                                              KMRevertInitPDFViewSettingTypeKey : self.revertInitPDFViewSettingTypeDefaultValue.rawValue,
                                                                        KMAutoSaveNoteBackupKey : self.autoSaveNoteBackupDefaultValue,
                                                                   KMKeepSnapshotWindowToTopKey : self.keepSnapshotWindowToTopDefaultValue()],
                KMPreferenceGroup.display.rawValue : [KMViewPageDisplayTypeKey : 1,
                                                        KMViewZoomScaleTypeKey : 0,
                                                      KMLeftSideDisplayTypeKey : 0,
                                                           KMShowOutlineListKey: true,
                                                       KMLeftSideExpandTypeKey : 0,
                                                  KMPropertyPanelExpandTypeKey : 0,
                                             KMDisplayBackgroundNormalColorKey : self.displayBackgroundNormalColorValues,
                                         KMDisplayBackgroundFullScreenColorKey : self.displayBackgroundFullScreenColorValues,
                                                           KMHighlightFormsKey : true,
                                            KMDisplayFieldHighlightingColorKey : self.displayFieldHighlightingColorValues,
                                    KMDisplayRequiredFieldHighlightingColorKey : self.displayRequiredFieldHighlightingColorValues,
                                                        KMPageIndicatorTypeKey : 0,
                                                           KMHighlightLinksKey : true,
                                                      // Tip: 新补充
                                                            KMThumbPageSizeKey : self.thumbPageSizeDefaultValue(),
                                                        KMThumbSnapshotSizeKey : self.thumbSnapshotSizeDefaultValue(),
                                                             KMDiscreteSizeKey : self.discreteSizeDefaultValue(),
                                                          KMOutlineFontSizeKey : self.outlineFontSizeDefaultValue(),
                                                           KMGreekThresholdKey : self.greekThresholdDefaultValue(),
                                                            KMAntiAliasTextKey : self.antiAliasTextDefaultValue(),
                                                             KMReadBarColorKey : self.readBarColorValues,
                                                                KMInvertBarKey : self.invertBarDefaultValue()],
                KMPreferenceGroup.markup.rawValue : [KMMarkupColorHighlightKey : self.markupHighlightColorValues,
                                                  KMMarkupColorStrikthroughKey : self.markupStrikthroughColorValues,
                                                     KMMarkupColorUnderlineKey : self.markupUnderlineColorValues,
                                                           KMMarkupColorPenKey : self.markupPenColorValues,
                                                          KMMarkupColorTextKey : self.markupTextColorValues,
                                                          KMMarkupColorNoteKey : self.markupNoteColorValues,
                                                 KMMarkupColorRectangleFillKey : self.markupRectangleFillColorValues,
                                               KMMarkupColorRectangleBorderKey : self.markupRectangleBorderColorValues,
                                                    KMMarkupColorCircleFillKey : self.markupCircleFillColorValues,
                                                  KMMarkupColorCircleBorderKey : self.markupCircleBorderColorValues,
                                                          KMMarkupColorLineKey : self.markupLineColorValues,
                                                         KMMarkupColorArrowKey : self.markupArrowColorValues,
                                                     KMMarkupFontTextStringKey : KMDefaultFontName,
                                                   KMMarkupFontTextAligmentKey : 0,
                                                     KMMarkupFontNoteStringKey : KMDefaultFontName,
                                                     // 新补充
                                                       KMMarkupTextFontSizeKey : self.textFontSizeDefaultValue(),
                                                       KMMarkupNoteFontSizeKey : self.noteFontSizeDefaultValue(),
                                                    KMFreeTextNoteLineWidthKey : self.freeTextNoteLineWidthDefaultValue(),
                                                    KMFreeTextNoteLineStyleKey : self.freeTextNoteLineStyleDefaultValue(),
                                                  KMFreeTextNoteDashPatternKey : self.freeTextNoteDashPatternDefaultValue(),
                                                      KMCircleNoteLineWidthKey : self.circleNoteLineWidthDefaultValue(),
                                                      KMCircleNoteLineStyleKey : self.circleNoteLineStyleDefaultValue(),
                                                    KMCircleNoteDashPatternKey : self.circleNoteDashPatternDefaultValue(),
                                                      KMSquareNoteLineWidthKey : self.squareNoteLineWidthDefaultValue(),
                                                      KMSquareNoteLineStyleKey : self.squareNoteLineStyleDefaultValue(),
                                                    KMSquareNoteDashPatternKey : self.squareNoteDashPatternDefaultValue(),
                                                        KMLineNoteLineWidthKey : self.lineNoteLineWidthDefaultValue(),
                                                        KMLineNoteLineStyleKey : self.lineNoteLineStyleDefaultValue(),
                                                      KMLineNoteDashPatternKey : self.lineNoteDashPatternDefaultValue(),
                                                   KMLineNoteStartLineStyleKey : self.lineNoteStartLineStyleDefaultValue(),
                                                     KMLineNoteEndLineStyleKey : self.lineNoteEndLineStyleDefaultValue(),
                                                         KMInkNoteLineWidthKey : self.inkNoteLineWidthDefaultValue(),
                                                         KMInkNoteLineStyleKey : self.inkNoteLineStyleDefaultValue(),
                                                       KMInkNoteDashPatternKey : self.inkNoteDashPatternDefaultValue()],
                KMPreferenceGroup.infomation.rawValue : [:],
                KMPreferenceGroup.other.rawValue : [:]]
    }
}

// MARK: - 默认值

extension KMPreferenceManager {
    // General
    
    var openDocumentTypeDefaultValue: KMPreferenceOpenDocumentType {
        get {
            return .inSameWindow
        }
    }
    var showInMenuBarDefaultValue: Bool {
        get {
            return false
        }
    }
    var openFileTypeDefaultValue: KMPreferenceOpenFileType {
        get {
            return .default
        }
    }
    var showLeftSideBarDefaultValue: Bool {
        get {
            return true
        }
    }
    var rememberSnapshotDefaultValue: Bool {
        get {
            return true
        }
    }
    var revertInitPDFViewSettingTypeDefaultValue: KMPreferenceRevertInitSettingType {
        get {
            return .normal
        }
    }
    var autoSaveNoteBackupDefaultValue: Bool {
        get {
            return false
        }
    }
    
    // 内联函数
    @inline (__always) func keepSnapshotWindowToTopDefaultValue() -> Bool {
        return true
    }
    
    // Display
    
    @inline (__always) func thumbPageSizeDefaultValue() -> Float {
        return 128
    }
    
    @inline (__always) func thumbSnapshotSizeDefaultValue() -> Float {
        return 128
    }
    
    @inline (__always) func discreteSizeDefaultValue() -> Bool {
        return false
    }
    
    @inline (__always) func outlineFontSizeDefaultValue() -> Float {
        return 12
    }
    
    @inline (__always) func greekThresholdDefaultValue() -> Float {
        return 3
    }
    
    @inline (__always) func antiAliasTextDefaultValue() -> Bool {
        return true
    }
    
//    @inline (__always) func readBarColorDefaultValue() -> Float {
//        return 0
//    }
    
    @inline (__always) func invertBarDefaultValue() -> Bool {
        return false
    }
    
    @inline (__always) func textFontSizeDefaultValue() -> Float {
        return 11.0
    }
    
    @inline (__always) func noteFontSizeDefaultValue() -> Float {
        return 12.0
    }
    
    @inline (__always) func freeTextNoteLineWidthDefaultValue() -> Float {
        return 0.0
    }
    
    @inline (__always) func freeTextNoteLineStyleDefaultValue() -> Int {
        return 0
    }
    
    @inline (__always) func freeTextNoteDashPatternDefaultValue() -> [CGFloat] {
        return []
    }
    
    @inline (__always) func circleNoteLineWidthDefaultValue() -> Float {
        return 2.0
    }
    
    @inline (__always) func circleNoteLineStyleDefaultValue() -> Int {
        return 0
    }
    
    @inline (__always) func circleNoteDashPatternDefaultValue() -> [CGFloat] {
        return []
    }
    
    @inline (__always) func squareNoteLineWidthDefaultValue() -> Float {
        return 2.0
    }
    
    @inline (__always) func squareNoteLineStyleDefaultValue() -> Int {
        return 0
    }
    
    @inline (__always) func squareNoteDashPatternDefaultValue() -> [CGFloat] {
        return []
    }
    
    @inline (__always) func lineNoteLineWidthDefaultValue() -> Float {
        return 2.0
    }
    
    @inline (__always) func lineNoteLineStyleDefaultValue() -> Int {
        return 0
    }
    
    @inline (__always) func lineNoteDashPatternDefaultValue() -> [CGFloat] {
        return []
    }
    
    @inline (__always) func lineNoteStartLineStyleDefaultValue() -> Int {
        return 0
    }
    
    @inline (__always) func lineNoteEndLineStyleDefaultValue() -> Int {
        return 0
    }
    
    @inline (__always) func inkNoteLineWidthDefaultValue() -> Float {
        return 2.0
    }
    
    @inline (__always) func inkNoteLineStyleDefaultValue() -> Int {
        return 0
    }
    
    @inline (__always) func inkNoteDashPatternDefaultValue() -> [CGFloat] {
        return []
    }
}

//protocol KMPreferenceManagerColorPart: NSObjectProtocol {
//    var markupHighlightColorValues: [Double] { get }
//}
// MARK: - 扩展 颜色
extension KMPreferenceManager {
    fileprivate var displayBackgroundNormalColorValues: [Double] {
        get {
            return [206/255.0, 208/255.0, 212/255.0, 1.0]
        }
    }
    fileprivate var displayBackgroundFullScreenColorValues: [Double] {
        get {
            return [54/255.0, 56/255.0, 59/255.0, 1.0]
        }
    }
    fileprivate var displayFieldHighlightingColorValues: [Double] {
        get {
            return [189/255.0, 223/255.0, 253/255.0, 1.0]
        }
    }
    fileprivate var displayRequiredFieldHighlightingColorValues: [Double] {
        get {
            return [23/255.0, 112/255.0, 224/255.0, 1.0]
        }
    }
    fileprivate var markupHighlightColorValues: [Double] {
        get {
            return [255/255.0, 199/255.0, 0.0, 1.0]
        }
    }
    fileprivate var markupStrikthroughColorValues: [Double] {
        get {
            return [252/255.0, 31/255.0, 31/255.0, 1.0]
        }
    }
    fileprivate var markupUnderlineColorValues: [Double] {
        get {
            return [30/255.0, 137/255.0, 86/255.0, 1.0]
        }
    }
    fileprivate var markupPenColorValues: [Double] {
        get {
            self.markupStrikthroughColorValues
        }
    }
    fileprivate var markupTextColorValues: [Double] {
        get {
            return [37/255.0, 38/255.0, 41/255.0, 1.0]
        }
    }
    fileprivate var markupNoteColorValues: [Double] {
        get {
            return [255/255.0, 213/255.0, 115/255.0, 1.0]
        }
    }
    fileprivate var markupRectangleFillColorValues: [Double] {
        get {
            return [0/255.0, 0/255.0, 0/255.0, 0.0]
        }
    }
    fileprivate var markupRectangleBorderColorValues: [Double] {
        get {
            self.markupStrikthroughColorValues
        }
    }
    fileprivate var markupCircleFillColorValues: [Double] {
        get {
            self.markupRectangleFillColorValues
        }
    }
    fileprivate var markupCircleBorderColorValues: [Double] {
        get {
            return self.markupRectangleBorderColorValues
        }
    }
    fileprivate var markupLineColorValues: [Double] {
        get {
            self.markupStrikthroughColorValues
        }
    }
    fileprivate var markupArrowColorValues: [Double] {
        get {
            self.markupStrikthroughColorValues
        }
    }
    
    fileprivate var readBarColorValues: [Double] {
        get {
            return [23/255.0, 112/255.0, 224/255.0, 1.0]
        }
    }
    
    private func setColor(_ color: NSColor, forKey key: KMPreferenceKey) -> Bool {
        var red: CGFloat = 0.0
        var green: CGFloat = 0.0
        var blue: CGFloat = 0.0
        var alpha: CGFloat = 0.0
        color.usingColorSpaceName(.calibratedRGB)?.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
        return KMPreferenceManager.shared.setData(data: [red, green, blue, alpha], forKey: key)
    }
    
    private func getColor(forKey key: KMPreferenceKey) -> NSColor {
        var colors: [Double]? = self.getData(forKey: key) as? [Double]
        if (colors == nil) {
            colors = self.getDefaultColors(forKey: key)
        }
        return NSColor(red: colors![0], green: colors![1], blue: colors![2], alpha: colors![3])
    }
    
    private func dataToColor(colors:[Double]) -> NSColor {
        NSColor(red: colors[0], green: colors[1], blue: colors[2], alpha: colors[3])
    }
}

// MARK: 扩展 General
extension KMPreferenceManager {
    var openLastUnclosedDocumentWhenAppStart: Bool {
        get {
            return KMDataManager.ud_bool(forKey: KMReopenLastOpenFilesKey)
        }
        set {
            let pKey = KMPreference.openLastUnclosedDocumentWhenAppStartKey
            self._syncDataToUserDefault(newValue, forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var openLastUnlockedDocumentWhenAppStart: Bool {
        get {
            return self.getData(forKey: KMOpenLastUnlockedDocumentWhenAppStartKey) as? Bool ?? true
        }
        set {
            let _ = self.setData(data: newValue, forKey: KMOpenLastUnlockedDocumentWhenAppStartKey)
        }
    }
    
    var documentMaximunDisplayNumber: Int {
        get {
            return self.getData(forKey: KMDocumentMaximunDisplayNumberKey) as? Int ?? 10
        }
        set {
            let _ = self.setData(data: newValue, forKey: KMDocumentMaximunDisplayNumberKey)
        }
    }
    
    var autoSave: Bool {
        get {
            return self.getData(forKey: KMAutoSaveKey) as? Bool ?? true
        }
        set {
            let _ = self.setData(data: newValue, forKey: KMAutoSaveKey)
        }
    }
    
    var autoSavePerNumberMinute: Int {
        get {
            return self.getData(forKey: KMAutoSavePerNumberMinuteKey) as? Int ?? 5
        }
        set {
            let _ = self.setData(data: newValue, forKey: KMAutoSavePerNumberMinuteKey)
        }
    }
    
    var closeFilePromptType: KMPreferenceCloseFilePromptType {
        get {
            if let type = self.getData(forKey: KMCloseFilePromptTypeKey) as? Int {
                if type == 1 { // 无提示,直接保存
                    return .noPromp
                }
            }
            
            return .promp
        }
        set {
            if (newValue == .promp || newValue == .noPromp) {
                let _ = self.setData(data: newValue.rawValue, forKey: KMCloseFilePromptTypeKey)
            }
        }
    }
    
    var openImageFileType: Int {
        get {
            return self.getData(forKey: KMOpenImageFileTypeKey) as? Int ?? 0
        }
        set {
            let _ = self.setData(data: newValue, forKey: KMOpenImageFileTypeKey)
        }
    }
    
    var author: String {
        get {
            return KMDataManager.ud_string(forKey: KMUserNameKey) ?? NSFullUserName()
        }
        set {
            self._syncDataToUserDefault(newValue, forKey: KMGeneralAuthorNameKey)
            let _ = self.setData(data: newValue, forKey: KMGeneralAuthorNameKey)
        }
    }
    
    var setDefaultPDFReader: Bool {
        get {
            return self.getData(forKey: KMSetDefaultPDFReaderKey) as? Bool ?? true
        }
        set {
            let result = KMTools.setDefaultPDFReader(newValue)
            if (result) {
                let _ = self.setData(data: newValue, forKey: KMSetDefaultPDFReaderKey)
            }
        }
    }
    
    var savePasswordType: KMPreferenceSavePasswordType {
        get {
            let type = KMDataManager.ud_integer(forKey: KMSavePasswordOptionKey)
            if type == -1 {
                return .ask
            } else if type == 1 {
                return .always
            }
            return .never
        }
        set {
            if (newValue == .always || newValue == .never || newValue == .ask) {
                var data: Int = 0
                if newValue == .always {
                    data = 1
                } else if newValue == .ask {
                    data = -1
                }
                self._syncDataToUserDefault(data, forKey: KMSavePasswordTypeKey)
                let _ = self.setData(data: newValue.rawValue, forKey: KMSavePasswordTypeKey)
            }
        }
    }
    
    var openDocumentType: KMPreferenceOpenDocumentType { // KMOpenDocumentInTab
        get {
            guard let num = KMDataManager.ud_object(forKey: KMOpenDocumentInTabKey) as? NSNumber else {
                return self.openDocumentTypeDefaultValue
            }
            if num.intValue == 1 {
                return .newWindow
            }
            return .inSameWindow
        }
        set {
            if (newValue == .inSameWindow || newValue == .newWindow) {
                let pKey = KMOpenDocumentTypeKey
                self._syncDataToUserDefault(NSNumber(value: newValue.rawValue), forKey: pKey)
                _ = self.setData(data: newValue.rawValue, forKey: pKey)
            }
        }
    }
    
    var showInMenuBar: Bool {
        get {
            return self.getData(forKey: KMGeneralShowInMenuBarKey) as? Bool ?? self.showInMenuBarDefaultValue
        }
        set {
            _ = self.setData(data: newValue, forKey: KMGeneralShowInMenuBarKey)
        }
    }
    
    var openFileType: KMPreferenceOpenFileType {
        get {
            // 数据兼容
            let value = KMDataManager.ud_integer(forKey: KMInitialWindowSizeOptionKey)
            if let type = KMPreferenceOpenFileType(rawValue: value) {
                return type
            }
            
//            guard let type = self.getData(forKey: KMOpenFileTypeKey) as? Int else {
//                return .default
//            }
//
//            if type == 1 {
//                return .maxim
//            } else if type == 2 {
//                return .fit
//            }
            return .default
        }
        set {
            if (newValue == .default || newValue == .maxim || newValue == .fit) {
                // 数据兼容
                self._syncDataToUserDefault(newValue.rawValue, forKey: KMOpenFileTypeKey)
                let _ = self.setData(data: newValue.rawValue, forKey: KMOpenFileTypeKey)
            }
        }
    }
    
    var showLeftSideBar: Bool {
        get {
            return KMDataManager.ud_bool(forKey: KMOpenContentsPaneOnlyForTOCKey)
//            return self.getData(forKey: KMShowLeftSideBarKey) as? Bool ?? self.showLeftSideBarDefaultValue
        }
        set {
            self._syncDataToUserDefault(newValue, forKey: KMShowLeftSideBarKey)
            _ = self.setData(data: newValue, forKey: KMShowLeftSideBarKey)
        }
    }
    
    var rememberSnapshot: Bool {
        get {
            return KMDataManager.ud_bool(forKey: KMRememberSnapshotsKey)
//            return self.getData(forKey: KMRememberSnapshotKey) as? Bool ?? self.rememberSnapshotDefaultValue
        }
        set {
            // 数据兼容
            self._syncDataToUserDefault(newValue, forKey: KMRememberSnapshotKey)
            _ = self.setData(data: newValue, forKey: KMRememberSnapshotKey)
        }
    }
    
    var revertInitPDFViewSettingType: KMPreferenceRevertInitSettingType {
        get {
            guard let type = self.getData(forKey: KMRevertInitPDFViewSettingTypeKey) as? Int else {
                return self.revertInitPDFViewSettingTypeDefaultValue
            }
            
            if type == 1 {
                return .fullScreen
            }
            return .normal
        }
        set {
            if (newValue == .normal || newValue == .fullScreen) {
                let _ = self.setData(data: newValue.rawValue, forKey: KMRevertInitPDFViewSettingTypeKey)
            }
        }
    }
    
    var autoSaveNoteBackup: Bool {
        get {
            return KMDataManager.ud_bool(forKey: KMAutoSaveSkimNotesKey)
//            return self.getData(forKey: KMAutoSaveNoteBackupKey) as? Bool ?? self.autoSaveNoteBackupDefaultValue
        }
        set {
            self._syncDataToUserDefault(newValue, forKey: KMAutoSaveNoteBackupKey)
            _ = self.setData(data: newValue, forKey: KMAutoSaveNoteBackupKey)
        }
    }
    
    var keepSnapshotWindowToTop: Bool {
        get {
            return KMDataManager.ud_bool(forKey: KMSnapshotsOnTopKey)
//            return self.getData(forKey: KMKeepSnapshotWindowToTopKey) as? Bool ?? self.keepSnapshotWindowToTopDefaultValue()
        }
        set {
            // 数据兼容
            self._syncDataToUserDefault(newValue, forKey: KMKeepSnapshotWindowToTopKey)
            _ = self.setData(data: newValue, forKey: KMKeepSnapshotWindowToTopKey)
        }
    }
}

// MARK: 扩展 Display
extension KMPreferenceManager {
    var viewPageDisplayType: KMPDFDisplayType {
        get {
            if let type = self.getData(forKey: KMViewPageDisplayTypeKey) as? Int {
                if (type == 0) {
                    return .singlePage
                } else if type == 1 {
                    return .singlePageContinuous
                } else if (type == 2) {
                    return .twoUp
                } else if (type == 3) {
                    return .twoUpContinuous
                } else if (type == 4) {
                    return .bookMode
                } else if (type == 5) {
                    return .bookContinuous
                }
            }
            return .singlePageContinuous
        }
        set {
            if (newValue == .singlePage || newValue == .singlePageContinuous || newValue == .twoUp ||
                newValue == .twoUpContinuous || newValue == .bookMode || newValue == .bookContinuous) {
                let _ = self.setData(data: newValue.rawValue, forKey: KMViewPageDisplayTypeKey)
            }
        }
    }
    
    var viewZoomScaleType: KMPDFZoomType {
        get {
            if let type = self.getData(forKey: KMViewZoomScaleTypeKey) as? Int {
                if (type == 0) {
                    return .width
                } else if (type == 1) {
                    return .fit
                } else if (type == 2) {
                    return .actualSize
                }
            }
            return .width
        }
        set {
            if (newValue == .width || newValue == .fit || newValue == .actualSize) {
                let _ = self.setData(data: newValue.rawValue, forKey: KMViewZoomScaleTypeKey)
            }
        }
    }
    
    var leftSideDisplayType: KMPreferenceLeftSideDisplayType {
        get {
            if let type = self.getData(forKey: KMLeftSideDisplayTypeKey) as? Int {
                if type == 0 {
                    return .closeWhenOpenFile
                } else if (type == 1) {
                    return .openAppSaveLastSelect
                } else if (type == 2) {
                    return .showOutlineIfHas
                }
            }
            return .closeWhenOpenFile
        }
        set {
            if (newValue == .closeWhenOpenFile || newValue == .openAppSaveLastSelect || newValue == .showOutlineIfHas) {
                let _ = self.setData(data: newValue.rawValue, forKey: KMLeftSideDisplayTypeKey)
            }
        }
    }
    
    var propertyPanelExpandType: KMPreferencePropertyPanelExpandType {
        get {
            if let type = self.getData(forKey: KMPropertyPanelExpandTypeKey) as? Int {
                if (type == 0) {
                    return .auto
                } else if (type == 1) {
                    return .manual
                }
            }
            return .auto
        }
        set {
            if (newValue == .auto || newValue == .manual) {
                let _ = self.setData(data: newValue.rawValue, forKey: KMPropertyPanelExpandTypeKey)
            }
        }
    }
    
    var highlightForms: Bool {
        get {
            return self.getData(forKey: KMHighlightFormsKey) as? Bool ?? true
        }
        set {
            let _ = self.setData(data: newValue, forKey: KMHighlightFormsKey)
        }
    }

    var pageIndicatorType: KMPreferencePageIndicatorDisplayType {
        get {
            if let type = self.getData(forKey: KMPageIndicatorTypeKey) as? Int {
                if (type == 2) {
                    return .never
                } else if (type == 0) {
                    return .automatic
                } else if (type == 1) {
                    return .always
                }
            }
            return .never
        }
        set {
            if (newValue == .automatic || newValue == .always || newValue == .never) {
                let _ = self.setData(data: newValue.rawValue, forKey: KMPageIndicatorTypeKey)
            }
        }
    }
    
    var highlightLinks: Bool {
        get {
            return self.getData(forKey: KMHighlightLinksKey) as? Bool ?? true
        }
        set {
            let _ = self.setData(data: newValue, forKey: KMHighlightLinksKey)
        }
    }
    
    var displayBackgroundNormalColor: NSColor {
        get {
            if let color = UserDefaults.standard.color(forKey: KMBackgroundColorKey) {
                return color
            }
            return self.getColor(forKey: KMDisplayBackgroundNormalColorKey)
        }
        set {
            self._syncDataToUserDefault(newValue, forKey: KMDisplayBackgroundNormalColorKey)
            let _ = self.setColor(newValue, forKey: KMDisplayBackgroundNormalColorKey)
        }
    }
    
    var displayBackgroundFullScreenColor: NSColor {
        get {
            if let color = UserDefaults.standard.color(forKey: KMFullScreenBackgroundColorKey) {
                return color
            }
            return self.getColor(forKey: KMDisplayBackgroundFullScreenColorKey)
        }
        set {
            self._syncDataToUserDefault(newValue, forKey: KMDisplayBackgroundFullScreenColorKey)
            let _ = self.setColor(newValue, forKey: KMDisplayBackgroundFullScreenColorKey)
        }
    }
    
    var displayFieldHighlightingColor: NSColor {
        get {
            return self.getColor(forKey: KMDisplayFieldHighlightingColorKey)
        }
        set {
            let _ = self.setColor(newValue, forKey: KMDisplayFieldHighlightingColorKey)
        }
    }
    
    var displayRequiredFieldHighlightingColor: NSColor {
        get {
            return self.getColor(forKey: KMDisplayRequiredFieldHighlightingColorKey)
        }
        set {
            let _ = self.setColor(newValue, forKey: KMDisplayRequiredFieldHighlightingColorKey)
        }
    }
    
    // Tip: 新补充
    
    var thumbPageSize: Float {
        get {
            let num = KMDataManager.ud_object(forKey: KMThumbnailSizeKey) as? NSNumber
            return num?.floatValue ?? self.thumbPageSizeDefaultValue()
//            return self.getData(forKey: KMThumbPageSizeKey) as? Float ?? self.thumbPageSizeDefaultValue()
        }
        set {
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: KMThumbPageSizeKey)
            let _ = self.setData(data: NSNumber(value: newValue), forKey: KMThumbPageSizeKey)
        }
    }
    
    var thumbSnapshotSize: Float { 
        get {
//            return self.getData(forKey: KMThumbSnapshotSizeKey) as? Float ?? self.thumbSnapshotSizeDefaultValue()
            let num = KMDataManager.ud_object(forKey: KMSnapshotThumbnailSizeKey) as? NSNumber
            return num?.floatValue ?? self.thumbSnapshotSizeDefaultValue()
        }
        set {
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: KMThumbSnapshotSizeKey)
            let _ = self.setData(data: newValue, forKey: KMThumbSnapshotSizeKey)
        }
    }
    
    var discreteSize: Bool {
        get {
            return self.getData(forKey: KMDiscreteSizeKey) as? Bool ?? self.discreteSizeDefaultValue()
        }
        set {
            let _ = self.setData(data: newValue, forKey: KMDiscreteSizeKey)
        }
    }
    
    var outlineFontSize: Float { 
        get {
            let num = KMDataManager.ud_object(forKey: KMTableFontSizeKey) as? NSNumber
            return num?.floatValue ?? self.outlineFontSizeDefaultValue()
//            return self.getData(forKey: KMOutlineFontSizeKey) as? Float ?? self.outlineFontSizeDefaultValue()
        }
        set {
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: KMOutlineFontSizeKey)
            let _ = self.setData(data: newValue, forKey: KMOutlineFontSizeKey)
        }
    }
    
    var greekThreshold: Float { 
        get {
            let num = KMDataManager.ud_object(forKey: KMGreekingThresholdKey) as? NSNumber
            return num?.floatValue ?? self.greekThresholdDefaultValue()
//            return self.getData(forKey: KMGreekThresholdKey) as? Float ?? self.greekThresholdDefaultValue()
        }
        set {
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: KMGreekThresholdKey)
            let _ = self.setData(data: newValue, forKey: KMGreekThresholdKey)
        }
    }
    
    var antiAliasText: Bool {
        get {
//            return self.getData(forKey: KMAntiAliasTextKey) as? Bool ?? self.antiAliasTextDefaultValue()
            return KMDataManager.ud_bool(forKey: KMShouldAntiAliasKey)
        }
        set {
            self._syncDataToUserDefault(newValue, forKey: KMAntiAliasTextKey)
            let _ = self.setData(data: newValue, forKey: KMAntiAliasTextKey)
        }
    }
    
    var readBarColor: NSColor {
        get {
            if let color = UserDefaults.standard.color(forKey: KMReadingBarColorKey) {
                return color
            }
            return self.getColor(forKey: KMReadBarColorKey)
        }
        set {
            self._syncDataToUserDefault(newValue, forKey: KMReadBarColorKey)
            let _ = self.setColor(newValue, forKey: KMReadBarColorKey)
        }
    }
    
    var invertBar: Bool {
        get {
            return KMDataManager.ud_bool(forKey: KMReadingBarInvertKey)
//            return self.getData(forKey: KMInvertBarKey) as? Bool ?? self.invertBarDefaultValue()
        }
        set {
            self._syncDataToUserDefault(newValue, forKey: KMInvertBarKey)
            let _ = self.setData(data: newValue, forKey: KMInvertBarKey)
        }
    }
}

// MARK: 扩展 Markup
extension KMPreferenceManager {
    var markupHighlightColor: NSColor {
        get {
            let pKey = KMPreference.markupColorHighlightKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            if let color = UserDefaults.standard.color(forKey: udKey) {
                return color
            }
            return self.getColor(forKey: KMPreference.markupColorHighlightKey)
        }
        set {
            self.syncDataToPDFView(newValue, forKey: KMPreference.markupColorHighlightKey)
            self._syncDataToUserDefault(newValue, forKey: KMPreference.markupColorHighlightKey)
            let _ = self.setColor(newValue, forKey: KMPreference.markupColorHighlightKey)
        }
    }
    var markupStrikthroughColor: NSColor {
        get {
            let pKey = KMPreference.markupColorStrikthroughKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            if let color = UserDefaults.standard.color(forKey: udKey) {
                return color
            }
            return self.getColor(forKey: KMPreference.markupColorStrikthroughKey)
        }
        set {
            self.syncDataToPDFView(newValue, forKey: KMPreference.markupColorStrikthroughKey)
            
            let _ = self.setColor(newValue, forKey: KMPreference.markupColorStrikthroughKey)
            self._syncDataToUserDefault(newValue, forKey: KMPreference.markupColorStrikthroughKey)
        }
    }
    var markupUnderlineColor: NSColor {
        get {
            let pKey = KMPreference.markupColorUnderlineKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            if let color = UserDefaults.standard.color(forKey: udKey) {
                return color
            }
            return self.getColor(forKey: KMPreference.markupColorUnderlineKey)
        }
        set {
            self.syncDataToPDFView(newValue, forKey: KMPreference.markupColorUnderlineKey)
            self._syncDataToUserDefault(newValue, forKey: KMPreference.markupColorUnderlineKey)
            let _ = self.setColor(newValue, forKey: KMPreference.markupColorUnderlineKey)
        }
    }
    var markupPenColor: NSColor {
        get {
            let pKey = KMPreference.markupColorPenKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            if let color = UserDefaults.standard.color(forKey: udKey) {
                return color
            }
            return self.getColor(forKey: KMPreference.markupColorPenKey)
        }
        set {
            self.syncDataToPDFView(newValue, forKey: KMPreference.markupColorPenKey)
            self._syncDataToUserDefault(newValue, forKey: KMPreference.markupColorPenKey)
            let _ = self.setColor(newValue, forKey: KMPreference.markupColorPenKey)
        }
    }
    var markupTextColor: NSColor {
        get {
            let pKey = KMPreference.markupColorTextKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            if let color = UserDefaults.standard.color(forKey: udKey) {
                return color
            }
            return self.getColor(forKey: KMPreference.markupColorTextKey)
        }
        set {
            self.syncDataToPDFView(newValue, forKey: KMPreference.markupColorTextKey)
            self._syncDataToUserDefault(newValue, forKey: KMPreference.markupColorTextKey)
            let _ = self.setColor(newValue, forKey: KMPreference.markupColorTextKey)
        }
    }
    var markupNoteColor: NSColor {
        get {
            let pKey = KMPreference.markupColorNoteKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            if let color = UserDefaults.standard.color(forKey: udKey) {
                return color
            }
            return self.getColor(forKey: KMPreference.markupColorNoteKey)
        }
        set {
            self.syncDataToPDFView(newValue, forKey: KMPreference.markupColorNoteKey)
            self._syncDataToUserDefault(newValue, forKey: KMPreference.markupColorNoteKey)
            let _ = self.setColor(newValue, forKey: KMPreference.markupColorNoteKey)
        }
    }
    var markupRectangleFillColor: NSColor {
        get {
            let pKey = KMPreference.markupColorRectangleFillKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            if let color = UserDefaults.standard.color(forKey: udKey) {
                return color
            }
            return self.getColor(forKey: KMPreference.markupColorRectangleFillKey)
        }
        set {
            self.syncDataToPDFView(newValue, forKey: KMPreference.markupColorRectangleFillKey)
            self._syncDataToUserDefault(newValue, forKey: KMPreference.markupColorRectangleFillKey)
            let _ = self.setColor(newValue, forKey: KMPreference.markupColorRectangleFillKey)
        }
    }
    var markupRectangleBorderColor: NSColor {
        get {
            let pKey = KMPreference.markupColorRectangleBorderKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            if let color = UserDefaults.standard.color(forKey: udKey) {
                return color
            }
            return self.getColor(forKey: KMPreference.markupColorRectangleBorderKey)
        }
        set {
            self.syncDataToPDFView(newValue, forKey: KMPreference.markupColorRectangleBorderKey)
            self._syncDataToUserDefault(newValue, forKey: KMPreference.markupColorRectangleBorderKey)
            let _ = self.setColor(newValue, forKey: KMPreference.markupColorRectangleBorderKey)
        }
    }
    var markupCircleFillColor: NSColor {
        get {
            let pKey = KMPreference.markupColorCircleFillKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            if let color = UserDefaults.standard.color(forKey: udKey) {
                return color
            }
            return self.getColor(forKey: KMPreference.markupColorCircleFillKey)
        }
        set {
            self.syncDataToPDFView(newValue, forKey: KMPreference.markupColorCircleFillKey)
            self._syncDataToUserDefault(newValue, forKey: KMPreference.markupColorCircleFillKey)
            let _ = self.setColor(newValue, forKey: KMPreference.markupColorCircleFillKey)
        }
    }
    var markupCircleBorderColor: NSColor {
        get {
            let pKey = KMPreference.markupColorCircleBorderKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            if let color = UserDefaults.standard.color(forKey: udKey) {
                return color
            }
            return self.getColor(forKey: KMPreference.markupColorCircleBorderKey)
        }
        set {
            self.syncDataToPDFView(newValue, forKey: KMPreference.markupColorCircleBorderKey)
            self._syncDataToUserDefault(newValue, forKey: KMPreference.markupColorCircleBorderKey)
            let _ = self.setColor(newValue, forKey: KMPreference.markupColorCircleBorderKey)
        }
    }
    var markupLineColor: NSColor {
        get {
            let pKey = KMPreference.markupColorLineKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            if let color = UserDefaults.standard.color(forKey: udKey) {
                return color
            }
            return self.getColor(forKey: KMPreference.markupColorLineKey)
        }
        set {
            self.syncDataToPDFView(newValue, forKey: KMPreference.markupColorLineKey)
            self._syncDataToUserDefault(newValue, forKey: KMPreference.markupColorLineKey)
            let _ = self.setColor(newValue, forKey: KMPreference.markupColorLineKey)
        }
    }
    var markupArrowColor: NSColor {
        get {
            let pKey = KMPreference.markupColorArrowKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            if let color = UserDefaults.standard.color(forKey: udKey) {
                return color
            }
            return self.getColor(forKey: KMPreference.markupColorArrowKey)
        }
        set {
            self.syncDataToPDFView(newValue, forKey: KMPreference.markupColorArrowKey)
            self._syncDataToUserDefault(newValue, forKey: KMPreference.markupColorArrowKey)
            let _ = self.setColor(newValue, forKey: KMPreference.markupColorArrowKey)
        }
    }
    
    var markupTextFontSize: Float {
        get {
            let pKey = KMPreference.markupTextFontSizeKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.floatValue ?? self.textFontSizeDefaultValue()
//            return self.getData(forKey: pKey) as? Float ?? self.textFontSizeDefaultValue()
        }
        set {
            let pKey = KMPreference.markupTextFontSizeKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var markupNoteFontSize: Float {
        get {
            let pKey = KMPreference.markupNoteFontSizeKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.floatValue ?? self.noteFontSizeDefaultValue()
//            return self.getData(forKey: KMPreference.markupNoteFontSizeKey) as? Float ?? self.noteFontSizeDefaultValue()
        }
        set {
            let pKey = KMPreference.markupNoteFontSizeKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var freeTextNoteLineWidth: Float {
        get {
            let pKey = KMPreference.freeTextNoteLineWidthKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.floatValue ?? self.freeTextNoteLineWidthDefaultValue()
//            return self.getData(forKey: KMPreference.freeTextNoteLineWidthKey) as? Float ?? self.freeTextNoteLineWidthDefaultValue()
        }
        set {
            let pKey = KMPreference.freeTextNoteLineWidthKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var freeTextNoteLineStyle: Int {
        get {
            let pKey = KMPreference.freeTextNoteLineStyleKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.intValue ?? self.freeTextNoteLineStyleDefaultValue()
//            return self.getData(forKey: KMPreference.freeTextNoteLineStyleKey) as? Int ?? self.freeTextNoteLineStyleDefaultValue()
        }
        set {
            let pKey = KMPreference.freeTextNoteLineStyleKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var freeTextNoteDashPattern: [CGFloat] {
        get {
            let pKey = KMPreference.freeTextNoteDashPatternKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            return KMDataManager.ud_array(forKey: udKey) as? [CGFloat] ?? self.freeTextNoteDashPatternDefaultValue()
//            return self.getData(forKey: KMPreference.freeTextNoteDashPatternKey) as? [CGFloat] ?? self.freeTextNoteDashPatternDefaultValue()
        }
        set {
            let pKey = KMPreference.freeTextNoteDashPatternKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(newValue, forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var circleNoteLineWidth: Float {
        get {
            let pKey = KMPreference.circleNoteLineWidthKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.floatValue ?? self.circleNoteLineWidthDefaultValue()
//            return self.getData(forKey: KMPreference.circleNoteLineWidthKey) as? Float ?? self.circleNoteLineWidthDefaultValue()
        }
        set {
            let pKey = KMPreference.circleNoteLineWidthKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var circleNoteLineStyle: Int {
        get {
            let pKey = KMPreference.circleNoteLineStyleKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.intValue ?? self.circleNoteLineStyleDefaultValue()
//            return self.getData(forKey: KMPreference.circleNoteLineStyleKey) as? Int ?? self.circleNoteLineStyleDefaultValue()
        }
        set {
            let pKey = KMPreference.circleNoteLineStyleKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var circleNoteDashPattern: [CGFloat] {
        get {
            let pKey = KMPreference.circleNoteDashPatternKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            return KMDataManager.ud_array(forKey: udKey) as? [CGFloat] ?? self.circleNoteDashPatternDefaultValue()
//            return self.getData(forKey: KMPreference.circleNoteDashPatternKey) as? [CGFloat] ?? self.circleNoteDashPatternDefaultValue()
        }
        set {
            let pKey = KMPreference.circleNoteDashPatternKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(newValue, forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var squareNoteLineWidth: Float {
        get {
            let pKey = KMPreference.squareNoteLineWidthKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.floatValue ?? self.squareNoteLineWidthDefaultValue()
//            return self.getData(forKey: KMPreference.squareNoteLineWidthKey) as? Float ?? self.squareNoteLineWidthDefaultValue()
        }
        set {
            let pKey = KMPreference.squareNoteLineWidthKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var squareNoteLineStyle: Int {
        get {
            let pKey = KMPreference.squareNoteLineStyleKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.intValue ?? self.squareNoteLineStyleDefaultValue()
//            return self.getData(forKey: KMPreference.squareNoteLineStyleKey) as? Int ?? self.squareNoteLineStyleDefaultValue()
        }
        set {
            let pKey = KMPreference.squareNoteLineStyleKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var squareNoteDashPattern: [CGFloat] {
        get {
            let pKey = KMPreference.squareNoteDashPatternKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            return KMDataManager.ud_array(forKey: udKey) as? [CGFloat] ?? self.squareNoteDashPatternDefaultValue()
//            return self.getData(forKey: KMPreference.squareNoteDashPatternKey) as? [CGFloat] ?? self.squareNoteDashPatternDefaultValue()
        }
        set {
            let pKey = KMPreference.squareNoteDashPatternKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(newValue, forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var lineNoteLineWidth: Float {
        get {
            let pKey = KMPreference.lineNoteLineWidthKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.floatValue ?? self.lineNoteLineWidthDefaultValue()
//            return self.getData(forKey: KMPreference.lineNoteLineWidthKey) as? Float ?? self.lineNoteLineWidthDefaultValue()
        }
        set {
            let pKey = KMPreference.lineNoteLineWidthKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var lineNoteLineStyle: Int {
        get {
            let pKey = KMPreference.lineNoteLineStyleKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.intValue ?? self.lineNoteLineStyleDefaultValue()
//            return self.getData(forKey: KMPreference.lineNoteLineStyleKey) as? Int ?? self.lineNoteLineStyleDefaultValue()
        }
        set {
            let pKey = KMPreference.lineNoteLineStyleKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var lineNoteDashPattern: [CGFloat] {
        get {
            let pKey = KMPreference.lineNoteDashPatternKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            return KMDataManager.ud_array(forKey: udKey) as? [CGFloat] ?? self.lineNoteDashPatternDefaultValue()
//            return self.getData(forKey: KMPreference.lineNoteDashPatternKey) as? [CGFloat] ?? self.lineNoteDashPatternDefaultValue()
        }
        set {
            let pKey = KMPreference.lineNoteDashPatternKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(newValue, forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var lineNoteStartLineStyle: Int {
        get {
            let pKey = KMPreference.lineNoteStartLineStyleKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.intValue ?? self.lineNoteStartLineStyleDefaultValue()
//            return self.getData(forKey: KMPreference.lineNoteStartLineStyleKey) as? Int ?? self.lineNoteStartLineStyleDefaultValue()
        }
        set {
            let pKey = KMPreference.lineNoteStartLineStyleKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var lineNoteEndLineStyle: Int {
        get {
            let pKey = KMPreference.lineNoteEndLineStyleKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.intValue ?? self.lineNoteEndLineStyleDefaultValue()
//            return self.getData(forKey: KMPreference.lineNoteEndLineStyleKey) as? Int ?? self.lineNoteEndLineStyleDefaultValue()
        }
        set {
            let pKey = KMPreference.lineNoteEndLineStyleKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var inkNoteLineWidth: Float {
        get {
            let pKey = KMPreference.inkNoteLineWidthKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.floatValue ?? self.inkNoteLineWidthDefaultValue()
//            return self.getData(forKey: KMPreference.inkNoteLineWidthKey) as? Float ?? self.inkNoteLineWidthDefaultValue()
        }
        set {
            let pKey = KMPreference.inkNoteLineWidthKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var inkNoteLineStyle: Int {
        get {
            let pKey = KMPreference.inkNoteLineStyleKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            let num = KMDataManager.ud_object(forKey: udKey) as? NSNumber
            return num?.intValue ?? self.inkNoteLineStyleDefaultValue()
//            return self.getData(forKey: KMPreference.inkNoteLineStyleKey) as? Int ?? self.inkNoteLineStyleDefaultValue()
        }
        set {
            let pKey = KMPreference.inkNoteLineStyleKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(NSNumber(value: newValue), forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
    
    var inkNoteDashPattern: [CGFloat] {
        get {
            let pKey = KMPreference.inkNoteDashPatternKey
            let udKey = KMPreferenceKeyToUDKey(pKey: pKey) ?? ""
            return KMDataManager.ud_array(forKey: udKey) as? [CGFloat] ?? self.inkNoteDashPatternDefaultValue()
//            return self.getData(forKey: KMPreference.inkNoteDashPatternKey) as? [CGFloat] ?? self.inkNoteDashPatternDefaultValue()
        }
        set {
            let pKey = KMPreference.inkNoteDashPatternKey
            self.syncDataToPDFView(newValue, forKey: pKey)
            self._syncDataToUserDefault(newValue, forKey: pKey)
            _ = self.setData(data: newValue, forKey: pKey)
        }
    }
}

// MARK: -
// MARK: Additions

extension KMPreferenceManager {
    var markupFontTextString: String {
        get {
            if let string = UserDefaults.standard.object(forKey: CFreeTextNoteFontNameKey) as? String {
                if (KMPreferenceManager.supportFonts.contains(string)) {
                    return string
                }
            }
            return KMDefaultFontName
        }
        set {
            if (KMPreferenceManager.supportFonts.contains(newValue)) {
                UserDefaults.standard.set(newValue, forKey: CFreeTextNoteFontNameKey)
                UserDefaults.standard.synchronize()
                
                self.postNotification(object: [.markup], userInfo: [KMMarkupFontTextStringKey : newValue])
            }
        }
    }
    
    var markupFontTextAligment: NSTextAlignment {
        get {
            let type = UserDefaults.standard.integer(forKey: CFreeTextNoteAlignmentKey)
            if (type == 0 || type == 1 || type == 2) {
                return NSTextAlignment(rawValue: type) ?? .left
            }
            return .left
        }
        set {
            if (newValue == .left || newValue == .center || newValue == .right) {
                UserDefaults.standard.set(newValue.rawValue, forKey: CFreeTextNoteAlignmentKey)
                UserDefaults.standard.synchronize()
                
                self.postNotification(object: [.markup], userInfo: [KMMarkupFontTextAligmentKey : newValue.rawValue])
            }
        }
    }
    
    var markupFontNoteString: String {
        get {
            if let fontName = KMPreferenceManager.shared.getData(forKey: KMPreference.markupFontNoteStringKey) as? String {
                if (KMPreference.supportFonts.contains(fontName)) {
                    return fontName
                }
            }
            return KMDefaultFontName
        }
        set {
            if (KMPreferenceManager.supportFonts.contains(newValue)) {
                let _ = KMPreferenceManager.shared.setData(data: newValue, forKey: KMPreference.markupFontNoteStringKey)
            }
        }
    }
    
    class var supportFonts: [String] {
        get {
            var fontNames: Array<String> = []
            for fontInfo in CPDFAnnotationModel.supportFonts() {
                if ((fontInfo is NSDictionary) == false) {
                    continue
                }
                
                for (familyString, styleStrings) in (fontInfo as! NSDictionary) {
                    if ((styleStrings is NSArray) == false) {
                        break
                    }
                    for styleString in (styleStrings as! NSArray) {
                        fontNames.append("\(familyString)-\(styleString)")
                    }
                }
            }
            return fontNames
        }
    }
    
    private func syncDataToPDFView(_ data: Any, forKey key: KMPreferenceKey) {
        if (key == KMMarkupColorHighlightKey) {
            if let color = data as? NSColor {
                UserDefaults.standard.setPDFListViewColor(color, forKey: CHighlightNoteColorKey)
            }
        } else if (key == KMMarkupColorUnderlineKey) {
            if let color = data as? NSColor {
                UserDefaults.standard.setPDFListViewColor(color, forKey: CUnderlineNoteColorKey)
            }
        } else if (key == KMMarkupColorStrikthroughKey) {
            if let color = data as? NSColor {
                UserDefaults.standard.setPDFListViewColor(color, forKey: CStrikeOutNoteColorKey)
            }
        } else if (key == KMMarkupColorPenKey) {
            if let color = data as? NSColor {
                UserDefaults.standard.setPDFListViewColor(color, forKey: CInkNoteColorKey)
            }
        } else if (key == KMMarkupColorNoteKey) {
            if let color = data as? NSColor {
                UserDefaults.standard.setPDFListViewColor(color, forKey: CAnchoredNoteColorKey)
            }
        } else if (key == KMMarkupColorRectangleFillKey) {
            if let color = data as? NSColor {
                UserDefaults.standard.setPDFListViewColor(color, forKey: CSquareNoteInteriorColorKey)
            }
        } else if (key == KMMarkupColorRectangleBorderKey) {
            if let color = data as? NSColor {
                UserDefaults.standard.setPDFListViewColor(color, forKey: CSquareNoteColorKey)
            }
        } else if (key == KMMarkupColorCircleFillKey) {
            if let color = data as? NSColor {
                UserDefaults.standard.setPDFListViewColor(color, forKey: CCircleNoteInteriorColorKey)
            }
        } else if (key == KMMarkupColorCircleBorderKey) {
            if let color = data as? NSColor {
                UserDefaults.standard.setPDFListViewColor(color, forKey: CCircleNoteColorKey)
            }
        } else if (key == KMMarkupColorLineKey) {
            if let color = data as? NSColor {
                UserDefaults.standard.setPDFListViewColor(color, forKey: CLineNoteColorKey)
            }
        } else if (key == KMMarkupColorArrowKey) {
            if let color = data as? NSColor {
                UserDefaults.standard.setPDFListViewColor(color, forKey: CArrowNoteColorKey)
            }
        } else if (key == KMMarkupColorTextKey) {
            if let color = data as? NSColor {
                UserDefaults.standard.setPDFListViewColor(color, forKey: CFreeTextNoteColorKey)
            }
        } else if (key == KMMarkupFontTextStringKey) {
            UserDefaults.standard.set(data, forKey: CFreeTextNoteFontNameKey)
            UserDefaults.standard.synchronize()
        } else if (key == KMMarkupFontTextAligmentKey) {
            UserDefaults.standard.set(data, forKey: CFreeTextNoteAlignmentKey)
            UserDefaults.standard.synchronize()
        } else if (key == KMMarkupFontNoteStringKey) {
//            UserDefaults.standard.set(data, forKey: CFreeTextNoteAlignmentKey)
//            UserDefaults.standard.synchronize()
        } else if (key == KMMarkupTextFontSizeKey) {
            UserDefaults.standard.set(data, forKey: CFreeTextNoteFontSizeKey)
            UserDefaults.standard.synchronize()
        } else if (key == KMMarkupNoteFontSizeKey) {
//            UserDefaults.standard.set(data, forKey: CFreeTextNoteFontSizeKey)
//            UserDefaults.standard.synchronize()
        } else {
            if let cKey = KMPreferenceKeyToCKey(pKey: key) {
                UserDefaults.standard.set(data, forKey: cKey)
                UserDefaults.standard.synchronize()
            }
        }
    }
    
    private func _syncDataToUserDefault(_ data: Any, forKey key: KMPreferenceKey) {
        // general
        if let udKey = KMPreferenceKeyToUDKey(pKey: key) {
            if let color = data as? NSColor {
                UserDefaults.standard.setColor(color, forKey: udKey)
            } else {
                KMDataManager.ud_set(data, forKey: udKey)
            }
        }
    }
    
    private func getDefaultColors(forKey key: KMPreferenceKey) -> [Double] {
        let markupGroupInfo: [KMPreferenceKey : Any] = self.getDefaultInfo()[KMPreferenceGroup.markup.rawValue] ?? [:]
        for key_i in [KMMarkupColorHighlightKey, KMMarkupColorUnderlineKey, KMMarkupColorStrikthroughKey,
                    KMMarkupColorPenKey, KMMarkupColorNoteKey, KMMarkupColorTextKey,
                    KMMarkupColorRectangleFillKey, KMMarkupColorRectangleBorderKey,
                    KMMarkupColorCircleFillKey, KMMarkupColorCircleBorderKey,
                    KMMarkupColorLineKey, KMMarkupColorArrowKey] {
            if (key == key_i) {
                return markupGroupInfo[key] as? [Double] ?? []
            }
        }

        let displayGroupInfo: [KMPreferenceKey : Any] = self.getDefaultInfo()[KMPreferenceGroup.display.rawValue] ?? [:]
        for key_i in [KMDisplayBackgroundNormalColorKey, KMDisplayBackgroundFullScreenColorKey,
                      KMDisplayFieldHighlightingColorKey, KMDisplayRequiredFieldHighlightingColorKey] {
            if (key == key_i) {
                return displayGroupInfo[key] as? [Double] ?? []
            }
        }
        
        return [0.0, 0.0, 0.0, 1.0]
    }
    
    private func getDefaultColor(forKey key: KMPreferenceKey) -> NSColor {
        return self.dataToColor(colors: self.getDefaultColors(forKey: key))
    }
    
    private func resetDataToPDFView() {
        let markupGroupInfo: [KMPreferenceKey : Any] = self.getDefaultInfo()[KMPreferenceGroup.markup.rawValue] ?? [:]
        // colors
        for key in [KMMarkupColorHighlightKey, KMMarkupColorUnderlineKey, KMMarkupColorStrikthroughKey,
                    KMMarkupColorPenKey, KMMarkupColorNoteKey, KMMarkupColorTextKey,
                    KMMarkupColorRectangleFillKey, KMMarkupColorRectangleBorderKey,
                    KMMarkupColorCircleFillKey, KMMarkupColorCircleBorderKey,
                    KMMarkupColorLineKey, KMMarkupColorArrowKey] {
            if let data = markupGroupInfo[key] as? [Double] {
                self.syncDataToPDFView(self.dataToColor(colors:data ), forKey: key)
            }
        }
        // lines
        for key in [KMFreeTextNoteLineStyleKey, KMFreeTextNoteLineWidthKey, KMFreeTextNoteDashPatternKey,
                    KMLineNoteLineStyleKey, KMLineNoteLineWidthKey, KMLineNoteDashPatternKey,
                    KMInkNoteLineStyleKey, KMInkNoteLineWidthKey, KMInkNoteDashPatternKey,
                    KMCircleNoteLineStyleKey, KMCircleNoteLineWidthKey, KMCircleNoteDashPatternKey,
                    KMSquareNoteLineStyleKey, KMSquareNoteLineWidthKey, KMSquareNoteDashPatternKey] {
            self.syncDataToPDFView(markupGroupInfo[key] as Any, forKey: key)
        }
        
        self.syncDataToPDFView(KMDefaultFontName, forKey: KMMarkupFontTextStringKey)
        self.syncDataToPDFView(NSTextAlignment.left.rawValue, forKey: KMMarkupFontTextAligmentKey)
        self.syncDataToPDFView(KMDefaultFontName, forKey: KMMarkupFontNoteStringKey)
    }
    
    public func resumeDataToPDFView() {
        self.syncDataToPDFView(KMPreferenceManager.shared.markupHighlightColor, forKey: KMMarkupColorHighlightKey)
        self.syncDataToPDFView(KMPreferenceManager.shared.markupUnderlineColor, forKey: KMMarkupColorUnderlineKey)
        self.syncDataToPDFView(KMPreferenceManager.shared.markupStrikthroughColor, forKey: KMMarkupColorStrikthroughKey)
        
        self.syncDataToPDFView(KMPreferenceManager.shared.markupPenColor, forKey: KMMarkupColorPenKey)
        self.syncDataToPDFView(KMPreferenceManager.shared.markupNoteColor, forKey: KMMarkupColorNoteKey)
        self.syncDataToPDFView(KMPreferenceManager.shared.markupRectangleFillColor, forKey: KMMarkupColorRectangleFillKey)
        
        self.syncDataToPDFView(KMPreferenceManager.shared.markupRectangleBorderColor, forKey: KMMarkupColorRectangleBorderKey)
        self.syncDataToPDFView(KMPreferenceManager.shared.markupCircleFillColor, forKey: KMMarkupColorCircleFillKey)
        self.syncDataToPDFView(KMPreferenceManager.shared.markupCircleBorderColor, forKey: KMMarkupColorCircleBorderKey)
        
        self.syncDataToPDFView(KMPreferenceManager.shared.markupLineColor, forKey: KMMarkupColorLineKey)
        self.syncDataToPDFView(KMPreferenceManager.shared.markupArrowColor, forKey: KMMarkupColorArrowKey)
        self.syncDataToPDFView(KMPreferenceManager.shared.markupTextColor, forKey: KMMarkupColorTextKey)
        self.syncDataToPDFView(KMPreferenceManager.shared.markupFontTextString, forKey: KMMarkupFontTextStringKey)
        self.syncDataToPDFView(KMPreferenceManager.shared.markupFontTextAligment.rawValue, forKey: KMMarkupFontTextAligmentKey)
        self.syncDataToPDFView(KMPreferenceManager.shared.markupFontNoteString, forKey: KMMarkupFontNoteStringKey)
    }
    
    private func _resetDataToUserDefault(group: KMPreferenceGroup) {
        if group == .general {
            KMDataManager.ud_set(nil, forKey: KMUserNameKey)
            KMDataManager.ud_set(NSNumber(value: 0), forKey: KMOpenDocumentInTabKey)
            KMDataManager.ud_set(false, forKey: KMReopenLastOpenFilesKey)
            KMDataManager.ud_set(0, forKey: KMInitialWindowSizeOptionKey)
            KMDataManager.ud_set(true, forKey: KMOpenContentsPaneOnlyForTOCKey)
            KMDataManager.ud_set(false, forKey: KMRememberSnapshotsKey)
            KMDataManager.ud_set(false, forKey: KMAutoSaveSkimNotesKey)
            KMDataManager.ud_set(false, forKey: KMSnapshotsOnTopKey)
            KMDataManager.ud_set(-1, forKey: KMSavePasswordOptionKey)
        } else if group == .display {
            KMDataManager.ud_set(NSNumber(value: self.thumbPageSizeDefaultValue()), forKey: KMThumbnailSizeKey)
            KMDataManager.ud_set(NSNumber(value: self.thumbSnapshotSizeDefaultValue()), forKey: KMSnapshotThumbnailSizeKey)
            KMDataManager.ud_set(NSNumber(value: self.outlineFontSizeDefaultValue()), forKey: KMTableFontSizeKey)
            KMDataManager.ud_set(NSNumber(value: self.greekThresholdDefaultValue()), forKey: KMGreekingThresholdKey)
            KMDataManager.ud_set(false, forKey: KMShouldAntiAliasKey)
            UserDefaults.standard.setColor(self.getDefaultColor(forKey: KMPreference.displayBackgroundNormalColorKey), forKey: KMBackgroundColorKey)
            UserDefaults.standard.setColor(self.getDefaultColor(forKey: KMPreference.displayBackgroundFullScreenColorKey), forKey: KMFullScreenBackgroundColorKey)
            UserDefaults.standard.setColor(self.getDefaultColor(forKey: KMPreference.readBarColorKey), forKey: KMReadingBarColorKey)
            KMDataManager.ud_set(false, forKey: KMReadingBarInvertKey)
        }
    }
    
    public func initDataForAppLaunch() {
//        KMPreferenceManager.shared.author = NSFullUserName()
        if (KMPreferenceManager.shared.autoSave) {
            if KMLightMemberManager.manager.canPayFunction() == false {
                KMPreferenceManager.shared.autoSave = false
            }
        }
    }
}

// MARK: -
// MARK: UserDefaults

fileprivate let kKMLastOpenFilepathKey = "lastOpenFilepath"
fileprivate let kKMLastOpenFilepathKeys = "lastOpenFilepaths"
fileprivate let kKMViewSettingKey = "viewSetting"
fileprivate let kKMPageNumberKey = "pageNumber"
fileprivate let kKMPageScaleKey = "pageScale"
extension KMPreferenceManager {
    var lastOpenFilepath: String? {
        get {
            return UserDefaults.standard.value(forKey: kKMLastOpenFilepathKey) as? String
        }
        set {
            UserDefaults.standard.set(newValue, forKey: kKMLastOpenFilepathKey)
            UserDefaults.standard.synchronize()
        }
    }
    
    var lastOpenFilepaths: [String]? {
        get {
            return UserDefaults.standard.value(forKey: kKMLastOpenFilepathKeys) as? [String]
        }
        set {
            UserDefaults.standard.set(newValue, forKey: kKMLastOpenFilepathKeys)
            UserDefaults.standard.synchronize()
        }
    }
    
    var viewSetting: [KMPreferenceViewSetting : Any]? {
        get {
            return UserDefaults.standard.value(forKey: kKMViewSettingKey) as? [KMPreferenceViewSetting : Any]
        }
        set {
            if (newValue == nil) {
                UserDefaults.standard.set(newValue, forKey: kKMViewSettingKey)
                UserDefaults.standard.synchronize()
                return
            }
            
            let viewSetting: [String : Any] = UserDefaults.standard.value(forKey: kKMViewSettingKey) as? [String : Any] ?? [:]
            var info: [String : Any] = [:]
            for (key, value) in viewSetting {
                info.updateValue(value, forKey: key)
            }
            for (key, value) in newValue ?? [:] {
                if (key == .pageNumber) { // 只处理 枚举
                    info.updateValue(value, forKey: key.rawValue)
                }
            }
            
            UserDefaults.standard.set(info, forKey: kKMViewSettingKey)
            UserDefaults.standard.synchronize()
        }
    }
    
    func setPageNumber(_ number: Int, forKey key: String) {
        UserDefaults.standard.set(number, forKey: "\(key)+\(kKMPageNumberKey)")
        UserDefaults.standard.synchronize()
    }
    
    func getPageNumber(forKey key: String) -> Int? {
        return UserDefaults.standard.value(forKey: "\(key)+\(kKMPageNumberKey)") as? Int
    }
    
    func setPageScale(_ scale: Float, forKey key: String) {
        UserDefaults.standard.set(scale, forKey: "\(key)+\(kKMPageScaleKey)")
        UserDefaults.standard.synchronize()
    }
    
    func getPageScale(forKey key: String) -> Float? {
        return UserDefaults.standard.value(forKey: "\(key)+\(kKMPageScaleKey)") as? Float
    }
}

// MARK: -
// MARK: Qiuck

extension KMPreferenceManager {
    internal func closeFileIsPrompt() -> Bool {
        return self.closeFilePromptType == .promp
    }
    
    func leftSideNeedCloseWhenOpenFile() -> Bool {
        return KMPreferenceManager.shared.leftSideDisplayType == .closeWhenOpenFile
    }
    
    var autoExpandPropertyPanel: Bool {
        get {
            return KMPreferenceManager.shared.propertyPanelExpandType == .auto
        }
    }
    
    // 单位: 秒
    var autoSaveTimeInterval: TimeInterval {
        get {
            var minute = KMPreferenceManager.shared.autoSavePerNumberMinute
            if (minute < 5) {
                minute = 5
            } else if (minute > 99) {
                minute = 99
            }
            
            let interval: TimeInterval = Double(minute) * 60.0
            return interval
        }
    }
}

// MARK: - Grouping

extension KMPreferenceManager {
    var grouping: Bool {
        get {
            return false
        }
    }
    
    private static var _groupingInfosKey = "KMGroupingInfosKey"
    private var _grouping_infos: [KMPreferenceKey : Any] {
        get {
            return (objc_getAssociatedObject(self, &Self._groupingInfosKey) as? [KMPreferenceKey : Any]) ?? [:]
        }
        set {
            objc_setAssociatedObject(self, &Self._groupingInfosKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    private static var _groupingObjectsKey = "KMGroupingObjectsKey"
    private var _grouping_objects: Set<KMPreferenceGroup> {
        get {
            return (objc_getAssociatedObject(self, &Self._groupingObjectsKey) as? Set) ?? []
        }
        set {
            objc_setAssociatedObject(self, &Self._groupingObjectsKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    private static var _groupingKey = "KMGroupingKey"
    private var _grouping: Bool {
        get {
            return (objc_getAssociatedObject(self, &Self._groupingKey) as? Bool) ?? false
        }
        set {
            objc_setAssociatedObject(self, &Self._groupingKey, newValue, .OBJC_ASSOCIATION_ASSIGN)
        }
    }
    
    func beginGrouping() {
        self._grouping = true
        self._grouping_objects = []
        self._grouping_infos = [:]
    }
    
    func endGrouping() {
        self._grouping = false
        
        if self._grouping_objects.isEmpty == false { // 发送通知
            var objects: [KMPreferenceGroup] = []
            for data in self._grouping_objects {
                objects.append(data)
            }
            self.postNotification(object: objects, userInfo: self._grouping_infos)
        }
        
        self._grouping_objects = []
        self._grouping_infos = [:]
    }
}