//
//  KMMergeWindowController.swift
//  PDF Reader Pro
//
//  Created by lizhe on 2023/11/8.
//

import Cocoa

typealias KMMergeWindowControllerCancelAction = (_ controller: KMMergeWindowController) -> Void
typealias KMMergeWindowControllerAddFilesAction = (_ controller: KMMergeWindowController) -> Void
typealias KMMergeWindowControllerMergeAction = (_ controller: KMMergeWindowController, _ filePath: String) -> Void
typealias KMMergeWindowControllerClearAction = (_ controller: KMMergeWindowController) -> Void

class KMMergeWindowController: KMBaseWindowController {

    @IBOutlet weak var mergeView: KMMergeView!
    
//    var cancelAction: KMMergeWindowControllerCancelAction?
    
    var oldPDFDocument: PDFDocument = PDFDocument()
    var password: String = ""
    
    var oriDucumentUrl: URL? {
        didSet {
            oldPDFDocument = PDFDocument(url: oriDucumentUrl!)!
            oldPDFDocument.unlock(withPassword: self.password)
        }
    }
    
    var type: KMMergeViewType = .add
    
    var pageIndex: Int?
    
    var mergeAction: KMMergeWindowControllerMergeAction?
//    - (id)initWithPDFDocument:(PDFDocument *)document password:(NSString *)password
//    {
//        if (self = [super initWithWindowNibName:@"KMPDFEditAppendWindow"]) {
//            
//    //        self.PDFDocument = document;
//            self.PDFDocument = [[PDFDocument alloc] init];
//            self.editType = KMPDFPageEditAppend;
//            _lockFilePathArr = [[NSMutableArray alloc] init];
//            _files = [[NSMutableArray alloc] init];
//
//            KMFileAttribute *file = [[KMFileAttribute alloc] init];
//            file.myPDFDocument = document;
//            file.filePath = document.documentURL.path;
//            file.oriFilePath = self.oriDucumentUrl.path;
//            if (password && password.length > 0) {
//                file.password = password;
//                file.isLocked = YES;
//            }
//            [self.files addObject:file];
//        }
//        return self;
//    }
    convenience init(document: PDFDocument, password: String) {
        self.init(windowNibName: "KMMergeWindowController")
        self.password = password
        
    }
    
    override func windowDidLoad() {
        super.windowDidLoad()

        self.window!.title = NSLocalizedString("Merge PDF Files", comment: "");
        // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
        self.mergeView.type = self.type
        mergeView.addFilesAction = { [unowned self] view in
            self.addFile()
        }
        
        mergeView.clearAction = { [unowned self] view in
            
        }
        
        mergeView.mergeAction = { [unowned self] view, files, size in
            mergeFiles(files: files, size: size)
        }
        
        mergeView.cancelAction = { [unowned self] view in
            cancelAction?(self)
        }
    }
}

extension KMMergeWindowController {
    func addFile() {
        var size = 0.0
        let files = self.mergeView.files
        for file in files {
            size = size + file.fileSize
        }
        
        if !IAPProductsManager.default().isAvailableAllFunction() && (files.count >= 2 || size > 20 * 1024 * 1024) {
            KMPurchaseCompareWindowController.sharedInstance().showWindow(nil)
            return
        }
        
        let openPanel = NSOpenPanel()
        openPanel.allowedFileTypes = ["pdf"]

        if KMPurchaseManager.manager.state == .subscription {
            openPanel.allowsMultipleSelection = true
            openPanel.message = NSLocalizedString("Select files to merge. To select multiple files press cmd ⌘ button on keyboard and click on the target files one by one.", comment: "")
        } else {
            openPanel.allowsMultipleSelection = false
            openPanel.message = NSLocalizedString("Select files to merge, only one file can be selected at a time.", comment: "")
        }

        openPanel.beginSheetModal(for: self.window!) { (result) in
            if result == NSApplication.ModalResponse.OK {
                var array: [URL] = []
                for fileURL in openPanel.urls {
                    array.append(fileURL)
                }
                
                let attribe = try?FileManager.default.attributesOfItem(atPath: openPanel.urls.first!.path)
                let fileSize = attribe?[FileAttributeKey.size] as? CGFloat ?? 0
                size = fileSize + size

                if !IAPProductsManager.default().isAvailableAllFunction() && (files.count >= 2 || size > 20 * 1024 * 1024) {
                    KMPurchaseCompareWindowController.sharedInstance().showWindow(nil)
                    return
                }
                
                self.mergeView.addFilePaths(urls: array)
            }
        }

    }
    
