//
//  KMPasswordInputWindow.swift
//  PDF Reader Pro
//
//  Created by tangchao on 2022/11/29.
//

import Cocoa
import PDFKit

@objc enum KMPasswordInputWindowType: Int {
    case open = 1
    case owner = 2
}

@objc enum KMPasswordInputWindowResult: Int {
    case cancel = 1
    case success = 2
}

typealias KMPasswordInputWindowItemClick = (KMPasswordInputWindow, Int, String) -> ()

@objcMembers class KMPasswordInputWindow: NSWindow, NibLoadable {
    @IBOutlet weak var titleLabel: NSTextField!
    
    @IBOutlet weak var despLabel: NSTextField!
    @IBOutlet weak var secureTextFiled: KMSecureTextFiled!
    
    @IBOutlet weak var iconImageView: NSImageView!
    
    @IBOutlet weak var passwordErrorLabel: NSTextField!
    @IBOutlet weak var cancelButton: NSButton!
    @IBOutlet weak var confirmButton: NSButton!
    
    var confirmButtonVC: KMDesignButton?
    
    var documentURL: URL?
    var itemClick: KMPasswordInputWindowItemClick?
    
    var type: KMPasswordInputWindowType = .open {
        didSet {
            self.titleLabel?.stringValue = NSLocalizedString("Permission Password", comment: "")

            var fileName = NSLocalizedString("", comment: "")
            if (self.documentURL != nil) {
                fileName.append("\(self.documentURL!.lastPathComponent)")
            }
            self.despLabel?.maximumNumberOfLines = 3
            self.despLabel?.lineBreakMode = .byTruncatingTail
            self.despLabel?.cell?.truncatesLastVisibleLine = true
            let ps = NSMutableParagraphStyle()
            ps.lineSpacing = 5
            let despLabelString = "\"\(fileName)\"\(NSLocalizedString("This PDF is password protected. Please enter the password below to access this PDF.", comment: ""))"
            self.despLabel?.attributedStringValue = NSAttributedString(string: despLabelString, attributes: [.foregroundColor : KMAppearance.Layout.h0Color(), .font : NSFont.SFProTextRegularFont(14), .paragraphStyle : ps])
        }
    }
    
    var canEncrpty = false
    
    static var permissionsStatus: CPDFDocumentPermissions = .none
    
    deinit {
        KMPrint("KMPasswordInputWindow 已释放了")
    }
    
    static var nibName: String? {
        return "KMPasswordInputWindow"
    }
    
    static func createFromNib(in bundle: Bundle) -> Self? {
        guard let nibName = self.nibName else {
            return nil
        }
        var topLevelArray: NSArray? = nil
        bundle.loadNibNamed(NSNib.Name(nibName), owner: self, topLevelObjects: &topLevelArray)
        guard let results = topLevelArray else {
            return nil
        }
        let views = Array<Any>(results).filter { $0 is Self }
        return views.last as? Self
    }
    
    class func createWindow() -> Self? {
        KMPasswordInputWindow.permissionsStatus = .none
        
        return self.createFromNib(in: MainBundle)
    }
    
//    func window() -> Self? {
//        KMPasswordInputWindow.canEncrpty = false
//        KMPasswordInputWindow.permissionsStatus = .none
//
//        var topLevelArray: NSArray? = nil
//        Bundle.main.loadNibNamed(NSNib.Name("KMPasswordInputWindow"), owner: self, topLevelObjects: &topLevelArray)
//        guard let results = topLevelArray else {
//            return nil
//        }
//
//        var passwordInputWindow: KMPasswordInputWindow!
//        for object in results {
//            let window: NSObject = object as! NSObject
//            if window.isKind(of: KMPasswordInputWindow.self) {
//                passwordInputWindow = window as! KMPasswordInputWindow?
//            }
//        }
//
//        guard let myWindow = passwordInputWindow else {
//            return nil
//        }
//        return myWindow as? Self
//    }
    
