//
//  KMBackgroundManager.swift
//  PDF Reader Pro
//
//  Created by tangchao on 2022/12/23.
//

import Cocoa

class KMBackgroundManager: NSObject, NSCoding {
    let kBackgroundFolderPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!).stringByAppendingPathComponent("background")
    let kBackgroundPlistPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!).stringByAppendingPathComponent("background").stringByAppendingPathComponent("background.plist")
    
    var datas: Array<KMBackgroundModel> = []
    
    var backgroundObjects: [KMBackgroundObject] = []
    
    static let defaultManager = KMBackgroundManager()
    
    override init() {
        super.init()
        
        if let storedData = UserDefaults.standard.value(forKey: "kBackgroundInfoSaveKey") as? Data {
            NSKeyedUnarchiver.setClass(KMBackgroundObject.self, forClassName: "KMBackgroundObject")
            NSKeyedUnarchiver.setClass(KMBackgroundManager.self, forClassName: "KMBackgroundManager")
            if let man = NSKeyedUnarchiver.unarchiveObject(with: storedData) as? KMBackgroundManager {
                for object in man.backgroundObjects {
                    let model = self.parseObject(object: object)
                    self.datas.append(model)
                    self.backgroundObjects.append(object)
                }
            }
        }
        
//        if (FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) {
//            let dataDict = NSDictionary(contentsOfFile: kBackgroundPlistPath!)
//            if (dataDict == nil) {
//                return
//            }
//            var deleteKeys: Array<String> = []
//            for keyIndex in 0 ..< (dataDict?.allKeys.count)! {
//                let key: String = dataDict?.allKeys[keyIndex] as! String
//                let backgroundDict: NSDictionary = dataDict?.object(forKey: key) as! NSDictionary
//
//                let model = parseDictionary(dict: backgroundDict)
//                /// 赋值id
//                model.backgroundID = key
//                if (model.type == .file) {
//                    if (model.image == nil) {
//                        deleteKeys.append(key)
//                    } else {
//                        self.datas.append(model)
//                    }
//                } else {
//                    self.datas.append(model)
//                }
//            }
//
//            if (deleteKeys.count > 0) {
//                let newDict: NSMutableDictionary = NSMutableDictionary(dictionary: dataDict!)
//                for key in deleteKeys {
//                    newDict.removeObject(forKey: key)
//                }
//                newDict.write(toFile: kBackgroundPlistPath!, atomically: true)
//            }
//        }
    }
    
    required init?(coder: NSCoder) {
        if let data = coder.decodeObject(forKey: "backgroundObjects") {
            self.backgroundObjects = data as? [KMBackgroundObject] ?? []
        }
    }
    
    func encode(with coder: NSCoder) {
        coder.encode(self.backgroundObjects, forKey: "backgroundObjects")
    }
    
    func addTemplate(model: KMBackgroundModel) -> Bool {
        if (!FileManager.default.fileExists(atPath: kBackgroundFolderPath!)) {
            let create: ()? = try?FileManager.default.createDirectory(atPath: kBackgroundFolderPath!, withIntermediateDirectories: false)
            if (create == nil) {
                return false
            }
        }
        
        if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) {
            let create = try?FileManager.default.createFile(atPath: kBackgroundPlistPath!, contents: nil)
            if (create == nil) {
                return false
            }
        }
        
        let dict = NSDictionary(contentsOfFile: kBackgroundPlistPath!)
        var newDict:NSMutableDictionary!
        if (dict != nil) {
            newDict = NSMutableDictionary(dictionary: dict!)
        } else {
            newDict = NSMutableDictionary()
        }
        
        let backgroundDict = self.parseModel(model: model)
        if (backgroundDict.isEmpty) {
            let alert = NSAlert()
            alert.alertStyle = .critical
            alert.messageText = NSLocalizedString("文件\(model.imagePath.lastPathComponent)已损坏", comment: "")
            alert.runModal()
            return false
        }
        
        let tag = fetchAvailableName()//tagString()
        newDict.addEntries(from: [tag : backgroundDict])
        model.backgroundID = tag
        let result = newDict.write(toFile: kBackgroundPlistPath!, atomically: true)
        if (result) {
            if (self.datas.count < 1) {
                self.datas.append(model)
            } else {
                self.datas.insert(model, at: 0)
            }
        }
        
        let obj = self.parseModeForObject(model)
        self._addBackground(obj)
        
        return result
    }
    
    func deleteTemplate(model: KMBackgroundModel) -> Bool {
        if (model.backgroundID.isEmpty) {
            return false
        }
        
        if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) {
            return false
        }
        
        let key: String = model.backgroundID
        
        let dictionary = NSDictionary(contentsOfFile: kBackgroundPlistPath!)
        var newDictionary: NSMutableDictionary!
        if (dictionary != nil) {
            newDictionary = NSMutableDictionary(dictionary: dictionary!)
        } else {
            newDictionary = NSMutableDictionary()
        }
        newDictionary.removeObject(forKey: key)
        
        let result = newDictionary.write(toFile: kBackgroundPlistPath!, atomically: true)
        if (result) {
            if (model.type == .file) {
                let filePath: String = model.imagePath
                try?FileManager.default.removeItem(atPath: filePath)
            }
            
            if (self.datas.contains(model)) {
                self.datas.removeObject(model)
            }
        }
        
        let obj = self.parseModeForObject(model)
        self._removeBackground(obj)
        
        return result
    }
    
    func deleteAllTemplates() -> Bool {
        if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) {
            return false
        }
        
        let dictionary = NSDictionary(contentsOfFile: kBackgroundPlistPath!)
        var newDictionary: NSMutableDictionary!
        if (dictionary != nil) {
            newDictionary = NSMutableDictionary(dictionary: dictionary!)
        } else {
            newDictionary = NSMutableDictionary()
        }
        
        newDictionary.removeAllObjects()
        
        let result = newDictionary.write(toFile: kBackgroundPlistPath!, atomically: true)
        if (result) {
            self.datas.removeAll()
        }
        
        self._clearStored()
        
        return result
    }
    
    func deleteAllColorTemplates() -> Bool {
        if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) {
            return false
        }
        
        let dictionary = NSDictionary(contentsOfFile: kBackgroundPlistPath!)
        var newDictionary: NSMutableDictionary!
        if (dictionary != nil) {
            newDictionary = NSMutableDictionary(dictionary: dictionary!)
        } else {
            newDictionary = NSMutableDictionary()
        }
        
        let count = self.datas.count-1
        var deleteArray: Array<KMBackgroundModel> = []
        for i in 0 ... count {
            let model = self.datas[i]
            if (model.type == .color) {
                newDictionary.removeObject(forKey: model.backgroundID as Any)
                deleteArray.append(model)
            }
        }
        
        let result = newDictionary.write(toFile: kBackgroundPlistPath!, atomically: true)
        if (result) {
            for model in deleteArray {
                self.datas.removeObject(model)
            }
        }
        
        self._clearStored()
        
        return result
    }
    
    func deleteAllFileTemplates() -> Bool {
        if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) {
            return false
        }
        
        let dictionary = NSDictionary(contentsOfFile: kBackgroundPlistPath!)
        var newDictionary: NSMutableDictionary!
        if (dictionary != nil) {
            newDictionary = NSMutableDictionary(dictionary: dictionary!)
        } else {
            newDictionary = NSMutableDictionary()
        }
        
        let count = self.datas.count-1
        var deleteArray: Array<KMBackgroundModel> = []
        for i in 0 ... count {
            let model = self.datas[i]
            if (model.type == .file) {
                newDictionary.removeObject(forKey: model.backgroundID as Any)
                deleteArray.append(model)
            }
        }
        
        let result = newDictionary.write(toFile: kBackgroundPlistPath!, atomically: true)
        if (result) {
            for model in deleteArray {
                self.datas.removeObject(model)
            }
        }
        
        self._clearStored()
        
        return result
    }
    
    func updateTemplate(model: KMBackgroundModel) -> Bool {
        if (!FileManager.default.fileExists(atPath: kBackgroundFolderPath!)) {
            let create = try?FileManager.default.createDirectory(atPath: kBackgroundFolderPath!, withIntermediateDirectories: false)
            if (create == nil) {
                return false
            }
        }
        
        if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) {
            let create = try?FileManager.default.createFile(atPath: kBackgroundPlistPath!, contents: nil)
            if (create == nil) {
                return false
            }
        }
        
        var flagModel: KMBackgroundModel!
        for model_ in self.datas {
            if (model_.backgroundID == model.backgroundID) {
                flagModel = model_
                break
            }
        }
        
        if (flagModel == nil) {
            return false
        }
        
        let dict = NSDictionary(contentsOfFile: kBackgroundPlistPath!)
        var newDict:NSMutableDictionary!
        if (dict != nil) {
            newDict = NSMutableDictionary(dictionary: dict!)
        } else {
            newDict = NSMutableDictionary()
        }
        
        let modelDict = self.parseModel(model: model)
        if (modelDict.isEmpty) {
            let alert = NSAlert()
            alert.alertStyle = .critical
            alert.messageText = NSLocalizedString("文件\(model.imagePath.lastPathComponent)已损坏", comment: "")
            alert.runModal()
            return false
        }
        
        newDict.setObject(modelDict, forKey: flagModel.backgroundID as NSCopying)
        let result = newDict.write(toFile: kBackgroundPlistPath!, atomically: true)
        if (result) {
            let index = self.datas.index(of: flagModel)
            self.datas[index!] = model
        }
        
        return result
    }
    
    /**
        `Private Methods`
     */
    private func parseModel(model: KMBackgroundModel) -> Dictionary<String, Any> {
        let tag = tagString()
        
        var dict: [String : Any] = [:]
        if (model.type == .color) {
            var red: CGFloat = 0.0
            var green: CGFloat = 0.0
            var blue: CGFloat = 0.0
            model.color!.usingColorSpaceName(NSColorSpaceName.calibratedRGB)?.getRed(&red, green: &green, blue: &blue, alpha: nil)
            dict["red"] = red
            dict["green"] = green
            dict["blue"] = blue
        } else {
            if (!FileManager.default.fileExists(atPath: kBackgroundFolderPath!)) {
                try?FileManager.default.createDirectory(atPath: kBackgroundFolderPath!, withIntermediateDirectories: false)
            }

            let path = kBackgroundFolderPath?.stringByAppendingPathComponent("\(tag).png")
            let image = model.image
            let data = image?.tiffRepresentation
            let imageRep = NSBitmapImageRep(data: data!)
            imageRep?.size = image!.size

            var imageData: Data!
            let pathExtension = model.imagePath.components(separatedBy: ".").last
            if (pathExtension?.lowercased() == "png") {
                imageData = imageRep?.representation(using: NSBitmapImageRep.FileType.png, properties: [:])
            } else {
                imageData = imageRep?.representation(using: NSBitmapImageRep.FileType.jpeg, properties: [:])
            }

            do {
                try imageData.write(to: URL(fileURLWithPath: path!))
                dict["imagePath"] = path?.lastPathComponent
            }
            catch {
                KMPrint("Failed to write to disk.")
                return [:]
            }
        }
        
        dict["type"] = model.type.rawValue
        dict["scale"] = model.scale
        dict["opacity"] = model.opacity
        dict["rotation"] = model.rotation
        dict["horizontalMode"] = model.horizontalMode
        dict["horizontalSpace"] = model.horizontalSpace
        dict["verticalMode"] = model.verticalMode
        dict["verticalSpace"] = model.verticalSpace

        dict["pageRangeType"] = model.pageRangeType.rawValue
        dict["pagesString"] = model.pagesString
        
        return dict
    }
    
    private func parseDictionary(dict: NSDictionary) -> KMBackgroundModel {
        let model = KMBackgroundModel()
        
        model.type = KMBackgroundType(rawValue: dict.object(forKey: "type") as! Int)!
        
        if (model.type == .file) {
            let path = kBackgroundFolderPath?.stringByAppendingPathComponent(dict.object(forKey: "imagePath") as! String)
            if (FileManager.default.fileExists(atPath: path!)) {
                model.image = NSImage(contentsOfFile: path!)
                model.imagePath = path!
            } else {
                model.image = nil
            }
        } else {
            let red: CGFloat = dict.object(forKey: "red") as! CGFloat
            let green: CGFloat = dict.object(forKey: "green") as! CGFloat
            let blue: CGFloat = dict.object(forKey: "blue") as! CGFloat
            model.color = NSColor(red: red, green: green, blue: blue, alpha: 1.0)
        }
        
        model.scale = dict.object(forKey: "scale") as! CGFloat
        model.rotation = CGFloat(Int(dict.object(forKey: "rotation") as! CGFloat))
        model.opacity = (dict.object(forKey: "opacity") as! CGFloat)
        model.verticalMode = (dict.object(forKey: "verticalMode") as! Int)
        model.verticalSpace = (dict.object(forKey: "verticalSpace") as! CGFloat)
        model.horizontalMode = (dict.object(forKey: "horizontalMode") as! Int)
        model.horizontalSpace = (dict.object(forKey: "horizontalSpace") as! CGFloat)
        model.pageRangeType =  KMWatermarkeModelPageRangeType.init(rawValue: (dict.object(forKey: "pageRangeType") as! Int))!
        model.pagesString = (dict.object(forKey: "pagesString") as! String)
        
        return model
    }
    
    private func parseObject(object: KMBackgroundObject) -> KMBackgroundModel {
        let model = KMBackgroundModel()
        
        model.type = object.type
        if (model.type == .file) {
            model.image = object.image
            model.imagePath = object.imagePath ?? ""
        } else {
            model.color = object.color
        }
        
        model.scale = object.scale
        model.rotation = object.rotation.cgFloat
        model.opacity = object.opacity
        model.verticalMode = object.verticalMode
        model.verticalSpace = object.verticalSpace.cgFloat
        model.horizontalMode = object.horizontalMode
        model.horizontalSpace = object.horizontalSpace.cgFloat
        model.pageRangeType = KMWatermarkeModelPageRangeType.init(rawValue: object.pageRangeType.rawValue) ?? .all
        model.pagesString = object.pagesString
        model.backgroundID = object.backgroundID ?? "background0"
        
        return model
    }
    
    private func parseModeForObject(_ mode: KMBackgroundModel) -> KMBackgroundObject {
        let obj = KMBackgroundObject()
        
        obj.type = mode.type
        if (obj.type == .file) {
            obj.image = mode.image
            obj.imagePath = mode.imagePath
        } else {
            obj.color = mode.color ?? .red
        }
        
        obj.scale = mode.scale
        obj.rotation = Int(mode.rotation)
        obj.opacity = mode.opacity
        obj.verticalMode = mode.verticalMode
        obj.verticalSpace = Int(mode.verticalSpace)
        obj.horizontalMode = mode.horizontalMode
        obj.horizontalSpace = Int(mode.horizontalSpace)
        obj.pageRangeType = KMBatchOperatePageChoice.init(rawValue: mode.pageRangeType.rawValue) ?? .All
        obj.pagesString = mode.pagesString
        obj.backgroundID = mode.backgroundID
        
        return obj
    }
    
    private func _addBackground(_ obj: KMBackgroundObject) {
        self.backgroundObjects.insert(obj, at: 0)
        self._store()
    }
    
    private func _removeBackground(_ obj: KMBackgroundObject) {
        self.backgroundObjects.removeObject(obj)
        self._store()
    }
    
    private func _store() {
        let encodedObject = NSKeyedArchiver.archivedData(withRootObject: self)
        let defaults = UserDefaults.standard
        defaults.set(encodedObject, forKey: "kBackgroundInfoSaveKey")
        defaults.synchronize()
    }
    
    private func _clearStored() {
        let defaults = UserDefaults.standard
        defaults.set(nil, forKey: "kBackgroundInfoSaveKey")
        defaults.synchronize()
    }
    
    private func tagString() -> String {
        var result: String = ""
        
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyMMddHHmmss"
        result.append(dateFormatter.string(from: Date()))
        result = result.appendingFormat("%04d", arc4random()%10000)
        
        return result
    }
    
    func fetchAvailableName() -> String {
        var availableIndex = 0
        
        for item in datas {
            if item.backgroundID.hasPrefix("Background") {
                if let index = Int(item.backgroundID.dropFirst("Background".count)), index >= availableIndex {
                    availableIndex = index + 1
                }
            }
        }
        
        return "Background\(availableIndex)"
    }
}