import Foundation
import PrinterSDK

typealias ReturnReult = Result<Void, Error>
typealias ReturnBlock = ((Result<Void, Error>) -> ())

class Printer: NSObject {
    
    private static var nextId = 1
    
    let ipAddress: String
    private let printWidth: CGFloat
    private let command = PTCommandESC()
    private var resolver: ReturnBlock?
    private let id: Int
    
    static func printWidthFor(model: String) -> CGFloat {
        
        switch model {
            case "TP80BE":
                return 576
            default:
                return 576
        }
    }
    
    init(model: String, ipAddress: String) {
        
        Printer.log(id: Printer.nextId, "Init Printer")
        
        self.ipAddress = ipAddress
        
        printWidth = Printer.printWidthFor(model: model)
        
        id = Printer.nextId
        Printer.nextId += 1
    }
    
    func add(image: UIImage) {
        
        let resizedImage = PDResetImage.scaleImageForWidth(image: image, width: printWidth)
        
        command.appendRasterImage(resizedImage?.cgImage, mode: PTBitmapMode.binary, compress: PTBitmapCompressMode.none, package: false)
    }
    
    func addOpenDrawer() {
        /// open drawer /// 0~2
        command.kickCashdrawer(2)
    }
    
    func addCut() {
        command.setPartialCutWithDistance(0)
    }
    
    /// Print completion trigger PTDispatch `whenESCPrintSuccess` callback
    func addRequestPrintSuccessCallback() {
        command.escRequestTransmissionOfResponseOrStatus()
    }
    
    func sendData(completion: @escaping ReturnBlock) {
        
        Printer.log(id: id, "\(ipAddress): Connecting")
        
        let printer = PTPrinter()
        printer.ip = ipAddress
        printer.module = .wiFi
        printer.port = "9100"
        
        self.resolver = completion
        
        PTDispatcher.share().whenConnectSuccess {
            self.sendCommand()
        }
        
        PTDispatcher.share().whenConnectFailureWithErrorBlock { (error) in
            self.handleConnectError(error)
        }
        
        PTDispatcher.share().connect(printer)
    }
    
    func handleConnectError(_ error: PTConnectError) {
        
        Printer.log(id: id, "\(ipAddress): connect error: \(error)")
        
        PTDispatcher.share().disconnect()
        
        switch error {
            case .streamTimeout:
                resolver?(.failure((PrinterManager.PrintError.ConnectErrorTimeout)))
            case .streamEmpty:
                resolver?(.failure((PrinterManager.PrintError.ConnectErrorSocket)))
            case .streamOccured:
                resolver?(.failure((PrinterManager.PrintError.ConnectErrorSocket)))
            default:
                resolver?(.failure((PrinterManager.PrintError.ConnectErrorUnkown)))
        }
        
    }
    
    func sendCommand() {
        
        Printer.log(id: id, "\(ipAddress): connect success")
        
        PTDispatcher.share().send(command.getCommandData())
        
        PTDispatcher.share()?.whenSendFailure {
            
            Printer.log(id: self.id, "\(self.ipAddress): send failure")
            PTDispatcher.share()?.disconnect()
            
            self.resolver?(.failure((PrinterManager.PrintError.SendDataError)))
        }
        
        
        PTDispatcher.share().whenESCPrintSuccess { _ in
            
            Printer.log(id: self.id, "\(self.ipAddress): print success")
            self.resolver?(.success(()))
        }
        
    }
    
    deinit {
        Printer.log(id: id, "deinit printer")
    }
    
    static func log(id: Int, _ logString: String, functionName: StaticString = #function, fileName: StaticString = #file, lineNumber: Int = #line) {
        
//        print("[\(fileName)] [\(functionName)] [\(lineNumber)] [\(id)]: " + logString)
    }
}
