//
//  AutoTest.swift
//  KdanAuto
//
//  Created by 朱东勇 on 2022/11/25.
//

import Foundation
import AppKit

var cacheObjects = NSMutableDictionary()

class AutoTest : NSObject, AutoTestProtocal {
    var reportString : NSMutableAttributedString? = nil
    
    public var _status :  AutoTestStatus = .Normal
    
    var _fileType : String = "RTF"
    var _type : String = "Others"
    var _extention : String = "rtf"
    
    class func autoTestFor(_ fileType:NSString ,type:NSString) -> AutoTest? {
        let key = String(fileType) + "." + String(type)
        
        if cacheObjects.value(forKey: key) != nil {
            let object = cacheObjects.value(forKey: key)
            
            return object as? AutoTest
        }
        
//        if  let cacheObject as AutoTest {
//            return cacheObject
//        }
        
        let fileTypes = testTypeInfo[fileType] as! NSArray
       
        let clsname = "KdanAuto"//Bundle.main.infoDictionary! ["CFBundleExecutable"]
    
        for item in fileTypes {
            let cItem = item as! NSDictionary
            
            let cType = cItem["Type"] as! NSString
            let cExtention = cItem["Extention"] as! NSString
            if (cType.isEqual(to: type)) {
                let className = String((clsname )+"."+(cItem["Class"] as! String)) as! String
                let cl = NSClassFromString(className) as! AutoTest.Type
                
                let object = cl.shared()
                object?._fileType = fileType as! String
                object?._type = String(cType)
                object?._extention = String(cExtention)
                cacheObjects.setValue(object, forKey: key)
                
                return object
            }
        }
        
        let object = AutoTest.shared()
        
        object?._fileType = fileType as String
        object?._type = String(cType)
        object?._extention = ""
        cacheObjects.setValue(object, forKey: key)
        
        return object
    }
    
    class func shared() -> AutoTest? {
        return AutoTest()
    }
    
    func fileType() -> String {
        return _fileType
    }
    
    func type() -> String {
        return _type
    }
    
    func name() -> String {
        return "未指定类型对照测试"
    }
    
    func keys() -> NSArray {
        return ["快照对比"]
    }
    
    func selectedKeys() -> NSArray {
        let userDefaults = UserDefaults.standard
        
        let key = self.fileType() + "." + self.type() + ".selectedKeys"
        
        if userDefaults.value(forKey: key) != nil {
            return userDefaults.value(forKey: key) as! NSArray
        }
        
        self.setSelectedKeys(self.keys())
        
        return self.keys()
    }
    
    func setSelectedKeys(_ keys: NSArray) {
        let userDefaults = UserDefaults.standard
        
        let key = self.fileType() + "." + self.type() + ".selectedKeys"
        
        userDefaults.setValue(keys, forKey: key)
        userDefaults.synchronize()
    }
    
    func status() -> AutoTestStatus {
        return _status
    }
    
    func setStatus(_ status:AutoTestStatus) {
        _status = status
    }
    
