//
//  KMPDFConvert.swift
//  PDF Master
//
//  Created by tangchao on 2022/12/7.
//

import Cocoa
import PDFKit
import ComPDFKit_Conversion

let KMPDFConvertOptionsKeyImageDPI = "KMPDFConvertOptionsKeyImageDPI"
let KMPDFConvertOptionsKeyImageWithAnnotation = "KMPDFConvertOptionsKeyImageWithAnnotation"
 
enum KMPDFConvertType: Int {
    case word = 0
    case excel = 1
    case ppt = 2
    case rtf = 3
    case csv = 4
    case html = 5
    case text = 6
    case jpeg = 7
    case jpg = 8
    case png = 9
    case gif = 10
    case tiff = 11
    case tga = 12
    case bmp = 13
    case jp2 = 14
    
    static let image: KMPDFConvertType = .jpeg
}

typealias KMPDFConvertCallback = (_ finished: Bool, _ error: Error?) -> ()
typealias KMPDFConvertProgress = (Int) -> ()
class KMPDFConvert: Operation {
    var type: Int = 0
    var filePath: String = ""
    var password: String = ""
    var outputFileName: String = ""
    var outputFolderPath: String = ""
    var pages: [Int]!
    var convertType: KMPDFConvertType = .word
    var options: [String:Any]!
    var outputFilePath: String = ""
    var isSuccessful: Bool = false
    var isAllInOneSheet: Bool = false
    var isExtractTable: Bool = false
    var isExtractText: Bool = false
    /**
        0   支持一个表格提取到单独的工作表
        1  支持按页面提取表格到单独的工作表
        2  支持将所有表格提取到一个工作表
     */
    var extractTableIndex: Int = 0
    var errorInfo: Error!
    
    private var pathExtension: String = ""
    private var fpPDFConverter: CPDFConverterFP!
    private var converter: CPDFConverter!
    private var isCompletion: Bool = false
    
    var callback: KMPDFConvertCallback!
    var progress: KMPDFConvertProgress?
    
    public class func pathExtension(_ type: KMPDFConvertType) -> String {
        return self.pathExtension(type, nil)
    }
    
    public class func pathExtension(_ type: KMPDFConvertType, _ isExtractTable: Bool?) -> String {
        if type == .word {
            return "docx"
        } else if type == .excel {
            return "xlsx"
        } else if type == .ppt {
            return "pptx"
        } else if type == .rtf {
            return "rtf"
        } else if type == .csv {
            if isExtractTable != nil && isExtractTable! {
                return "zip"
            }
            return "csv"
        } else if type == .html {
            return "html"
        } else if type == .text {
            return "txt"
        } else if type == .jpeg {
            return "jpeg"
        } else if type == .jpg {
            return "jpg"
        } else if type == .png {
            return "png"
        } else if type == .gif {
            return "gif"
        } else if type == .tga {
            return "tga"
        } else if type == .bmp {
            return "bmp"
        } else if type == .jp2 {
            return "jp2"
        } else if type == .tiff {
            return "tiff"
        }
        return ""
    }
    
    override func start() {
        if isCancelled {
            return
        }
        
        var pathExtension = KMPDFConvert.pathExtension(self.convertType, self.isExtractTable)
        var fileName = outputFileName
        var path = outputFolderPath
        
        
        if convertType == .jpeg || convertType == .jpg || convertType == .png || convertType == .gif || convertType == .tga || convertType == .bmp || convertType == .jp2 || convertType == .tiff {
            path.append("/")
            path.append(fileName)
//            let folderPath = getUniqueFilePath(filePath: path)
            try?FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: false)
            outputFilePath = path
        } else {
            if !pathExtension.isEmpty {
                fileName.append(".")
                fileName.append(pathExtension)
                
                path.append("/")
                path.append(fileName)
//                let folderPath = getUniqueFilePath(filePath: path)
                
                outputFilePath = path
            } else {
                outputFolderPath.append("/")
                outputFolderPath.append(outputFileName)
                outputFilePath = outputFolderPath
            }
        }
        
        self.pathExtension = pathExtension
        