    func mergeFiles(files: [KMFileAttribute], size: CGSize = CGSizeZero) {
        var size = 0.0
        for file in files {
            size = size + file.fileSize
        }
        
        if !IAPProductsManager.default().isAvailableAllFunction() && (files.count >= 2 || size > 20 * 1024 * 1024) {
            KMPurchaseCompareWindowController.sharedInstance().showWindow(nil)
            return
        }
        
        var filesCount = 1
        if self.oriDucumentUrl != nil {
            filesCount = 0
        }
        if files.count <= filesCount {
            let alert = NSAlert.init()
            alert.alertStyle = .critical
            alert.messageText = NSLocalizedString("To start merging, please select at least 2 files.", comment: "")
            alert.runModal()
            return
        }
        
//        _isSuccessfully = NO;
//        [self.nCancelVC setEnabled:NO];
//        self.canMerge = NO;
//        
        var rootPDFOutlineArray: [PDFOutline] = []
        var allPage = true //只有是全部才支持大纲的合并
        for file in files {
            if file.fetchSelectPages().count == 0 {
                let alert = NSAlert.init()
                alert.alertStyle = .critical
                alert.messageText = "\(file.filePath.lastPathComponent) + \(NSLocalizedString("Invalid page range or the page number is out of range. Please try again.", comment: ""))"
                alert.runModal()
                return
            }
            
            allPage = file.bAllPage
            
            /*防止文件被地址变换后crash*/
            guard let tDocument = PDFDocument(url: NSURL(fileURLWithPath: file.filePath) as URL) else {
                print("文件不存在")
                let alert = NSAlert.init()
                alert.alertStyle = .critical
                alert.messageText = "\(file.filePath.lastPathComponent) + \(NSLocalizedString("Failed to merge!", comment: ""))"
                alert.runModal()
                return
            }
            
            var outlineArray: [PDFOutline] = []
            
//            if file.isLocked {
                tDocument.unlock(withPassword: file.password)
//            }
            
            if tDocument.outlineRoot != nil {
                rootPDFOutlineArray.append((tDocument.outlineRoot)!)
                self.fetchAllOfChildren((tDocument.outlineRoot)!, containerArray: &outlineArray)
                outlineArray.removeObject((tDocument.outlineRoot)!)
            } else {
                let rootOutline = PDFOutline.init()
                tDocument.outlineRoot = rootOutline
                if tDocument.outlineRoot != nil {
                    rootPDFOutlineArray.append(tDocument.outlineRoot!)
                }
            }
            
            for number in file.fetchSelectPages() {
                let page = tDocument.page(at: number - 1)
//                if pageIndex != nil {
//                    self.oldPDFDocument.insert(page!, at: pageIndex!)
//                    pageIndex = pageIndex! + 1
//                } else {
                    self.oldPDFDocument.insert(page!, at: self.oldPDFDocument.pageCount)
//                }
//                self.insertIndexSet.addIndex:(self.pdfDocument.pageCount - 1)
            }
        }
        
        
        let fileName = (files.first?.filePath.deletingPathExtension.lastPathComponent ?? "") + "_Merged"
        DispatchQueue.main.async {
            self.oldPDFDocument.outlineRoot = PDFOutline.init()
//            if allPage {
                var insertIndex = 0
                for i in 0..<rootPDFOutlineArray.count {
                    let rootOutline = rootPDFOutlineArray[i]
                    for j in 0..<rootOutline.numberOfChildren {
                        self.oldPDFDocument.outlineRoot?.insertChild(rootOutline.child(at: j)!, at: insertIndex)
                        insertIndex = insertIndex + 1
                    }
                }
                
                self.handleReDraw()
                
                if self.oriDucumentUrl != nil {
                    
                    let newPath = self.oldPDFDocument.documentURL!.path
                    var options: [PDFDocumentWriteOption : Any] = [:]
                    var success = false
                    let password = self.password
                    let pdf = self.oldPDFDocument
//                    if pdf.isEncrypted {
//                        options.updateValue(password, forKey: .userPasswordOption)
//                        options.updateValue(password, forKey: .ownerPasswordOption)
//                        success = pdf.write(toFile: newPath, withOptions: options)
//                    } else {
//                        success = pdf.write(toFile: newPath)
//                    }
                    
//                    var success = self.oldPDFDocument.write(toFile: self.oldPDFDocument.documentURL!.path)
//                    if success {
                        let savePanelAccessoryViewController = KMSavePanelAccessoryController.init()
                        let savePanel = NSSavePanel()
                        savePanel.nameFieldStringValue = fileName
                        savePanel.allowedFileTypes = ["pdf"]
                        savePanel.accessoryView = savePanelAccessoryViewController.view
        //                self.savePanelAccessoryViewController = savePanelAccessoryViewController;
                        savePanel.beginSheetModal(for: self.window!) { result in
                            if result == .OK {
                                self.cancelAction?()
                                var outputSavePanel = savePanel.url?.path ?? ""
                                DispatchQueue.main.async {
                                    var success = false
                                    if pdf.isEncrypted {
                                        options.updateValue(password, forKey: .userPasswordOption)
                                        options.updateValue(password, forKey: .ownerPasswordOption)
                                        success = pdf.write(toFile: outputSavePanel, withOptions: options)
                                    } else {
                                        success = pdf.write(toFile: outputSavePanel)
                                    }
                                    
                                    if success {
                                        if savePanelAccessoryViewController.needOpen {
                                            NSDocumentController.shared.openDocument(withContentsOf: savePanel.url!, display: true) { document, open, error in
                                                
                                            }
                                        } else {
                                            NSWorkspace.shared.activateFileViewerSelecting([NSURL(fileURLWithPath: outputSavePanel) as URL])
                                        }
                                    } else {
                                        let alert = NSAlert.init()
                                        alert.alertStyle = .critical
                                        alert.messageText = "\(String(describing: files.first?.filePath.lastPathComponent)) + \(NSLocalizedString("Failed to merge!", comment: ""))"
                                        alert.runModal()
                                    }
                                }
                            }
                        }
//                        self.mergeAction?(self, self.oldPDFDocument.documentURL!.path)
//                    } else {
//                        print("合并失败")
//                    }
                } else {
                    let savePanelAccessoryViewController = KMSavePanelAccessoryController.init()
                    let savePanel = NSSavePanel()
                    savePanel.nameFieldStringValue = fileName
                    savePanel.allowedFileTypes = ["pdf"]
                    savePanel.accessoryView = savePanelAccessoryViewController.view
    //                self.savePanelAccessoryViewController = savePanelAccessoryViewController;
                    savePanel.beginSheetModal(for: self.window!) { result in
                        if result == .OK {
                            self.cancelAction?()
                            var outputSavePanel = savePanel.url?.path
                            DispatchQueue.main.async {
                                var success = self.oldPDFDocument.write(toFile: outputSavePanel!)
                                if !success {
                                    success = ((try?self.oldPDFDocument.dataRepresentation()?.write(to: URL(string: outputSavePanel!)!)) != nil)
                                }
                                
                                if success {
                                    if savePanelAccessoryViewController.needOpen {
                                        NSDocumentController.shared.openDocument(withContentsOf: savePanel.url!, display: true) { document, open, error in
                                            
                                        }
                                    } else {
                                        NSWorkspace.shared.activateFileViewerSelecting([NSURL(fileURLWithPath: outputSavePanel!) as URL])
                                    }
                                } else {
                                    let alert = NSAlert.init()
                                    alert.alertStyle = .critical
                                    alert.messageText = "\(String(describing: files.first?.filePath.lastPathComponent)) + \(NSLocalizedString("Failed to merge!", comment: ""))"
                                    alert.runModal()
                                }
                            }
                        }
                    }
                }
//            }
        }
    }
    