    override init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool) {
        super.init(contentRect: contentRect, styleMask: style, backing: backingStoreType, defer: flag)
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
            
        self.titleLabel.stringValue = NSLocalizedString("Permission Password", comment: "")
        self.titleLabel.textColor = KMAppearance.Layout.h0Color()
        self.titleLabel.font = NSFont.SFProTextRegularFont(16)
        
        self.despLabel.stringValue = NSLocalizedString("This PDF is password protected. Please enter the password below to access this PDF.", comment: "")
        self.despLabel.textColor = KMAppearance.Layout.h0Color()
        self.despLabel.font = NSFont.SFProTextRegularFont(14)
        self.despLabel.isSelectable = false
        let ps = NSMutableParagraphStyle()
        ps.lineSpacing = 5
        self.despLabel.maximumNumberOfLines = 2
        self.despLabel.lineBreakMode = .byTruncatingTail
            ps.lineBreakMode = .byTruncatingTail
        self.despLabel.attributedStringValue = NSAttributedString(string: despLabel.stringValue, attributes: [.foregroundColor : KMAppearance.Layout.h0Color(), .font : NSFont.SFProTextRegularFont(14), .paragraphStyle : ps])
        
        self.iconImageView.image = NSImage(named: "KMImageNameSecureIcon")

        self.secureTextFiled.backgroundView.wantsLayer = true
        self.secureTextFiled.backgroundView.layer?.borderWidth = 1
        self.secureTextFiled.backgroundView.layer?.borderColor = NSColor.buttonBorderColor().cgColor
        self.secureTextFiled.backgroundView.layer?.cornerRadius = 4
        self.secureTextFiled.placeholderString = NSLocalizedString("Password", comment: "")
            
        let rightView = NSView()
        rightView.frame = NSMakeRect(0, 0, 40, 32);
        self.secureTextFiled.rightView = rightView
        let clearButton = NSButton()
        rightView.addSubview(clearButton)
        clearButton.frame = NSMakeRect(10, 6, 20, 20)
        clearButton.wantsLayer = true
        clearButton.image = NSImage(named: "KMImageNameSecureClearIcon")
        clearButton.isBordered = false
        clearButton.target = self
        clearButton.action = #selector(clearButtonAction)
        rightView.isHidden = true

        self.secureTextFiled.becomeFirstResponderHandler = { [unowned self] securetextFiled in
            let mySecureTextField: KMSecureTextFiled = securetextFiled as! KMSecureTextFiled
            mySecureTextField.backgroundView.wantsLayer = true
            mySecureTextField.backgroundView.layer?.borderColor = NSColor.km_init(hex: "#1770F4").cgColor
                
            if mySecureTextField.password().isEmpty {
                self.secureTextFiled.rightView?.isHidden = true
            } else {
                self.secureTextFiled.rightView?.isHidden = false
            }
            self.passwordErrorLabel.isHidden = true
        }
        self.secureTextFiled.valueDidChange = { [unowned self] view, string in
            view.backgroundView.layer?.borderColor = NSColor.km_init(hex: "#1770F4").cgColor
            self.passwordErrorLabel.isHidden = true
            if string.isEmpty {
                view.rightView?.isHidden = true
                self.dealConfirmButtonEnabledState(enabled: false)
            } else {
                view.rightView?.isHidden = false
                self.dealConfirmButtonEnabledState(enabled: true)
            }
        }
            
        self.secureTextFiled.enterAction = { [unowned self] in
            self.confirmButtonAction()
        }
        
        self.passwordErrorLabel.stringValue = NSLocalizedString("Incorrect password. Please try again.", comment: "")
        self.passwordErrorLabel.font = NSFont.systemFont(ofSize: 12)
        self.passwordErrorLabel.wantsLayer = true
        self.passwordErrorLabel.textColor = NSColor.km_init(hex: "#F3465B")
        self.passwordErrorLabel.isHidden = true
        
        for button in [cancelButton, confirmButton] {
//                button?.wantsLayer = true
//                button?.layer?.cornerRadius = 4
//                button?.bezelStyle = .roundRect
//                button?.setButtonType(.momentaryPushIn)
                
            button!.target = self
            if ((button?.isEqual(to: cancelButton))!) {
//                    button?.layer?.borderWidth = 1
//                    button?.layer?.borderColor = NSColor.buttonBorderColor().cgColor
//                    button?.title = NSLocalizedString("Cancel", comment: "")
//                    button?.title = ""
//                    button?.setTitleColor(NSColor.buttonTitleColor())
//                    button?.font = NSFont.SFProTextRegularFont(14)
//                    button?.action = #selector(cancelButtonAction)
            } else {
//                    button?.title = NSLocalizedString("Open", comment: "")
//                    button?.attributedTitle = NSMutableAttributedString(string: button!.title, attributes: [.foregroundColor : NSColor.white])
//                    button?.font = NSFont.SFProTextRegularFont(14)
//                    button?.action = #selector(confirmButtonAction)
//                    button?.title = ""
            }
        }
            
        let cancelButtonVC = KMDesignButton(withType: .Text)
//            self.cancelButton.addSubview(cancelButtonVC.view)
        cancelButtonVC.view.frame = self.cancelButton.bounds
        cancelButtonVC.view.autoresizingMask = [.width, .height]
        cancelButtonVC.stringValue = NSLocalizedString("Cancel", comment: "")
        cancelButtonVC.button(type: .Sec_Icon, size: .m)
        cancelButtonVC.target = self
        cancelButtonVC.action = #selector(cancelButtonAction)
        cancelButtonVC.button.keyEquivalent = KMKeyEquivalent.esc.string()
        self.cancelButton.title = NSLocalizedString("Cancel", comment: "")
        self.cancelButton.action = #selector(cancelButtonAction)
            
        let confirmButtonVC = KMDesignButton(withType: .Text)
//            self.confirmButton.addSubview(confirmButtonVC.view)
        confirmButtonVC.view.frame = self.confirmButton.bounds
        confirmButtonVC.view.autoresizingMask = [.width, .height]
        confirmButtonVC.stringValue = NSLocalizedString("Open", comment: "")
        confirmButtonVC.button(type: .Cta, size: .m)
        confirmButtonVC.target = self
        confirmButtonVC.action = #selector(confirmButtonAction)
        self.confirmButtonVC = confirmButtonVC
        self.confirmButtonVC?.button.keyEquivalent = KMKeyEquivalent.enter
        self.confirmButton.title = NSLocalizedString("Open", comment: "")
        self.confirmButton.action = #selector(confirmButtonAction)
        
        self.dealConfirmButtonEnabledState(enabled: false)
    }
    
    // MARK: - Actions
    
    @objc func cancelButtonAction() {
        guard let callback = self.itemClick else {
            return
        }
        
        callback(self, 1, "")
    }
    
    @objc func confirmButtonAction() {
        if (!self.canEncrpty) {
            return
        }
        guard let documentURL = self.documentURL else {
            return
        }
        
        if (self.type == .open) {
            let document: CPDFDocument = CPDFDocument(url: documentURL)
            if document.permissionsStatus == .none {
                let reuslt = document.unlock(withPassword: secureTextFiled.password())
                /// CPDFDocumentPermissionsNone 解锁失败
                /// CPDFDocumentPermissionsUser 输入的开启密码
                /// CPDFDocumentPermissionsOwner 输入的权限密码
                KMPasswordInputWindow.permissionsStatus = document.permissionsStatus
                if document.permissionsStatus != CPDFDocumentPermissions.none { /// 密码正确
                    guard let callback = self.itemClick else {
                        return
                    }
                    
                    callback(self ,2, secureTextFiled.password())
                } else { /// 密码错误
                    self.passwordErrorLabel.isHidden = false
                    self.passwordErrorLabel.stringValue = NSLocalizedString("Incorrect password. Please try again.", comment: "")
                    self.secureTextFiled.backgroundView.layer?.borderColor = NSColor.km_init(hex: "#F3465B").cgColor
                }
            }
            return
        }
        
        /// 权限密码类型
        let document: CPDFDocument = CPDFDocument(url: documentURL)
        if (document.isLocked) {
            if document.permissionsStatus == CPDFDocumentPermissions.none {
                let reuslt = document.unlock(withPassword: secureTextFiled.password())
                KMPasswordInputWindow.permissionsStatus = document.permissionsStatus
                
                if document.permissionsStatus == .owner { /// 密码正确
                    guard let callback = self.itemClick else {
                        return
                    }
                    
                    callback(self, 2, secureTextFiled.password())
                } else { /// 密码错误
                    self.passwordErrorLabel.isHidden = false
                    self.passwordErrorLabel.stringValue = NSLocalizedString("Incorrect password. Please try again.", comment: "")
                    self.secureTextFiled.backgroundView.layer?.borderColor = NSColor.km_init(hex: "#F3465B").cgColor
                }
            }
        } else {
            if document.permissionsStatus == CPDFDocumentPermissions.user {
                document.unlock(withPassword: secureTextFiled.password())
                KMPasswordInputWindow.permissionsStatus = document.permissionsStatus
                if document.permissionsStatus == .owner { /// 密码正确
                    guard let callback = self.itemClick else {
                        return
                    }
                    
                    callback(self, 2, secureTextFiled.password())
                } else { /// 密码错误
                    self.passwordErrorLabel.isHidden = false
                    self.passwordErrorLabel.stringValue = NSLocalizedString("Incorrect password. Please try again.", comment: "")
                    self.secureTextFiled.backgroundView.layer?.borderColor = NSColor.km_init(hex: "#F3465B").cgColor
                }
            }
        }
    }
    
    @objc func clearButtonAction() {
        self.secureTextFiled.clear()
    }

    func dealConfirmButtonEnabledState(enabled: Bool) {
        self.canEncrpty = enabled
            
//            confirmButton.wantsLayer = true
//            confirmButton.layer?.backgroundColor = NSColor.buttonFunctionBackgroundColor(enabled: enabled).cgColor
//            confirmButton?.title = NSLocalizedString("Open", comment: "")
//            var color = NSColor.buttonTitleColor(enabled: enabled)
//            if (enabled) {
//                color = NSColor.km_init(hex: "#FFFFFF")
//            }
//            confirmButton?.attributedTitle = NSMutableAttributedString(string: confirmButton!.title, attributes: [.foregroundColor : color])
            
//            if (self.confirmButtonVC != nil) {
//                self.confirmButtonVC?.enabled = enabled
//            }
        self.confirmButton.isEnabled = enabled
    }
    
    override func mouseUp(with event: NSEvent) {
        super.mouseUp(with: event)
        
        self.makeFirstResponder(nil)
        self.secureTextFiled.backgroundView.layer?.borderColor = NSColor.buttonBorderColor().cgColor
        self.passwordErrorLabel.isHidden = true
    }
}