        convertWithFPPDFConverter()
    }
    
    func getUniqueFilePath(filePath: String) -> String {
        var i: Int = 0
        var isDirectory: ObjCBool = false
        var uniqueFilePath = filePath
        let fileManager = FileManager.default
        fileManager.fileExists(atPath: uniqueFilePath, isDirectory: &isDirectory)
        if isDirectory.boolValue {
            var path: String = ""
            while fileManager.fileExists(atPath: uniqueFilePath) {
                i += 1
                
                path = filePath
                path.append("(\(i))")
                uniqueFilePath = path
            }
        } else {
            let fileURL = URL(fileURLWithPath: filePath)
            var path: String = ""
            while fileManager.fileExists(atPath: uniqueFilePath) {
                i += 1
                
                path = fileURL.deletingPathExtension().path
                path.append("(\(i))")
                path.append(".")
                path.append(fileURL.pathExtension)
                uniqueFilePath = path
            }
        }
        return uniqueFilePath
    }
    
    func convertWithFPPDFConverter() {
        if pathExtension.isEmpty {
            convertSuccessful(isSuccessful: false, errorInfo: nil)
            return
        }
        
        if convertType == .word && isAllInOneSheet {
            converter = CPDFConverterWord(url: URL(fileURLWithPath: filePath), password: self.password)
            converter.delegate = self
            converter.convert(toFilePath: outputFilePath, pageIndexs: pages, options: nil)
            return
        }
        
        if convertType == .excel {
            converter = CPDFConverterExcel(url: URL(fileURLWithPath: filePath), password: self.password)
            converter.delegate = self

            let options = CPDFConvertExcelOptions()
            if (isExtractText) {
                options.contentOptions = .onlyText
            } else if (isExtractTable) {
                options.contentOptions = .onlyTable
                if (extractTableIndex == 0) {
                    options.worksheetOptions = .forEachTable
                } else if (extractTableIndex == 1) {
                    options.worksheetOptions = .forEachPage
                } else if (extractTableIndex == 2) {
                    options.worksheetOptions = .forTheDocument
                }
            } else {
                options.contentOptions = .allContent
                if (isAllInOneSheet) {
                    options.worksheetOptions = .forTheDocument
                } else {
                    options.worksheetOptions = .forEachPage
                }
            }
            
            converter.convert(toFilePath: outputFilePath, pageIndexs: pages, options: options)
            return
        }
        
        if (convertType == .ppt) {
            converter = CPDFConverterPPT(url: URL(fileURLWithPath: filePath), password: self.password)
            converter.delegate = self
            converter.convert(toFilePath: outputFilePath, pageIndexs: pages, options: nil)
            return
        }
        
        if (convertType == .text) {
            converter = CPDFConverterTxt(url: URL(fileURLWithPath: filePath), password: self.password)
            converter.delegate = self
            converter.convert(toFilePath: outputFilePath, pageIndexs: pages, options: nil)
            return
        }
        
        if convertType == .csv && isExtractTable {
            converter = CPDFConverterCsv(url: URL(fileURLWithPath: filePath), password: self.password)
            converter.delegate = self
            converter.convert(toFilePath: outputFilePath, pageIndexs: pages, options: nil)
            return
        }
        
//        if (convertType == .jpeg || convertType == .png) {
//            converter = CPDFConverterImg(url: URL(fileURLWithPath: filePath), password: nil)
//            converter.delegate = self
//            let options = CPDFConvertImgOptions()
//            if (convertType == .jpeg) {
//                options.type = .JPEG
//            } else if (convertType == .png) {
//                options.type = .PNG
//            }
            
//            converter.convert(toFilePath: outputFilePath, pageIndexs: pages, options: options)
//            return
//        }
        
        fpPDFConverter = CPDFConverterFP()
        fpPDFConverter.setDelegate(self)
        var dpi: Int = 0
        if self.options != nil {
            dpi = self.options[KMPDFConvertOptionsKeyImageDPI] as! Int
        }
        let options: [String:Any] = [CPDFConvertOptionsKey.imageDPI.rawValue:dpi,CPDFConvertOptionsKey.allInOneSheet.rawValue:isAllInOneSheet]
        fpPDFConverter.convertPDF(atPath: filePath, pdfPassword: self.password, pdfPageIndexs: pages, destDocType: pathExtension, destDocPath: outputFilePath, moreOptions: options)
    }
    
    func convertSuccessful(isSuccessful: Bool, errorInfo: Error!) {
        self.isSuccessful = isSuccessful
        self.errorInfo = errorInfo
        
        DispatchQueue.main.async { [self] in
            
            guard let callbackBlock = callback else {
                return
            }
            
            callbackBlock(isSuccessful, errorInfo)
        }

        willChangeValue(forKey: "isFinished")
        isCompletion = true
        didChangeValue(forKey: "isFinished")
    }
    
    override var isFinished: Bool {
        return self.isCompletion
    }
}

extension KMPDFConvert: CPDFConverterDelegate {
    func converter(_ converter: CPDFConverter!, didStartConvert error: Error!) {
        
    }
    
    func converter(_ converter: CPDFConverter!, didEndConvert error: Error!) {
        if (error != nil) {
            convertSuccessful(isSuccessful: false, errorInfo: error)
        } else {
            convertSuccessful(isSuccessful: true, errorInfo: error)
        }
    }
    
    func converter(_ converter: CPDFConverter!, pageIndex index: UInt, pageCount count: UInt) {
        guard let callback = progress else {
            return
        }
        
        callback(Int(index))
    }
}

extension KMPDFConvert: CPDFConverterFPDelegate {
    func fppdfConverter(_ converter: Any!, didEndConversion error: Error!) {
        if (error != nil) {
            convertSuccessful(isSuccessful: false, errorInfo: error)
        } else {
            convertSuccessful(isSuccessful: true, errorInfo: error)
        }
    }
    
    func fppdfConverter(_ converter: Any!, convertPDFPageIndex pdfPageIndexA: UInt, writeWordPageIndex wordPageIndexA: UInt, finshedWordPageCount wordPageCountA: UInt) {
        guard let callback = progress else {
            return
        }
        
        callback(Int(wordPageIndexA))
    }
}