    func fetchAllOfChildren(_ aOutline: PDFOutline, containerArray aMArray: inout [PDFOutline]) {
        if !aMArray.contains(aOutline) {
            aMArray.append(aOutline)
        }
        
        for i in 0..<aOutline.numberOfChildren {
            if let childOutline = aOutline.child(at: i) {
                aMArray.append(childOutline)
                fetchAllOfChildren(childOutline, containerArray: &aMArray)
            }
        }
    }
    
    func handleReDraw() {
        if mergeView.originalSizeButton.state == .on {
            
        } else {
            let size = self.mergeView.newPageSize
            if size.width < 0 {
                return
            }
            
            var pagesArray: [PDFPage] = []
            let pageCount = self.oldPDFDocument.pageCount
            for i in 0..<pageCount {
                pagesArray.append(self.oldPDFDocument.page(at: 0)!)
                self.oldPDFDocument.removePage(at: 0)
            }
            
            for i in 0..<pageCount {
                let page: KMMergePDFPage = KMMergePDFPage.init()
                page.setBounds(CGRectMake(0, 0, size.width, size.height), for: .mediaBox)
                page.drawingPage = pagesArray[i]
                self.oldPDFDocument.insert(page, at: i)
            }
            
            if self.oldPDFDocument.outlineRoot != nil {
                let childCount = self.oldPDFDocument.outlineRoot?.numberOfChildren
                var outlineArray: [PDFOutline] = []
                
                for i in 0..<childCount! {
                    outlineArray.append((self.oldPDFDocument.outlineRoot?.child(at: i))!)
                }
                
                for outline in outlineArray {
                    outline.removeFromParent()
                }
            }
        }
    }
}