    // Auto Test
    func autoTest() {
        clearCacheFiles()
        
        if hasOriginFile() {
            let needCompare = self.selectedKeys().contains("快照对比")
            
            if !needCompare {
                _status = .Finished
                return
            }
            
            _status = .Process
            reportString = NSMutableAttributedString.init(string: "\n【\(String(self.fileType())) - \(self.name())】快照比对开始!\n",
                                                          attributes:[.foregroundColor : NSColor.blue])
            let files = DataModel.shared.originFilesFor(_fileType, type: _type) as [String]
            
            let checkDirectory = self.checkFileDirectory()
            let originDirectory = self.originFileDirectory()
            let resultDirectory = self.resultFileDirectory()
            for fileName in files {
                let fName = NSString(string: fileName).deletingPathExtension
                let originPath = NSString(string: originDirectory).appendingPathComponent(fName+".pdf")
                let resultPath = NSString(string: resultDirectory).appendingPathComponent(fName+"."+_extention)
                let checkPath = NSString(string: checkDirectory).appendingPathComponent(fName+"."+_extention)
                
                
                reportString?.append(NSMutableAttributedString.init(string: "【\(String(self.fileType())) - \(self.name())】开始转换文件 \"\(fName)\"\n",
                                                                    attributes:[.foregroundColor : NSColor.blue]))
                // ...
                // 执行转换过程
                let convertSemaphore = DispatchSemaphore(value: 0)
                var success = false
                let convertQueue = DispatchQueue.global()
                convertQueue.async {
                    success = FileConverter.shared().converter(originPath, inDesPath: resultPath)
                    
                    convertSemaphore.signal()
                }
                convertSemaphore.wait()
                
                
                var isDirectory = ObjCBool(false)
                if FileManager.default.fileExists(atPath: resultPath, isDirectory:&isDirectory) && success {
                    // compare screenshoot between result file with check file
                    if needCompare {
                        let items = NSMutableArray()
                        if (isDirectory.boolValue) {
                            let searchItems = try! FileManager.default.contentsOfDirectory(atPath: resultPath)
                            for item in NSArray(array: searchItems) {
                                let ext = NSString(string: item as! String).pathExtension.lowercased()
                                if NSArray(array: [_extention]).contains(ext) {
                                    let fileName = NSString(string: fName+"."+_extention+"/\(item as! String)").deletingPathExtension
                                    items.add(fileName)
                                }
                            }
                        }else {
                            items.add(fName)
                        }
                        
                        for item in items {
                            let subFileName = item as! String
                            let subResultPath = NSString(string: resultDirectory).appendingPathComponent(subFileName+"."+_extention)
                            
                            reportString?.append(NSMutableAttributedString.init(string: "【\(String(self.fileType())) - \(self.name())】文件 \"\(subFileName+".\(_extention)")\"快照生成中\n",
                                                                                attributes:[.foregroundColor : NSColor.black]))
                            let rComparePath = NSString(string: resultDirectory).appendingPathComponent(subFileName+"_\(DataModel.shared.latestReportID()!).jpg")
                            let cComparePath = NSString(string: checkDirectory).appendingPathComponent(subFileName+".jpg")
                            
                            
                            let processThumbSemaphore = DispatchSemaphore(value: 0)
                            var processSuccess = false
                            let thumbnailQueue = DispatchQueue.global()
                            thumbnailQueue.async {
                                processSuccess = ProcessThumbnal.process(subResultPath, desPath: rComparePath, outputSize: CGSize.init(width: 2048, height: 2048))
                                
                                if ( processSuccess &&
                                     FileManager.default.fileExists(atPath: rComparePath)) {
                                    
                                    let degree = self.compareJPEG(rComparePath, checkPath: cComparePath)
                                    
                                    if degree == -1 {
                                        self.reportString?.append(NSMutableAttributedString.init(string: "【\(String(self.fileType())) - \(self.name())】文件 \"\(subResultPath)\"快照对比失败,生成快照失败或无比对文件\n",
                                                                                            attributes:[.foregroundColor : NSColor.red]))
                                    }else {
                                        self.reportString?.append(NSMutableAttributedString.init(string: "【\(String(self.fileType())) - \(self.name())】文件 \"\(subResultPath)\"快照对比完成,图像相似度 \(degree)%\n",
                                                                                            attributes:[.foregroundColor : NSColor.red]))
                                    }
                                }else {
                                    self.reportString?.append(NSMutableAttributedString.init(string: "【\(String(self.fileType())) - \(self.name())】文件 \"\(subResultPath)\"快照生成失败\n",
                                                                                        attributes:[.foregroundColor : NSColor.red]))
                                }
                                
                                processThumbSemaphore.signal()
                            }
                            processThumbSemaphore.wait()
                            
                        }
                    }
                }else {
                    reportString?.append(NSMutableAttributedString.init(string: "【\(String(self.fileType())) - \(self.name())】文件 \"\(fName)\"转档失败!\n",
                                                                        attributes:[.foregroundColor : NSColor.red]))
                }
            }
            
            _status = .Finished
        }else {
            _status = .Normal
        }
    }
    