extension KMPasswordInputWindow {
    @objc class func openWindow(window: NSWindow, type: KMPasswordInputWindowType = .open, url: URL, callback: @escaping (KMPasswordInputWindowResult, String?)->Void) -> KMPasswordInputWindow {
        let passwordWindow = KMPasswordInputWindow.createWindow()
        passwordWindow?.documentURL = url
        passwordWindow?.type = type

        passwordWindow?.itemClick = { pwdWin, index, string in
            if let sheetParent = pwdWin.sheetParent {
                sheetParent.endSheet(pwdWin)
            }
            if index == 1 { /// 关闭
                callback(.cancel, "")
                return
            }
            /// 解密成功
            callback(.success, string)
        }

        window.beginSheet(passwordWindow!)
        return passwordWindow!
    }
    
    @objc class func success_openWindow(window: NSWindow, type: KMPasswordInputWindowType = .open, url: URL, callback: @escaping (String)->Void) {
        let passwordWindow = KMPasswordInputWindow.createWindow()
        passwordWindow?.documentURL = url
        passwordWindow?.type = type

        passwordWindow?.itemClick = { pwdWin, index, string in
            if let sheetParent = pwdWin.sheetParent {
                sheetParent.endSheet(pwdWin)
            }
            if index == 1 { /// 关闭
                return
            }
            /// 解密成功
            callback(string)
        }