class KMMergePDFPage: PDFPage {
    var drawingPage: PDFPage?
    
    override func draw(with box: PDFDisplayBox, to context: CGContext) {
        super.draw(with: box, to: context)
        
        let pageSize = self.bounds(for: .cropBox).size
        self.drawPage(with: context, page: self.drawingPage!, pageSize: pageSize)
    }
    
    func drawPage(with context: CGContext, page: PDFPage, pageSize: CGSize) {
        var originalSize = page.bounds(for: .cropBox).size
        
        // 如果页面的旋转角度为90或者270,宽高交换
        if page.rotation % 180 != 0 {
            originalSize = CGSize(width: originalSize.height, height: originalSize.width)
        }
        
        let wRatio = pageSize.width / originalSize.width
        let hRatio = pageSize.height / originalSize.height
        let ratio = min(wRatio, hRatio)
        
        context.saveGState()
        let xTransform = (pageSize.width - originalSize.width * ratio) / 2
        let yTransform = (pageSize.height - originalSize.height * ratio) / 2
        context.translateBy(x: xTransform, y: yTransform)
        context.scaleBy(x: ratio, y: ratio)
        
        if #available(macOS 10.12, *) {
            page.draw(with: .cropBox, to: context)
            page.transformContext(for: .cropBox)
        } else {
            NSGraphicsContext.saveGraphicsState()
            NSGraphicsContext.current = NSGraphicsContext(cgContext: context, flipped: false)
            page.draw(with: .cropBox)
            NSGraphicsContext.restoreGraphicsState()
            page.transformContext(for: .cropBox)
        }
        
        context.restoreGState()
    }
}