    func testReport() -> NSAttributedString? {
        return reportString
    }
    
    
    func compareJPEG(_ resultPath:String, checkPath:String) -> Double {
        if !FileManager.default.fileExists(atPath: resultPath) || !FileManager.default.fileExists(atPath: checkPath) {
            return -1
        }
        
        let rImage = NSImage.init(contentsOfFile: resultPath) ?? nil
        let cImage = NSImage.init(contentsOfFile: checkPath) ?? nil
        
        if nil == rImage || nil == cImage {
            return -1
        }
        
        let resultImage = rImage as! NSImage
        let checkImage = cImage as! NSImage
        
        let resultImageRep = NSBitmapImageRep.init(cgImage: resultImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
        let checkImageRep = NSBitmapImageRep.init(cgImage: checkImage.cgImage(forProposedRect: nil, context: nil, hints: nil)!)
        
        let rWidth = resultImageRep.pixelsWide
        let rHeight = resultImageRep.pixelsHigh
        let rBitPerPixel = resultImageRep.bitsPerPixel / 8
        let rBytePerRow = resultImageRep.bytesPerRow
        
        let cWidth = checkImageRep.pixelsWide
        let cHeight = checkImageRep.pixelsHigh
        let cBitPerPixel = checkImageRep.bitsPerPixel / 8
        let cBytePerRow = checkImageRep.bytesPerRow
        
        let maxWidth = min(rWidth, cWidth) - 1
        let maxHeight = min(rHeight, cHeight) - 1
        
        var equalCount = 0 as Double
        for w in 0...maxWidth {
            let x = Int(w)
            
            for h in 0...maxHeight {
                let y = Int(h)
                
                let cColor = checkImageRep.colorAt(x: x, y: y) as! NSColor
                let rColor = resultImageRep.colorAt(x: x, y: y) as! NSColor
                
                if (cColor.isEqual(to: rColor)) {
                    equalCount = equalCount + 1
                }
            }
        }
        
        return Double(equalCount/(Double(cWidth) * Double(cHeight)) * 100.0)
    }
    
    /// Path
    func originFileDirectory() -> String {
        return DataModel.shared.directoryPath().appendingFormat("/\(self.fileType())/\(self.type())/\(kOriginPathComponent)")
    }
    
    func resultFileDirectory() -> String {
        return DataModel.shared.directoryPath().appendingFormat("/\(self.fileType())/\(self.type())/\(kResultPathComponent)")
    }
    
    func checkFileDirectory() -> String {
        return DataModel.shared.directoryPath().appendingFormat("/\(self.fileType())/\(self.type())/\(kCheckPathComponent)")
    }
    
    // check File Exist
    func hasOriginFile() -> Bool {
        if nil != self.originFileDirectory() && FileManager.default.fileExists(atPath: self.originFileDirectory()) {
            let files = try? FileManager.default.subpathsOfDirectory(atPath: self.originFileDirectory())
            return (files ?? []).count > 0
        }
        
        return false
    }
    
    func hasResultFile() -> Bool {
        if nil != self.resultFileDirectory() && FileManager.default.fileExists(atPath: self.resultFileDirectory()) {
            let files = try? FileManager.default.subpathsOfDirectory(atPath: self.resultFileDirectory())
            return (files ?? []).count > 0
        }
        
        return false
    }
    
    ///
    func clearCacheFiles() {
        let resultDirectory = self.resultFileDirectory()
        
        let searchItems = try! FileManager.default.contentsOfDirectory(atPath: resultDirectory)
        for item in NSArray(array: searchItems) {
            let path = NSString(string: resultDirectory).appendingPathComponent(item as! String)
            try! FileManager.default.removeItem(atPath:  path)
        }
        
    }
}


extension AutoTest {
    func stringToImage(_ string:String) ->NSImage? {
        let length = Int(string.lengthOfBytes(using: .utf8)/2)
        
        var bytes = malloc(length)!
        
        for i in 0..<length {
            let index = i
            let subString = String(NSString(string: string).substring(with: NSMakeRange(Int(index * 2), 2)))
            bytes.storeBytes(of: UInt8(hexForString(subString)), as: UInt8.self)
        }
        
        var data = Data.init(bytes: bytes, count: length)
        let image = NSImage.init(data: data)
        
        return image
    }
    
    func hexForString(_ string:String) -> UInt8 {
        let chars = string.utf8CString
        
        if (string.lengthOfBytes(using: .utf8) >= 2) {
            return UInt8(intvalueForChar(chars[0]) * 16 + intvalueForChar(chars[1]))
        }
        
        return UInt8(0)
    }
    
    func intvalueForChar(_ char:CChar) -> UInt8 {
        let iValue = char
        
        // 0 ~ 9s
        if iValue >= 48 && iValue <= 57 {
            return UInt8(iValue - 48)
        }else if iValue >= 65 && iValue <= 70 {
            return UInt8(iValue - 65 + 10)
        }else if iValue >= 97 && iValue <= 102 {
            return UInt8(iValue - 97 + 10)
        }
        
        return UInt8(0)
    }
}