        window.beginSheet(passwordWindow!)
    }
    
    @objc class func openWindow(window: NSWindow, url: URL, needOwner: Bool, callback: @escaping (KMPasswordInputWindowResult, String?)->Void) {
        let passwordWindow = KMPasswordInputWindow.createWindow()
        passwordWindow?.documentURL = url
        
        let document = CPDFDocument(url: url)
        if (document?.isLocked != nil && document!.isLocked) {
            passwordWindow?.type = .open
        } else if (document?.isEncrypted != nil && document!.isEncrypted) {
            passwordWindow?.type = .owner
        } else {
            passwordWindow?.type = .open
        }
        

        passwordWindow?.itemClick = { pwdWin, index, string in
            let type = pwdWin.type
            if let sheetParent = pwdWin.sheetParent {
                sheetParent.endSheet(pwdWin)
            }
            if index == 1 { /// 关闭
                callback(.cancel, "")
                return
            }
            /// 解密成功
            if (type == .owner) { // 解除的是权限密码
                callback(.success, string)
                return
            }
            
            // 解除的是开启密码
            if (needOwner == false) { // 不需要解除权限密码
                callback(.success, string)
                return
            }
            
            if (document == nil) {
                callback(.success, string)
                return
            }
            
            document?.unlock(withPassword: string)
            if (document?.permissionsStatus == .owner) { // 用户是使用的权限密码解密
                callback(.success, string)
                return
            }
            if (document!.allowsCopying == true && document!.allowsPrinting == true) { // 文件没有权限限制
                callback(.success, string)
                return
            }
            
            // 需要解除权限密码
            KMPasswordInputWindow.openWindow(window: window, type: .owner, url: url) { result, password in
                if (result == .cancel) {
                    callback(.cancel, "")
                    return
                }
                callback(.success, password)
            }
        }

