import Cocoa

class TestMainViewController: NSViewController {
    private let posPrinter = MPosControllerPrinter()
    private let labelPrinter = MPosControllerLabelPrinter()
    private let dotPrinter = MPosControllerDotPrinter()
    private let mPosConfig = MPosControllerConfig()
    private let barcodeScanner = MPosControllerScanner()
    private let usbToSerialDevice = MPosControllerUSBSerial()
    private let customerDisplay = MPosControllerBCD()
    @IBOutlet weak var svHistory: NSScrollView!
    @IBOutlet weak var tfAddress: NSTextField!
    @IBOutlet weak var tfDeviceId: NSTextField!
    @IBOutlet weak var radioBgate: NSButton!
    @IBOutlet weak var radioDirect: NSButton!
    @IBOutlet weak var btnNetConfig: NSButton!
    var tvHistory: NSTextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        initTextView()
        
        print("Bixolon SDK Version: \(MPosLibVersion.getInfo())")
        let notiCenter = NotificationCenter.default
        notiCenter.addObserver(self, selector: #selector(self.deviceNotificationHandler), name: NSNotification.Name(rawValue: "DataEvent"), object: nil)
        notiCenter.addObserver(self, selector: #selector(self.deviceNotificationHandler), name: NSNotification.Name(rawValue: "StatusUpdateEvent"), object: nil)
        notiCenter.addObserver(self, selector: #selector(self.deviceNotificationHandler), name: NSNotification.Name(rawValue: "OutputCompletedEvent"), object: nil)
        
        // Whether create logs or not
        MPosControllerDevices.setEnableLog(true, saveFile: true, saveToHex: true)
        
        radioBgate.state = .on
        onDeviceIdInit(self)
        toggleMethod(self)
        btnNetConfig.isHidden = true
        
        NSApplication.shared.windows.first?.delegate = self
        // Do any additional setup after loading the view.
    }
    
    @IBAction func toggleMethod(_ sender: Any) {
        let ad = NSApplication.shared.delegate as! AppDelegate
        switch(radioBgate.state) {
        case .on:
            ad.connectionViaBgate = true;
            radioDirect.state = .off ;
        case .off:
            ad.connectionViaBgate = false;
            radioDirect.state = .on;
            break;
        default:
            break
        }
    }
    
    func initTextView() {
        let contentSize = svHistory.contentSize
        
        tvHistory = NSTextView(frame:NSMakeRect(0, 0, contentSize.width, contentSize.height))
        tvHistory.minSize = NSMakeSize(0.0, contentSize.height)
        tvHistory.maxSize = NSMakeSize(CGFloat(Float.greatestFiniteMagnitude), CGFloat(Float.greatestFiniteMagnitude));
        tvHistory.isVerticallyResizable = true
        tvHistory.isHorizontallyResizable = true
        tvHistory.isEditable = false
        
        tvHistory.textContainer?.containerSize = NSMakeSize(contentSize.width, CGFloat(Float.greatestFiniteMagnitude))
        tvHistory.textContainer?.widthTracksTextView = true
        
        svHistory.documentView = tvHistory
    }
    
    func addHistory(text:String) {
        DispatchQueue.main.async{ [weak self] in
            guard let strongSelf = self else { return }
            strongSelf.tvHistory?.string = strongSelf.tvHistory.string.appending(text.appending("\r\n"))
            strongSelf.tvHistory?.scrollRangeToVisible(NSMakeRange(strongSelf.tvHistory.string.count - 1, 1))
        }
    }
    
    func clearHistory() {
        tvHistory.string = "";
    }
    
    func updateUIInformation() {
        let ad:AppDelegate = NSApplication.shared.delegate as! AppDelegate
        tfAddress.stringValue = ad.connectionString == "" ? "Not Assigned" : ad.connectionString
        tfDeviceId.stringValue = String(format: "%@", ad.deviceId == -1 ? "Not Assgined" : String(format: "%ld", ad.deviceId))
        btnNetConfig.isHidden = ad.networkDevice == nil
    }
    
    func updatePosPrinterStatusText(status:Int){
        DispatchQueue.global(qos: .background).async {
            DispatchQueue.main.async { [weak self] in
                guard let strongSelf = self else { return }
                var message = ""
                if(status == 0){
                    message = "| NORMAL"
                }else{
                    if(MPOS_STATUS.POS_STATUS_COVEROPEN.rawValue & status != 0) {message += " | COVER_OPEN"}
                    if(MPOS_STATUS.POS_STATUS_PAPEREMPTY.rawValue & status != 0) {message += " | PAPER_EMPTY"}
                    if(MPOS_STATUS.POS_STATUS_PAPER_NEAREND.rawValue & status != 0) {message += " | PAPER_NEAREND"}
                    if(MPOS_STATUS.POS_STATUS_ERROR.rawValue & status != 0) {message += " | ERROR"}
                    if(MPOS_STATUS.POS_STATUS_BATT_LOW.rawValue & status != 0) {message += " | BATTERY_LOW"}
                    if(MPOS_STATUS.POS_STATUS_DRAWER_HIGH.rawValue & status != 0) {message += " | CD_HIGH"}
                    if(MPOS_STATUS.POS_STATUS_DRAWER_LOW.rawValue & status != 0) {message += " | CD_LOW"}
                    if(MPOS_STATUS.POS_STATUS_OFFLINE.rawValue & status != 0)    {message += " | OFFLINE" }
                }
                message += " |"
                strongSelf.addHistory(text: "POS Printer Status: \(message)")
            }
        }
    }
    
    @IBAction func onDeviceIdInit(_ sender: Any) {
        let ad = NSApplication.shared.delegate as! AppDelegate
        ad.deviceId = -1;
        tfDeviceId.stringValue = String(format: "%@", ad.deviceId == -1 ? "Not Assgined" : String(format: "%ld", ad.deviceId))
    }
    
    @IBAction func onAddressLookup(_ sender: Any) {
        guard let vc = self.storyboard?.instantiateController(withIdentifier: "DevListTableViewController") as? NSViewController else { return }
        self.presentAsSheet(vc)
    }
    
    @IBAction func onDeviceIdLookup(_ sender: Any) {
        guard let vc = self.storyboard?.instantiateController(withIdentifier: "DeviceIdTableViewController") as? NSViewController else { return }
        self.presentAsSheet(vc)
    }
    
    @IBAction func onNetConfigure(_ sender: Any) {
        guard let vc = self.storyboard?.instantiateController(withIdentifier: "NetConfigurationViewController") as? NSViewController else { return }
        self.presentAsSheet(vc)
    }
    
    func showMessageBox(message:String, style:NSAlert.Style) {
        let alert = NSAlert()
        alert.alertStyle = style
        alert.messageText = message
        alert.runModal()
    }
    
    @IBAction func openPosPrinter(_ sender: Any) {
        if(self.posPrinter.isOpen()){ return }
        let ad = NSApplication.shared.delegate as! AppDelegate
        posPrinter.selectCommand(ad.connectionViaBgate ? MPOS_COMMAND_MODE.MODE_BGATE : MPOS_COMMAND_MODE.MODE_DIRECT)
        posPrinter.selectInterface(MPOS_INTERFACE_TYPE(rawValue: ad.conntectionType)!, address: ad.connectionString)
        if posPrinter.openService(ad.deviceId, timeout: 10 /*seconds*/).rawValue != MPOS_RESULT.RES_SUCCESS.rawValue {
            showMessageBox(message: "openService failed", style: .critical)
            return
        }
        posPrinter.getModelName({ [weak self] (modelName: String?) in
            self?.addHistory(text: "Device Name: \(modelName!)")
        })
        
        posPrinter.asbEnable(true)
        showMessageBox(message: "OPEN SUCCESS", style: .informational)
        addHistory(text: "OPEN SUCCESS")
    }
    
    @IBAction func closePosPrinter(_ sender: Any) {
        posPrinter.closeService(1 /*seconds*/)
        addHistory(text: "CLOSED")
    }
    
    @IBAction func printWithLineMode(_ sender: Any) {
        if(!self.posPrinter.isOpen()){ return }
        posPrinter.setTransaction(MPOS_TRANSACTION.IN.rawValue)
        posPrinter.openDrawer(MPOS_CASHDRAWER_PIN.POS_DRAWER_PIN2.rawValue)
        posPrinter.printText("Default\n", fontType:0, fontWidth:0, fontHeight:0, bold:0, underline:0, reverse:0, alignment:-1)
        posPrinter.printText("Bold+Double\n", fontType:0, fontWidth:1, fontHeight:1, bold:1, underline:0, reverse:0, alignment:-1)
        posPrinter.printText("Underline in 2 dots\n", fontType:0, fontWidth:0, fontHeight:0, bold:0, underline:2, reverse:0, alignment:-1)
        posPrinter.print1DBarcode("{C1234567890", symbology: 110 /*Code128*/, width: 2, height: 50, alignment: -1, textPosition: 2)
        posPrinter.printImage(NSImage(named: "bixolon.jpg"), width: 384, alignment: 1, threshold: 128, ditheringType: 1, compressType: 1)
        posPrinter.cutPaper(MPOS_PRINTER_CUTTYPE.POS_FEED_PARTIAL_CUT.rawValue)
        posPrinter.setTransaction(MPOS_TRANSACTION.OUT.rawValue)
    }
    
    @IBAction func printWithPageMode(_ sender: Any) {
        if(!self.posPrinter.isOpen()){ return }
        
        var yPosition : Int = 30
        posPrinter.setPagemode(MPOS_PRINTER_PAGEMODE.POS_PAGEMODE_IN.rawValue)
        posPrinter.setPagemodePrintArea(0, y: 0, width: 384 /* dot */, height: 600 /* dot */)
        posPrinter.setPagemodeDirection(MPOS_PRINTER_PD.POS_DIRECTION_RIGHT90.rawValue);
        posPrinter.printText("PAGE MODE #1");
        yPosition += 30;
        posPrinter.setPagemodePosition(30, y: yPosition)
        posPrinter.printText("PAGE MODE #2");
        yPosition += 60;
        posPrinter.setPagemodePosition(30, y: yPosition)
        posPrinter.print1DBarcode("{C1234567890", symbology: 110 /*Code128*/, width: 2, height: 50, alignment: -1, textPosition: 0)
        posPrinter.setPagemode(MPOS_PRINTER_PAGEMODE.POS_PAGEMODE_OUT.rawValue)
        posPrinter.cutPaper(MPOS_PRINTER_CUTTYPE.POS_FEED_PARTIAL_CUT.rawValue)
    }
    
    @IBAction func getCurrentPosPrinterStatus(_ sender: Any) {
        DispatchQueue.global(qos: .background).async { [weak self] in
            guard let strongSelf = self else { return }
            strongSelf.posPrinter.checkStatus({ (status:Int) in
                strongSelf.updatePosPrinterStatusText(status: status)
            })
        }
    }
    
    @IBAction func openLabelPrinter(_ sender: Any) {
        if(self.labelPrinter.isOpen()){ return }
        let ad = NSApplication.shared.delegate as! AppDelegate
        let commandMode = ad.connectionViaBgate ? MPOS_COMMAND_MODE.MODE_BGATE : MPOS_COMMAND_MODE.MODE_DIRECT
        labelPrinter.selectCommand(commandMode)
        labelPrinter.selectInterface(MPOS_INTERFACE_TYPE(rawValue: ad.conntectionType)!, address: ad.connectionString)
        if labelPrinter.openService(ad.deviceId, timeout: 10 /*seconds*/).rawValue != MPOS_RESULT.RES_SUCCESS.rawValue {
            showMessageBox(message: "openService failed", style: .critical)
            return
        }
        labelPrinter.getModelName({ [weak self] (modelName: String?) in
            self?.addHistory(text: "Device Name: \(modelName!)")
        })
        showMessageBox(message: "OPEN SUCCESS", style: .informational)
        addHistory(text: "OPEN SUCCESS")
    }
    
    @IBAction func closeLabelPrinter(_ sender: Any) {
        labelPrinter.closeService(3/*seconds*/)
        addHistory(text: "CLOSED")
    }
    
    @IBAction func printLabel(_ sender: Any) {
        if(!self.labelPrinter.isOpen()){ return }
        let dotsPerUnit : Double = Double(labelPrinter.getPrinterDPI()) / 25.4
        labelPrinter.setTransaction(MPOS_TRANSACTION.IN.rawValue)
        if let speeds = labelPrinter.getSupportedSpeeds(), speeds.count > 0 {
            labelPrinter.setSpeed(speeds.first!.intValue /*Max Speed*/)
        }
        labelPrinter.setDensity(10)
        labelPrinter.setWidth(Int(dotsPerUnit * 52))
        labelPrinter.setLength(Int(dotsPerUnit * 75), gapLength: Int(dotsPerUnit * 3), mediaType: MPOS_LABEL_MEDIA_TYPE.LABEL_MEDIA_TYPE_GAP.rawValue, offsetLength: 0)
        labelPrinter.drawBlock(0, startPosY: 0, endPosX: Int(dotsPerUnit * 52), endPosY: Int(dotsPerUnit * 73), option: Int8(0x42/*B*/), thickness: Int(dotsPerUnit * 2))
        labelPrinter.setCharacterset(MPOS_LABEL_CONFIG_CODEPAGE.LABEL_CODEPAGE_CP437.rawValue, internationalCharset: MPOS_LABEL_CONFIG_ICS.LABEL_ICS_USA.rawValue)
        labelPrinter.drawTextDeviceFont("Device Font", xPos: 50, yPos: 30, fontSelection: MPOS_LABEL_DEVICE_FONT.LABEL_DEVICE_FONT_15PT.rawValue, fontWidth: 1, fontHeight: 1, rightSpace: 0, rotation: 0, reverse: false, bold: false, rightToLeft: false, alignment: 0)
        labelPrinter.drawTextVectorFont("Vector Font", xPos: 50, yPos: 100, fontSelection: MPOS_LABEL_VECTOR_FONT.LABEL_VECTOR_FONT_ASCII.rawValue, fontWidth: 30, fontHeight: 30, rightSpace: 0, rotation: 0, reverse: false, bold: false, italic: false, rightToLeft: false, alignment: 0)
        labelPrinter.drawBarcode1D("0987654321", xPos: 300, yPos: 150, barcodeType: MPOS_LABEL_BARCODE_TYPE.LABEL_BARCODE_CODE128.rawValue, widthNarrow: 2, widthWide: 2, height: 128, hri: MPOS_LABEL_BARCODE_HRI.LABEL_BARCODE_TEXT_BELOW3.rawValue, quietZoneWidth: 0, rotation: 1)
        labelPrinter.drawImage(NSImage(named: "bixolon.jpg"), xPos: 40, yPos: 370, width: 300, threshold: 128, ditheringType: 1, compressType: 1)
        labelPrinter.printBuffer(1/* Number of copies */)
        labelPrinter.setTransaction(MPOS_TRANSACTION.OUT.rawValue)
    }
    
    @IBAction func getCurrentLabelPrinterStatus(_ sender: Any) {
        DispatchQueue.global(qos: .background).async { [weak self] in
            guard let strongSelf = self else { return }
            var message = ""
            strongSelf.labelPrinter.checkStatus({ (printerStatus:Int) in
                DispatchQueue.main.async {
                    if(printerStatus == 0){
                        message = "NORMAL"
                    }else{
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_COVEROPEN.rawValue & printerStatus != 0) {message += " COVER_OPEN"}
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_PAPEREMPTY.rawValue & printerStatus != 0) {message += " PAPER_EMPTY"}
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_CUTTER_JAMMED.rawValue & printerStatus != 0) {message += " CUTTER_JAM"}
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_TPH_OVERHEAT.rawValue & printerStatus != 0) {message += " TPH_OVERHEAT"}
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_GAP_ERROR.rawValue & printerStatus != 0) {message += " GAP_ERROR"}
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_RIBBON_END.rawValue & printerStatus != 0) {message += " RIBBON END"}
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_ON_BUILDING_LABEL.rawValue & printerStatus != 0) {message += " BUILDING LEBEL"}
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_ON_PRINTING_LABEL.rawValue & printerStatus != 0) {message += " PRINTING LABEL"}
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_ISSUED_LABEL_PAUSED.rawValue & printerStatus != 0) {message += " LABEL PAUSED"}
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_MOTOR_OVERHEAT.rawValue & printerStatus != 0) {message += " MOTOR_OVERHEAT"}
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_BOARD_OVERHEAT.rawValue & printerStatus != 0) {message += " BOARD_OVERHEAT"}
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_WAIT_LABEL_TAKEN.rawValue & printerStatus != 0) {message += " WAIT_FOR_LABEL_TO_BE_TAKEN"}
                        if(MPOS_LABEL_STATUS.LABEL_STATUS_PRINTER_OFFLINE.rawValue & printerStatus != 0) {message += " LABEL_STATUS_PRINTER_OFFLINE"}
                    }
                    strongSelf.addHistory(text: "Label Printer Status: \(message)")
                }
            })
        }
    }
    
    @IBAction func openBarcodeScanner(_ sender: Any) {
        let ad = NSApplication.shared.delegate as! AppDelegate
        barcodeScanner.selectCommand(ad.connectionViaBgate ? MPOS_COMMAND_MODE.MODE_BGATE : MPOS_COMMAND_MODE.MODE_DIRECT)
        barcodeScanner.selectInterface(MPOS_INTERFACE_TYPE(rawValue: ad.conntectionType)!, address: ad.connectionString)
        if barcodeScanner.openService(ad.deviceId).rawValue != MPOS_RESULT.RES_SUCCESS.rawValue {
            showMessageBox(message: "openService failed", style: .critical)
            return
        }
        showMessageBox(message: "OPEN SUCCESS", style: .informational)
        addHistory(text: "OPEN SUCCESS")
    }
    
    @IBAction func closeBarcodeScanner(_ sender: Any) {
        barcodeScanner.closeService(0)
        addHistory(text: "CLOSED")
    }
    
    @IBAction func openUSBSerialDevice(_ sender: Any) {
        if(self.usbToSerialDevice.isOpen()){ return }
        let ad = NSApplication.shared.delegate as! AppDelegate
        usbToSerialDevice.selectCommand(ad.connectionViaBgate ? MPOS_COMMAND_MODE.MODE_BGATE : MPOS_COMMAND_MODE.MODE_DIRECT)
        usbToSerialDevice.selectInterface(MPOS_INTERFACE_TYPE(rawValue: ad.conntectionType)!, address: ad.connectionString)
        if usbToSerialDevice.openService(ad.deviceId).rawValue != MPOS_RESULT.RES_SUCCESS.rawValue {
            showMessageBox(message: "openService failed", style: .critical)
            return
        }
        showMessageBox(message: "OPEN SUCCESS", style: .informational)
    }
    
    @IBAction func closeUSBSerialDevice(_ sender: Any) {
        usbToSerialDevice.closeService(0)
        addHistory(text: "CLOSED")
    }
    
    @IBAction func openCustomerDisplay(_ sender: Any) {
        let ad = NSApplication.shared.delegate as! AppDelegate
        customerDisplay.selectCommand(ad.connectionViaBgate ? MPOS_COMMAND_MODE.MODE_BGATE : MPOS_COMMAND_MODE.MODE_DIRECT)
        customerDisplay.selectInterface(MPOS_INTERFACE_TYPE(rawValue: ad.conntectionType)!, address: ad.connectionString)
        if customerDisplay.openService(ad.deviceId).rawValue != MPOS_RESULT.RES_SUCCESS.rawValue {
            showMessageBox(message: "openService failed", style: .critical)
            return
        }
        customerDisplay.clearScreen()
        showMessageBox(message: "OPEN SUCCESS", style: .informational)
        addHistory(text: "OPEN SUCCESS")
    }
    
    @IBAction func closeCustomerDisplay(_ sender: Any) {
        customerDisplay.closeService(0)
        addHistory(text: "CLOSED")
    }
    
    @IBAction func textDisplay(_ sender: Any) {
        if(!self.customerDisplay.isOpen()){ return }
        customerDisplay.displayString("1st Line: HELLO", codePage: 16, internationalCharacterSet: 0, line: 1)
        customerDisplay.displayString("2nd Line: HELLO", codePage: 16, internationalCharacterSet: 0, line: 2)
    }
    
    @IBAction func clearDisplay(_ sender: Any) {
        if(!self.customerDisplay.isOpen()){ return }
        customerDisplay.clearScreen()
    }
    
    @objc func deviceNotificationHandler(notification:NSNotification){
        DispatchQueue.main.async { [weak self] in
            guard let strongSelf = self else { return }
            if notification.name.rawValue.compare("StatusUpdateEvent").rawValue == 0 {
                let deviceId = notification.object as! Int
                if(deviceId != strongSelf.posPrinter.getDeviceId()){ return }
                for key in notification.userInfo!.keys {
                    let status = notification.userInfo![key] as! Int
                    print("notification.name = \(notification.name), id = \(deviceId), status = \(status)")
                    strongSelf.updatePosPrinterStatusText(status: status)
                }
            } else if notification.name.rawValue.compare("DataEvent").rawValue  == 0 {
                let deviceId = notification.object as! Int
                for key in notification.userInfo!.keys {
                    let devData = notification.userInfo![key] as! Data
                    let message = String(format: "ID = %ld, data = %@", deviceId, devData as CVarArg)
                    strongSelf.addHistory(text: message)
                }
            }else if notification.name.rawValue.compare("OutputCompletedEvent").rawValue == 0 {
                strongSelf.addHistory(text: "OutputCompletedEvent")
            }
        }
    }
}

extension TestMainViewController: NSWindowDelegate {
    func windowDidEndSheet(_ notification: Notification) {
        updateUIInformation()
    }
}