        window.beginSheet(passwordWindow!)
    }
    
    class func saveDocument(_ document: CPDFDocument) -> Bool {
        let toPath = document.documentURL.path
        
        let tempFilePath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!).stringByAppendingPathComponent("\((document.documentURL.lastPathComponent))")
        if (FileManager.default.fileExists(atPath: tempFilePath!)) {
            /// 清空数据
            try?FileManager.default.removeItem(atPath: tempFilePath!)
        }
        
        var result: Bool = document.write(to: URL(fileURLWithPath: tempFilePath!))
        if (result == false) {
            return false
        }
        
        try?FileManager.default.removeItem(atPath: toPath)
        result = ((try?FileManager.default.moveItem(atPath: tempFilePath!, toPath: toPath)) != nil)
        /// 清空数据
        try?FileManager.default.removeItem(atPath: tempFilePath!)
        return result
    }
    
    class func saveDocumentForRemovePassword(_ document: CPDFDocument) -> Bool {
        let toPath = document.documentURL.path
        
        let tempFilePath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!).stringByAppendingPathComponent("\((document.documentURL.lastPathComponent))")
        if (FileManager.default.fileExists(atPath: tempFilePath!)) {
            /// 清空数据
            try?FileManager.default.removeItem(atPath: tempFilePath!)
        }
        
        var result: Bool = document.writeDecrypt(to: URL(fileURLWithPath: tempFilePath!))
        if (result == false) {
            return false
        }
        
        try?FileManager.default.removeItem(atPath: toPath)
        result = ((try?FileManager.default.moveItem(atPath: tempFilePath!, toPath: toPath)) != nil)
        /// 清空数据
        try?FileManager.default.removeItem(atPath: tempFilePath!)
        return result
    }
}

extension NSOpenPanel {
    /**
     * 打开 NSOpenPanel 窗口(如果文档存在开启密码或者权限密码,则会弹密码输入框)
     * @param window 弹出 NSOpenPanel 的窗口 [可选] [默认为 主窗口]
     * @param needOwner 是否需要限制权限密码(如果存在权限密码,会在解锁后再弹权限密码弹窗(目前未实现)) [可选] [默认为 false]
     * @param callback 回调
     *
     *  默认弹开启密码输入框,needOwner = true 弹权限密码输入框
     */
    class func km_secure_openPanel(window: NSWindow = NSApp.mainWindow!, needOwner: Bool = false, callback:@escaping (URL?, KMPasswordInputWindowResult? , String?)->Void) {
        let panel = NSOpenPanel()
        panel.allowedFileTypes = ["pdf"]
        panel.beginSheetModal(for: window) { response in
            if (response == .cancel) {
                callback(nil, nil, nil)
                return
            }
            
            let document = CPDFDocument(url: panel.url)
            if ((document?.isLocked)! == false) {
                if (document?.isEncrypted == false) {
                    callback(panel.url, nil, nil)
                    return
                }
                
                if (!needOwner) {
                    callback(panel.url, nil, nil)
                    return
                }
                
                KMPasswordInputWindow.openWindow(window: window, type: .owner, url: panel.url!) { result, password in
                    if (result == .cancel) {
                        callback(panel.url, .cancel , nil)
                        return
                    }
                    
                    callback(panel.url, .success , password)
                }
                return
            }
            
            /// 已加锁(开启密码)
            KMPasswordInputWindow.openWindow(window: window, url: panel.url!) { result, password in
                if (result == .cancel) {
                    callback(panel.url, .cancel, nil)
                    return
                }
                
                if (!needOwner) {
                    callback(panel.url, .success, password)
                    return
                }
                
                /// 用户输入的是权限密码
                if (KMPasswordInputWindow.permissionsStatus == .owner) {
                    callback(panel.url, .success ,password)
                    return
                }
                
                /// 用户输入的是开启密码 (无法判断是否还有权限未解密)
                
                /// 还有权限密码未解锁
//                KMPasswordInputWindow.openWindow(window: window, type: .owner, url: panel.url!) { result, password in
//                    if (result == .cancel) {
//                        callback(panel.url, .cancel , nil)
//                        return
//                    }
//
//                    callback(panel.url, .success , password)
//                }
                callback(panel.url, .success ,password)
            }
        }
    }
    
    /**
     * 打开 NSOpenPanel 窗口(如果文档存在开启密码或者权限密码,则会弹密码输入框)
     * @param window 弹出 NSOpenPanel 的窗口 [可选] [默认为 主窗口]
     * @param needOwner 是否需要限制权限密码(如果存在权限密码,会在解锁后再弹权限密码弹窗(目前未实现)) [可选] [默认为 false]
     * @param callback 回调
     *
     *  默认弹开启密码输入框,needOwner = true 弹权限密码输入框
     *  只返回成功的结果, 用户关闭的操作都未回调(如果有需要回调的需求可以使用 km_secure_openPanel 方法)
     */
    class func km_secure_openPanel_success(window: NSWindow = NSApp.mainWindow!, needOwner: Bool = false, callback:@escaping (URL, String?)->Void) {
        let panel = NSOpenPanel()
        panel.allowedFileTypes = ["pdf"]
        panel.beginSheetModal(for: window) { response in
            if (response == .cancel) {
                return
            }
            
            let document = CPDFDocument(url: panel.url)
            if ((document?.isLocked)! == false) {
                if (document?.isEncrypted == false) {
                    callback(panel.url!, nil)
                    return
                }
                
                if (!needOwner) {
                    callback(panel.url!, nil)
                    return
                }
                
                KMPasswordInputWindow.openWindow(window: window, type: .owner, url: panel.url!) { result, password in
                    if (result == .cancel) {
                        return
                    }
                    
                    callback(panel.url!, password)
                }
                return
            }
            
            /// 已加锁(开启密码)
            KMPasswordInputWindow.openWindow(window: window, url: panel.url!) { result, password in
                if (result == .cancel) {
                    return
                }
                
                if (!needOwner) {
                    callback(panel.url!, password)
                    return
                }
                
                /// 用户输入的是权限密码
                if (KMPasswordInputWindow.permissionsStatus == .owner) {
                    callback(panel.url!, password)
                    return
                }
                
                callback(panel.url!, password)
            }
        }
    }
}