# This sample code uses PyObjC and tkinter and was written in Python 3.9.13 environment.
# If error occured, please install each package via CLI below.
# pip install pyobjc
# pip install tk

from Foundation import NSObject
import Cocoa
import objc
from tkinter import messagebox
import tkinter as tk
import tkinter.ttk as ttk

from enum import IntEnum
import subprocess
from os import path

class MPOS_INTERFACE_TYPE(IntEnum):
    MPOS_INTERFACE_WIFI         = 1
    MPOS_INTERFACE_ETHERNET     = 2
    MPOS_INTERFACE_BLUETOOTH    = 4
    MPOS_INTERFACE_BLE          = 8
    MPOS_INTERFACE_USB          = 16
    
class MPOS_COMMAND_MODE(IntEnum):
    MPOS_COMMAND_MODE_BGATE   = 0
    MPOS_MODE_DIRECT          = 1

class MPOS_RESULT(IntEnum):
    MPOS_SUCCESS                        = 0
    MPOS_SUCCESS_ALREADY_OPEN           = 1
    MPOS_FAIL                           = 1000
    MPOS_FAIL_INVALID_INTERFACE         = 1001
    MPOS_FAIL_NO_OPEN                   = 1002
    MPOS_FAIL_NOT_SUPPORT               = 1003
    MPOS_FAIL_INVALID_PARAMETER         = 1004
    MPOS_FAIL_NO_RESPONSE               = 1005
    MPOS_FAIL_NOT_CONNECT               = 1006
    MPOS_FAIL_NO_DEVICE                 = 1007
    MPOS_FAIL_NO_FILE                   = 1008
    MPOS_FAIL_NOT_SUPPORT_CODEPAGE      = 1012
    MPOS_FAIL_NOT_SUPPORT_ICS           = 1013
    MPOS_PAGEMODE_ALREADY_IN            = 1014
    MPOS_TRANSACTION_ALREADY_IN         = 1015
    MPOS_FAIL_NOT_SUPPORT_ESCSEQ        = 1017
    
class MPOS_CASHDRAWER_PIN(IntEnum):
    MPOS_CASHDRAWER_PIN2    = 0
    MPOS_CASHDRAWER_PIN5    = 1
    
class MPOS_TRANSACTION(IntEnum):
    MPOS_TRANSACTION_OUT    = 0
    MPOS_TRANSACTION_IN     = 1
    
class MPOS_PRINTER_CUTTYPE(IntEnum):
    MPOS_PRINTER_CUTPAPER      = 0
    MPOS_PRINTER_FEEDCUT       = 65
    
class MPOS_PRINTER_PAGEMODE(IntEnum):
    MPOS_PRINTER_PAGEMODE_OUT   = 0
    MPOS_PRINTER_PAGEMODE_IN    = 1
    
class MPOS_PRINTER_PD(IntEnum):
    MPOS_PRINTER_PD_0           = 0
    MPOS_PRINTER_PD_LEFT90      = 1
    MPOS_PRINTER_PD_180         = 2
    MPOS_PRINTER_PD_RIGHT90     = 3
    
class MPOS_LABEL_MEDIA_TYPE:
    MPOS_LABEL_MEDIA_TYPE_GAP           = 'G'
    MPOS_LABEL_MEDIA_TYPE_CONTINUOUS    = 'C'
    MPOS_LABEL_MEDIA_TYPE_BLACK_MARK    = 'B'

class MPOS_LABEL_CONFIG_CODEPAGE(IntEnum):
    MPOS_LABEL_CONFIG_CODEPAGE_CP437            = 0
    MPOS_LABEL_CONFIG_CODEPAGE_CP850            = 1
    MPOS_LABEL_CONFIG_CODEPAGE_CP852            = 2
    MPOS_LABEL_CONFIG_CODEPAGE_CP860            = 3
    MPOS_LABEL_CONFIG_CODEPAGE_CP863            = 4
    MPOS_LABEL_CONFIG_CODEPAGE_CP865            = 5
    MPOS_LABEL_CONFIG_CODEPAGE_CP1252           = 6
    MPOS_LABEL_CONFIG_CODEPAGE_CP865_WCP1252    = 7
    MPOS_LABEL_CONFIG_CODEPAGE_CP857            = 8
    MPOS_LABEL_CONFIG_CODEPAGE_CP737            = 9
    MPOS_LABEL_CONFIG_CODEPAGE_WPC1250          = 10
    MPOS_LABEL_CONFIG_CODEPAGE_WPC1253          = 11
    MPOS_LABEL_CONFIG_CODEPAGE_WPC1254          = 12
    MPOS_LABEL_CONFIG_CODEPAGE_CP855            = 13
    MPOS_LABEL_CONFIG_CODEPAGE_CP862            = 14
    MPOS_LABEL_CONFIG_CODEPAGE_CP866            = 15
    MPOS_LABEL_CONFIG_CODEPAGE_WCP1251          = 16
    MPOS_LABEL_CONFIG_CODEPAGE_WCP1255          = 17
    MPOS_LABEL_CONFIG_CODEPAGE_CP928            = 18
    MPOS_LABEL_CONFIG_CODEPAGE_CP864            = 19
    MPOS_LABEL_CONFIG_CODEPAGE_CP775            = 20
    MPOS_LABEL_CONFIG_CODEPAGE_WCP1257          = 21
    MPOS_LABEL_CONFIG_CODEPAGE_CP858            = 22
    
class MPOS_LABEL_CONFIG_ICS(IntEnum):
    MPOS_LABEL_CONFIG_ICS_USA                   = 0
    MPOS_LABEL_CONFIG_ICS_FRANCE                = 1
    MPOS_LABEL_CONFIG_ICS_GERMANY               = 2
    MPOS_LABEL_CONFIG_ICS_UK                    = 3
    MPOS_LABEL_CONFIG_ICS_DENMARK_I             = 4
    MPOS_LABEL_CONFIG_ICS_SWEDEN                = 5
    MPOS_LABEL_CONFIG_ICS_ITALY                 = 6
    MPOS_LABEL_CONFIG_ICS_SPAIN_I               = 7
    MPOS_LABEL_CONFIG_ICS_NORWAY                = 8
    MPOS_LABEL_CONFIG_ICS_DENMARK_II            = 9
    MPOS_LABEL_CONFIG_ICS_JAPAN                 = 10
    MPOS_LABEL_CONFIG_ICS_SPAIN_II              = 11
    MPOS_LABEL_CONFIG_ICS_LATIN_AMERICA         = 12
    MPOS_LABEL_CONFIG_ICS_KOREA                 = 13
    MPOS_LABEL_CONFIG_ICS_SLOVENIA_CROATIA      = 14
    MPOS_LABEL_CONFIG_ICS_CHINA                 = 15

class MPOS_LABEL_DEVICE_FONT:
    MPOS_LABEL_DEVICE_FONT_6PT                   ='0'         #    9 X 15 (dots)
    MPOS_LABEL_DEVICE_FONT_8PT                   ='1'         #   12 X 20 (dots)
    MPOS_LABEL_DEVICE_FONT_10PT                  ='2'         #   16 X 25 (dots)
    MPOS_LABEL_DEVICE_FONT_12PT                  ='3'         #   19 X 30 (dots)
    MPOS_LABEL_DEVICE_FONT_15PT                  ='4'         #   24 X 38 (dots)
    MPOS_LABEL_DEVICE_FONT_20PT                  ='5'         #   32 X 40 (dots)
    MPOS_LABEL_DEVICE_FONT_30PT                  ='6'         #   48 X 76 (dots)
    MPOS_LABEL_DEVICE_FONT_14PT                  ='7'         #   22 X 34 (dots)
    MPOS_LABEL_DEVICE_FONT_18PT                  ='8'         #   28 X 44 (dots)
    MPOS_LABEL_DEVICE_FONT_24PT                  ='9'         #   37 X 58 (dots)
    MPOS_LABEL_DEVICE_FONT_KOREAN1               ='a'         #   16 X 16 (dots)  (ascii  9 X 15)
    MPOS_LABEL_DEVICE_FONT_KOREAN2               ='b'         #   24 X 24 (dots)  (ascii 12 X 24)
    MPOS_LABEL_DEVICE_FONT_KOREAN3               ='c'         #   20 X 20 (dots)  (ascii 12 X 20)
    MPOS_LABEL_DEVICE_FONT_KOREAN4               ='d'         #   26 X 26 (dots)  (ascii 16 X 30)
    MPOS_LABEL_DEVICE_FONT_KOREAN5               ='e'         #   20 X 26 (dots)  (ascii 16 X 30)
    MPOS_LABEL_DEVICE_FONT_KOREAN6               ='f'         #   38 X 38 (dots)  (ascii 22 X 34)
    MPOS_LABEL_DEVICE_FONT_GB2312                ='m'         #   24 X 24 (dots)  (ascii 12 X 24)
    MPOS_LABEL_DEVICE_FONT_BIG5                  ='n'         #   24 X 24 (dots)  (ascii 12 X 24)
    MPOS_LABEL_DEVICE_FONT_SHIFT_JIS             ='j'         #   24 X 24 (dots)  (ascii 12 X 24)

class MPOS_LABEL_VECTOR_FONT:
    MPOS_LABEL_VECTOR_FONT_ASCII                    = 'U'
    MPOS_LABEL_VECTOR_FONT_KS5601                   = 'K'
    MPOS_LABEL_VECTOR_FONT_BIG5                     = 'B'
    MPOS_LABEL_VECTOR_FONT_GB2312                   = 'G'
    MPOS_LABEL_VECTOR_FONT_SHIFT_JIS                = 'J'
    MPOS_LABEL_VECTOR_FONT_OCR_A                    = 'a'
    MPOS_LABEL_VECTOR_FONT_OCR_B                    = 'b'
    
class MPOS_LABEL_BARCODE_TYPE(IntEnum):
    MPOS_LABEL_BARCODE_TYPE_CODE39                      = 0
    MPOS_LABEL_BARCODE_TYPE_CODE128                     = 1
    MPOS_LABEL_BARCODE_TYPE_I2Of5                       = 2
    MPOS_LABEL_BARCODE_TYPE_CODABAR                     = 3
    MPOS_LABEL_BARCODE_TYPE_CODE93                      = 4
    MPOS_LABEL_BARCODE_TYPE_UPC_A                       = 5
    MPOS_LABEL_BARCODE_TYPE_UPC_E                       = 6
    MPOS_LABEL_BARCODE_TYPE_EAN13                       = 7
    MPOS_LABEL_BARCODE_TYPE_EAN8                        = 8
    MPOS_LABEL_BARCODE_TYPE_EAN128                      = 9
    MPOS_LABEL_BARCODE_TYPE_CODE11                      = 10
    MPOS_LABEL_BARCODE_TYPE_PLANET                      = 11
    MPOS_LABEL_BARCODE_TYPE_INDUSTRIAL_2Of5             = 12
    MPOS_LABEL_BARCODE_TYPE_STANDARD_2Of5               = 13
    MPOS_LABEL_BARCODE_TYPE_LOGMARS                     = 14
    MPOS_LABEL_BARCODE_TYPE_UPC_EAN_EXTENSIONS          = 15
    MPOS_LABEL_BARCODE_TYPE_POSTNET                     = 16

class MPOS_LABEL_BARCODE_HRI(IntEnum):
    MPOS_LABEL_BARCODE_NONE                     = 0
    MPOS_LABEL_BARCODE_BELOW                    = 1
    MPOS_LABEL_BARCODE_ABOVE                    = 2
    MPOS_LABEL_BARCODE_BELOW_FONTSIZE2          = 3
    MPOS_LABEL_BARCODE_ABOVE_FONTSIZE2          = 4
    MPOS_LABEL_BARCODE_BELOW_FONTSIZE3          = 5
    MPOS_LABEL_BARCODE_ABOVE_FONTSIZE3          = 6
    MPOS_LABEL_BARCODE_BELOW_FONTSIZE4          = 7
    MPOS_LABEL_BARCODE_ABOVE_FONTSIZE4          = 8
    
class MPOS_STATUS(IntEnum):
    MPOS_STATUS_NORMAL                  = 0
    MPOS_STATUS_PRINTER_COVEROPEN       = 1
    MPOS_STATUS_PRINTER_PAPEREMPTY      = 2
    MPOS_STATUS_PRINTER_NEAREND         = 4
    MPOS_STATUS_PRINTER_ERROR           = 8
    MPOS_STATUS_BATT_HIGH               = 16
    MPOS_STATUS_BATT_MID                = 32
    MPOS_STATUS_BATT_LOW                = 64
    MPOS_STATUS_PRINTER_OFFLINE         = 128
    MPOS_STATUS_DRAWER_HIGH             = 256
    MPOS_STATUS_DRAWER_LOW              = 512
    
    MPOS_STATUS_BGT_BUSY                = 1200
    MPOS_STATUS_BGT_PROGRESS            = 1201
    MPOS_STATUS_BGT_COMPLETED           = 1202
    MPOS_STATUS_BGT_REQUIRE_RESET       = 1203
    MPOS_STATUS_FAIL                    = -1
    
class MPOS_LABEL_STATUS(IntEnum):
    MPOS_LABEL_STATUS_IDLE                                      = 0
    MPOS_LABEL_STATUS_PRINTER_PAPEREMPTY                        = 1
    MPOS_LABEL_STATUS_PRINTER_COVEROPEN                         = 2
    MPOS_LABEL_STATUS_PRINTER_CUTTER_JAMMED                     = 4
    MPOS_LABEL_STATUS_PRINTER_TPH_OVERHEAT                      = 8
    MPOS_LABEL_STATUS_PRINTER_GAP_ERROR                         = 16
    MPOS_LABEL_STATUS_PRINTER_RIBBON_END                        = 32
    MPOS_LABEL_STATUS_PRINTER_ON_BUILDING_LABEL_TO_BE_PRINTED   = 64
    MPOS_LABEL_STATUS_PRINTER_ON_PRINTING_LABEL                 = 128
    MPOS_LABEL_STATUS_PRINTER_ISSUED_LABEL_ISPAUSED             = 256
    MPOS_LABEL_STATUS_PRINTER_MOTOR_OVERHEAT                    = 512
    MPOS_LABEL_STATUS_PRINTER_BOARD_OVERHEAT                    = 1024
    MPOS_LABEL_STATUS_PRINTER_WAIT_PAPER_TAKEN                  = 2048
    MPOS_LABEL_STATUS_PRINTER_RFID_WRITING_ERROR                = 4096
    MPOS_LABEL_STATUS_PRINTER_OFFLINE                           = 8192
    
def parsePosStatus(statusInteger):
    status = ""
    if statusInteger == 0:
        status = " | NORMAL"
    else:
        if MPOS_STATUS.MPOS_STATUS_PRINTER_COVEROPEN & statusInteger:
            status += " | COVER_OPEN"
        if MPOS_STATUS.MPOS_STATUS_PRINTER_PAPEREMPTY & statusInteger:
            status += " | PAPER_EMPTY"
        if MPOS_STATUS.MPOS_STATUS_PRINTER_NEAREND & statusInteger:
            status += " | PAPER_NEAREND"
        if MPOS_STATUS.MPOS_STATUS_PRINTER_ERROR & statusInteger:
            status += " | ERROR"
        if MPOS_STATUS.MPOS_STATUS_BATT_LOW & statusInteger:
            status += " | BATTERY_LOW"
        if MPOS_STATUS.MPOS_STATUS_DRAWER_HIGH & statusInteger:
            status += " | CD_HIGH"
        if MPOS_STATUS.MPOS_STATUS_DRAWER_LOW & statusInteger:
            status += " | CD_LOW"
        if MPOS_STATUS.MPOS_STATUS_PRINTER_OFFLINE & statusInteger:
            status += "OFFLINE"
    print(status)
    
def parseLabelStatus(statusInteger):
    status = ""
    if statusInteger == 0:
        status = "NORMAL"
    else:
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_PAPEREMPTY & statusInteger:
            status = "PAPER EMPTY"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_COVEROPEN & statusInteger:
            status = "COVER OPEN"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_CUTTER_JAMMED & statusInteger:
            status = "CUTTER JAM"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_TPH_OVERHEAT & statusInteger:
            status = "TPH OVERHEAT"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_GAP_ERROR & statusInteger:
            status = "GAP ERROR"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_RIBBON_END & statusInteger:
            status = "RIBBON END"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_ON_BUILDING_LABEL_TO_BE_PRINTED & statusInteger:
            status = "BUILDING LEBEL"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_ON_PRINTING_LABEL & statusInteger:
            status = "PRINTING LABEL"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_ISSUED_LABEL_ISPAUSED & statusInteger:
            status = "LABEL PAUSED"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_MOTOR_OVERHEAT & statusInteger:
            status = "MOTOR OVERHEAT"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_BOARD_OVERHEAT & statusInteger:
            status = "BOARD OVERHEAT"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_WAIT_PAPER_TAKEN & statusInteger:
            status = "WAIT FOR PAER TO BE TAKEN"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_RFID_WRITING_ERROR & statusInteger:
            status = "RFID WRITING ERROR"
        if MPOS_LABEL_STATUS.MPOS_LABEL_STATUS_PRINTER_OFFLINE & statusInteger:
            status = "OFFLINE"
    print(status)
    
def printModelName(modelName):
    print("Model Name : %s" % modelName)

class SDKLoader(NSObject):
    def init(self):
        objc.loadBundle("frameworkMPosSDK", bundle_path="../../../Libs/DynamicLibrary/frameworkMPosSDK.xcframework/macos-arm64_x86_64/frameworkMPosSDK.framework", module_globals=globals())
        NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, b"deviceNotificationHandler:", "DataEvent", None)
        NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, b"deviceNotificationHandler:", "StatusUpdateEvent", None)
        NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, b"deviceNotificationHandler:", "OutputCompletedEvent", None)
        
        ## Whether create logs or not
#        MPosControllerDevices.setEnableLog_saveFile_saveToHex_(True, True, True)
    
    def deviceNotificationHandler_(self, notification):
        deviceId = notification.object().integerValue()
        
        if notification.name() == "StatusUpdateEvent":
            if deviceId != posPrinter.getInstance().getDeviceId():
                return
                
            allKeys = notification.userInfo().allKeys().mutableCopy()
            
            for key in allKeys:
                statusInteger = notification.userInfo().objectForKey_(key).integerValue()
                parsePosStatus(statusInteger)
                print("%s, device id = %ld" % (notification.name(), deviceId))
                
        elif notification.name() == "DataEvent":
            allKeys = notification.userInfo().allKeys().mutableCopy()
            
            for key in allKeys:
                data = notification.userInfo().objectForKey_(key)
                print("%s, device id = %ld, data = %s" % (notification.name(), deviceId, data))
                
        elif notification.name() == "OutputCompletedEvent":
            print("OutputCompletedEvent")

class MPosControllerPrinterWrapper():
    def __init__(self):
        self.printer = MPosControllerPrinter.new()
        
    def getInstance(self):
        return self.printer
        
    def selectCommandMode(self, commandMode):
        return self.printer.selectCommandMode_(commandMode)
        
    def selectInterface(self, interfaceType, address):
        return self.printer.selectInterface_address_(interfaceType, address)
    
    def openService(self, deviceID, timeout):
        return self.printer.openService_timeout_(deviceID, timeout)
        
    def closeService(self):
        return self.printer.closeService_(3)
        
    def setTransaction(self, mode):
        return self.printer.setTransaction_(mode)
        
    def openDrawer(self, pinNumber):
        return self.printer.openDrawer_(pinNumber)
        
    def printText(self, data, fontType=None, fontWidth=None, fontHeight=None, bold=None, underline=None, reverse=None, alignment=None):
        if fontType == None and fontWidth == None and fontHeight == None and bold == None and underline == None and reverse == None and alignment == None:
            return self.printer.printText_(data)
        return self.printer.printText_fontType_fontWidth_fontHeight_bold_underline_reverse_alignment_(data, fontType, fontWidth, fontHeight, bold, underline, reverse, alignment)
        
    def print1DBarcode(self, data, symbology, width, height, alignment, textPosition):
        return self.printer.print1DBarcode_symbology_width_height_alignment_textPosition_(data, symbology, width, height, alignment, textPosition)
    
    def printImage(self, imgObj, width, alignment, threshold, ditheringType, compressType):
        return self.printer.printImage_width_alignment_threshold_ditheringType_compressType_(imgObj, width, alignment, threshold, ditheringType, compressType)
        
    def cutPaper(self, cutType):
        return self.printer.cutPaper_(cutType)
        
    def setPagemode(self, mode):
        return self.printer.setPagemode_(mode)
    
    def setPagemodePrintArea(self, x, y, width, height):
        return self.printer.setPagemodePrintArea_y_width_height_(x, y, width, height)
        
    def setPagemodeDirection(self, direction):
        return self.printer.setPagemodeDirection_(direction)
        
    def setPagemodePosition(self, x, y):
        return self.printer.setPagemodePosition_y_(x, y)

class MPosControllerLabelPrinterWrapper():
    def __init__(self):
        self.printer = MPosControllerLabelPrinter.new()
        
    def getInstance(self):
        return self.printer
        
    def selectCommandMode(self, commandMode):
        return self.printer.selectCommandMode_(commandMode)
        
    def selectInterface(self, interfaceType, address):
        return self.printer.selectInterface_address_(interfaceType, address)
    
    def openService(self, deviceID, timeout):
        return self.printer.openService_timeout_(deviceID, timeout)
        
    def closeService(self):
        return self.printer.closeService_(3)
    
    def getSupportedSpeeds(self):
        return self.printer.getSupportedSpeeds()
    
    def getPrinterDPI(self):
        return self.printer.getPrinterDPI()
        
    def setTransaction(self, mode):
        return self.printer.setTransaction_(mode)
        
    def setSpeed(self, speed):
        return self.printer.setSpeed_(speed)
    
    def setDensity(self, density):
        return self.printer.setDensity_(density)
    
    def setWidth(self, labelWidth):
        return self.printer.setWidth_(labelWidth)
             
    def setLength(self, labelLength, gapLength, mediaType, offsetLength):
        return self.printer.setLength_gapLength_mediaType_offsetLength_(labelLength, gapLength, mediaType, offsetLength)
    
    def drawBlock(self, startPosX, startPosY, endPosX, endPosY, option, thickness):
        return self.printer.drawBlock_startPosY_endPosX_endPosY_option_thickness_(startPosX, startPosY, endPosX, endPosY, option, thickness)
    
    def setCharacterset(self, characterset, internationalCharset):
        return self.printer.setCharacterset_internationalCharset_(characterset, internationalCharset)
                         
    def drawTextDeviceFont(self, data, xPos, yPos, fontSelection, fontWidth, fontHeight, rightSpace, rotation, reverse, bold, rightToLeft, alignment):
        return self.printer.drawTextDeviceFont_xPos_yPos_fontSelection_fontWidth_fontHeight_rightSpace_rotation_reverse_bold_rightToLeft_alignment_(data, xPos, yPos, fontSelection, fontWidth, fontHeight, rightSpace, rotation, reverse, bold, rightToLeft, alignment)
                         
    def drawTextVectorFont(self, data, xPos, yPos, fontSelection, fontWidth, fontHeight, rightSpace, rotation, reverse, bold, italic, rightToLeft, alignment):
        return self.printer.drawTextVectorFont_xPos_yPos_fontSelection_fontWidth_fontHeight_rightSpace_rotation_reverse_bold_italic_rightToLeft_alignment_(data, xPos, yPos, fontSelection, fontWidth, fontHeight, rightSpace, rotation, reverse, bold, italic, rightToLeft, alignment)
                     
    def drawBarcode1D(self, data, xPos, yPos, barcodeType, widthNarrow, widthWide, height, hri, quietZoneWidth, rotation):
        return self.printer.drawBarcode1D_xPos_yPos_barcodeType_widthNarrow_widthWide_height_hri_quietZoneWidth_rotation_(data, xPos, yPos, barcodeType, widthNarrow, widthWide, height, hri, quietZoneWidth, rotation)
             
    def drawImage(self, image, xPos, yPos, width, threshold, ditheringType, compressType):
        return self.printer.drawImage_xPos_yPos_width_threshold_ditheringType_compressType_(image, xPos, yPos, width, threshold, ditheringType, compressType)
    
    def printBuffer(self, numberOfCopies):
        return self.printer.printBuffer_(numberOfCopies)
        
class MPosControllerBCDWrapper():
    def __init__(self):
        self.device = MPosControllerBCD.new()
        
    def getInstance(self):
        return self.device
        
    def selectCommandMode(self, commandMode):
        return self.device.selectCommandMode_(commandMode)
        
    def selectInterface(self, interfaceType, address):
        return self.device.selectInterface_address_(interfaceType, address)
    
    def openService(self, deviceID, timeout):
        return self.device.openService_timeout_(deviceID, timeout)
        
    def closeService(self):
        return self.device.closeService_(0)
                         
    def displayString(self, data, codePage, internationalCharacterSet, line):
        return self.device.displayString_codePage_internationalCharacterSet_line_(data, codePage, internationalCharacterSet, line)
        
    def clearScreen(self):
        return self.device.clearScreen()

if __name__ == "__main__":
    window = tk.Tk()
    
    window.title("MPosSampleMacPython")
    window.geometry("605x530+100+100")
    window.resizable(False, False)
    
    sdkLoader = SDKLoader.new()
    
    lookup = MPosLookup.new()
    config = MPosControllerConfig.new()
    posPrinter = MPosControllerPrinterWrapper()
    labelPrinter = MPosControllerLabelPrinterWrapper()
    barcodeScanner = MPosControllerScanner.new()
    usbToSerialDevice = MPosControllerUSBSerial.new()
    customerDisplay = MPosControllerBCDWrapper()
    
    tree = ttk.Treeview(window, column=("one", "two", "three"), show='headings', height=5)
    
    tree.column("#1", width=100, anchor="center")
    tree.heading("#1", text="Interface")
    tree.column("#2", width=200, anchor="center")
    tree.heading("#2", text="Device Name")
    tree.column("#3", width=285, anchor="center")
    tree.heading("#3", text="Device Information")
    
    treeVerticalScroll = ttk.Scrollbar(window, orient='vertical', command=tree.yview)
    
    tree.configure(yscrollcommand=treeVerticalScroll.set)
    
    address = tk.StringVar()
    addressLabel = tk.Label(window, text="Address")
    addressText = ttk.Entry(window, width=42, textvariable=address)
    
    def onAddressLookup():
        tree.delete(*tree.get_children())
        networkDevices = lookup.getNetworkDevices()
        btDevices = lookup.getBluetoothDevices()
        bleDevices = lookup.getBLEDevices()
        usbDevices = lookup.getUSBDevices()
    
        devices = networkDevices + btDevices + bleDevices + usbDevices
        for i in range(0, devices.count()):
            if devices != None:
                if devices[i].interfaceType() == MPOS_INTERFACE_TYPE.MPOS_INTERFACE_WIFI or devices[i].interfaceType() == MPOS_INTERFACE_TYPE.MPOS_INTERFACE_ETHERNET:
                    tree.insert('', 'end', text="", values=('Network', "%s" % (devices[i].name() if devices[i].name().length() > 0 else "Network Device"), "%s:%s" % (devices[i].address(), devices[i].portNumber())))
                elif devices[i].interfaceType() == MPOS_INTERFACE_TYPE.MPOS_INTERFACE_BLUETOOTH:
                    tree.insert('', 'end', text="", values=('Bluetooth', "%s" % (devices[i].name() if devices[i].name().length() > 0 else "Bluetooth Device"), "%s" % (devices[i].macAddress())))
                elif devices[i].interfaceType() == MPOS_INTERFACE_TYPE.MPOS_INTERFACE_BLE:
                    tree.insert('', 'end', text="", values=('BLE', "%s" % (devices[i].name() if devices[i].name().length() > 0 else "BLE Device"), "%s" % (devices[i].macAddress())))
                elif devices[i].interfaceType() == MPOS_INTERFACE_TYPE.MPOS_INTERFACE_USB:
                    tree.insert('', 'end', text="", values=('USB', "%s" % (devices[i].name() if devices[i].name().length() > 0 else "USB Device"), "%s" % (devices[i].address())))
                else:
                    tree.insert('', 'end', text="", values=('Unknown', "Unknown"))
    
    def onSelectDeviceInformation(event):
        item = tree.selection()
        for i in item:
            address.set(tree.item(i, "values")[2])
        
    tree.bind("<<TreeviewSelect>>", onSelectDeviceInformation)
    addressLookupButton = tk.Button(window, text="Lookup",command=onAddressLookup, width=10, height=1)
    
    deviceIdTree = ttk.Treeview(window, column=("one", "two"), show='headings', height=5)
    
    deviceIdTree.column("#1", width=100, anchor="center")
    deviceIdTree.heading("#1", text="Device ID")
    deviceIdTree.column("#2", width=485, anchor="center")
    deviceIdTree.heading("#2", text="Category")
    
    deviceIdTreeVerticalScroll = ttk.Scrollbar(window, orient='vertical', command=deviceIdTree.yview)
    
    deviceIdTree.configure(yscrollcommand=deviceIdTreeVerticalScroll.set)
    
    deviceId = tk.StringVar()
    deviceIdLabel = tk.Label(window, text="Device ID")
    deviceIdText = ttk.Entry(window, width=28, textvariable=deviceId, state="disabled", foreground="grey")
    
    deviceId.set("Not Assigned")
    
    def getCurrentInterface():
        item = tree.selection()
        interface = MPOS_INTERFACE_TYPE.MPOS_INTERFACE_USB
        for i in item:
            if tree.item(i, "values")[0] == 'Network':
                interface = MPOS_INTERFACE_TYPE.MPOS_INTERFACE_ETHERNET
            elif tree.item(i, "values")[0] == 'Bluetooth':
                interface = MPOS_INTERFACE_TYPE.MPOS_INTERFACE_BLUETOOTH
            elif tree.item(i, "values")[0] == 'BLE':
                interface = MPOS_INTERFACE_TYPE.MPOS_INTERFACE_BLE
            elif tree.item(i, "values")[0] == 'USB':
                interface = MPOS_INTERFACE_TYPE.MPOS_INTERFACE_USB
            else:
                interface = MPOS_INTERFACE_TYPE.MPOS_INTERFACE_BLE
        
        return interface
        
    def getCurrentDeviceId():
        deviceIdInt = -1
        if deviceId.get() == "Not Assigned":
            return deviceIdInt
        
        deviceIdInt = int(deviceId.get())
        return deviceIdInt
    
    def onDeviceIdInit():
        deviceIdTree.selection_remove(deviceIdTree.selection())
        deviceId.set("Not Assigned")
    
    def onDeviceIdLookup():
        deviceIdTree.delete(*deviceIdTree.get_children())
        config.selectCommandMode_(MPOS_COMMAND_MODE(connectionMode.get()))
        config.selectInterface_address_(getCurrentInterface(), address.get())
        
        if config.openService() != MPOS_RESULT.MPOS_SUCCESS:
            return
        
        deviceIdList = config.searchDevices()
        config.closeService_(0)
        
        for i in range(0, deviceIdList.count()):
            value = deviceIdList[i].integerValue()
            category = "Unknown"
            if value > 0 and value < 10:
                category = "Label Printer"
            elif value == 10:
                category = "POS Printer (internal)"
            elif value  > 10 and value < 20:
                category = "POS Printer"
            elif value >= 20 and value < 30:
                category = "HID"
            elif value >= 30 and value < 40:
                category = "MSR"
            elif value >= 40 and value < 50:
                category = "Scanner"
            elif value >= 60 and value < 69:
                category = "RFID"
            elif value >= 70 and value < 79:
                category = "DALLAS KEY"
            elif value >= 80 and value < 89:
                category = "NFC"
            elif value >= 100 and value < 110:
                category = "Unregistered USB UART Device"
            elif value >= 110 and value < 120:
                category = "Customer Display"
            elif value >= 120 and value < 130:
                category = "TTY USB (USB to Serial)"
            elif value >= 130 and value < 140:
                category = "SCALE"
            
            deviceIdTree.insert('', 'end', text="", values=(value, category))
    
    deviceIdInitButton = tk.Button(window, text="Init", command=onDeviceIdInit, width=10, height=1)
    deviceIdLookupButton = tk.Button(window, text="Lookup",command=onDeviceIdLookup, width=10, height=1)
    
    def onSelectDeviceId(event):
        item = deviceIdTree.selection()
        for i in item:
            deviceId.set(deviceIdTree.item(i, "values")[0])
    
    deviceIdTree.bind("<<TreeviewSelect>>", onSelectDeviceId)
    
    connectionMethodLabel = tk.Label(window, text="Connection Method")
    connectionMode = tk.IntVar()
    
    radioViaBgate = tk.Radiobutton(window, text="Via B-gate", variable=connectionMode, value=0)
    radioDirect = tk.Radiobutton(window, text="Direct", variable=connectionMode, value=1)
    
    connectionMode.set(0)
    
    def onPosOpenBtn():
        posPrinter.selectCommandMode(MPOS_COMMAND_MODE(connectionMode.get()))
        posPrinter.selectInterface(getCurrentInterface(), address.get())
        if posPrinter.openService(getCurrentDeviceId(), 10) == MPOS_RESULT.MPOS_SUCCESS:
            posPrinter.getInstance().asbEnable_(True)
            objc.registerMetaDataForSelector(
                b'MPosControllerPrinter',
                b'getModelName:',
                {
                    'arguments': {
                            2: { 'callable': {
                                    'arguments': {
                                    0:{'type': b'^v'},
                                    1:{'type': b'@'},
                                    },
                            '       retval': { 'type': b'v' }
                                    }
                                }
                            }
                }
            )
            posPrinter.getInstance().getModelName_(printModelName)
            print("OPEN SUCCESS")
        else:
            print("OPEN FAILED")
        
    def onPosCloseBtn():
        if posPrinter.closeService() == MPOS_RESULT.MPOS_SUCCESS:
            print("CLOSE SUCCESS")
        else:
            print("CLOSE FAILED")
        
    def onPosPrintLineMode():
        posPrinter.setTransaction(MPOS_TRANSACTION.MPOS_TRANSACTION_IN)
        posPrinter.openDrawer(MPOS_CASHDRAWER_PIN.MPOS_CASHDRAWER_PIN2)
        posPrinter.printText("Default\n", 0, 0, 0, 0, 0, 0, -1)
        posPrinter.printText("Bold+Double\n", 0, 1, 1, 1, 0, 0, -1)
        posPrinter.printText("Underline in 2 dots\n", 0, 0, 0, 0, 2, 0, -1)
        posPrinter.print1DBarcode("{C1234567890", 110, 2, 50, -1, 2)
        posPrinter.printImage(Cocoa.NSImage.alloc().initWithContentsOfFile_("./bixolon.jpg"), 384, 1, 128, 1, 1)
        posPrinter.cutPaper(MPOS_PRINTER_CUTTYPE.MPOS_PRINTER_FEEDCUT)
        posPrinter.setTransaction(MPOS_TRANSACTION.MPOS_TRANSACTION_OUT)
        
    def onPosPrintPageMode():
        yPosition = 30
        posPrinter.setPagemode(MPOS_PRINTER_PAGEMODE.MPOS_PRINTER_PAGEMODE_IN)
        posPrinter.setPagemodePrintArea(0, 0, 384, 600)
        posPrinter.setPagemodeDirection(MPOS_PRINTER_PD.MPOS_PRINTER_PD_RIGHT90)
        posPrinter.printText("PAGE MODE #1")
        yPosition += 30
        posPrinter.setPagemodePosition(30, yPosition)
        posPrinter.printText("PAGE MODE #2")
        yPosition += 60
        posPrinter.setPagemodePosition(30, yPosition)
        posPrinter.print1DBarcode("{C1234567890", 110, 2, 50, -1, 0)
        posPrinter.setPagemode(MPOS_PRINTER_PAGEMODE.MPOS_PRINTER_PAGEMODE_OUT)
        posPrinter.cutPaper(MPOS_PRINTER_CUTTYPE.MPOS_PRINTER_FEEDCUT)
        
    def onPosCheckStatus():
        objc.registerMetaDataForSelector(
                b'MPosControllerPrinter',
                b'checkPrinterStatus:',
                {
                    'arguments': {
                            2: { 'callable': {
                                    'arguments': {
                                    0:{'type': b'^v'},
                                    1:{'type': b'i'},
                                    },
                            '       retval': { 'type': b'v' }
                                    }
                                }
                            }
                }
        )
        posPrinter.getInstance().checkPrinterStatus_(parsePosStatus)
        
    posFrame = tk.LabelFrame(window, text="Pos Printer")
    posOpenBtn = tk.Button(posFrame, text="Open", command=onPosOpenBtn, width=10, height=1)
    posCloseBtn = tk.Button(posFrame, text="Close", command=onPosCloseBtn, width=10, height=1)
    posPrintLineModeBtn = tk.Button(posFrame, text="Print (Line Mode)", command=onPosPrintLineMode, width=10, height=1)
    posPrintPageModeBtn = tk.Button(posFrame, text="Print (Page Mode)", command=onPosPrintPageMode, width=10, height=1)
    posCheckStatusBtn = tk.Button(posFrame, text="Check Status", command=onPosCheckStatus, width=10, height=1)
    posOpenBtn.pack()
    posCloseBtn.pack()
    posPrintLineModeBtn.pack()
    posPrintPageModeBtn.pack()
    posCheckStatusBtn.pack()
    
    def onLabelOpenBtn():
        labelPrinter.selectCommandMode(MPOS_COMMAND_MODE(connectionMode.get()))
        labelPrinter.selectInterface(getCurrentInterface(), address.get())
        if labelPrinter.openService(getCurrentDeviceId(), 10) == MPOS_RESULT.MPOS_SUCCESS:
            objc.registerMetaDataForSelector(
                b'MPosControllerLabelPrinter',
                b'getModelName:',
                {
                    'arguments': {
                            2: { 'callable': {
                                    'arguments': {
                                    0:{'type': b'^v'},
                                    1:{'type': b'@'},
                                    },
                            '       retval': { 'type': b'v' }
                                    }
                                }
                            }
                }
            )
            labelPrinter.getInstance().getModelName_(printModelName)
            print("OPEN SUCCESS")
        else:
            print("OPEN FAILED")
        
    def onLabelCloseBtn():
        if labelPrinter.closeService() == MPOS_RESULT.MPOS_SUCCESS:
            print("CLOSE SUCCESS")
        else:
            print("CLOSE FAILED")
            
    def onLabelPrintBtn():
        dotsPerUnit = labelPrinter.getPrinterDPI() / 25.4
        labelPrinter.setTransaction(MPOS_TRANSACTION.MPOS_TRANSACTION_IN)
        speeds = labelPrinter.getSupportedSpeeds()
        if speeds.count() > 0:
            speed = speeds.lastObject().integerValue()
            labelPrinter.setSpeed(speed)
            
        labelPrinter.setDensity(10)
        labelPrinter.setWidth(dotsPerUnit * 52)
        labelPrinter.setLength(dotsPerUnit * 75, dotsPerUnit * 3, ord(MPOS_LABEL_MEDIA_TYPE.MPOS_LABEL_MEDIA_TYPE_GAP), 0)
        labelPrinter.drawBlock(0, 0, dotsPerUnit * 52, dotsPerUnit *73, ord('B'), dotsPerUnit * 2)
        labelPrinter.setCharacterset(MPOS_LABEL_CONFIG_CODEPAGE.MPOS_LABEL_CONFIG_CODEPAGE_CP437, MPOS_LABEL_CONFIG_ICS.MPOS_LABEL_CONFIG_ICS_USA)
        labelPrinter.drawTextDeviceFont("Device Font", 50, 30, ord(MPOS_LABEL_DEVICE_FONT.MPOS_LABEL_DEVICE_FONT_10PT), 1, 1, 0, 0, False, False, False, 0)
        labelPrinter.drawTextVectorFont("Vector Font", 50, 100, ord(MPOS_LABEL_VECTOR_FONT.MPOS_LABEL_VECTOR_FONT_ASCII), 30, 30, 0, 0, False, False, False, False, 0)
        labelPrinter.drawBarcode1D("0987654321", 300, 150, MPOS_LABEL_BARCODE_TYPE.MPOS_LABEL_BARCODE_TYPE_CODE128, 2, 2, 128, MPOS_LABEL_BARCODE_HRI.MPOS_LABEL_BARCODE_BELOW_FONTSIZE3, 0, 1)
        labelPrinter.drawImage(Cocoa.NSImage.alloc().initWithContentsOfFile_("./bixolon.jpg"), 40, 370, 300, 128, 1, 1)
        labelPrinter.printBuffer(1)
        labelPrinter.setTransaction(MPOS_TRANSACTION.MPOS_TRANSACTION_OUT)
        
    def onLabelCheckStatus():
        objc.registerMetaDataForSelector(
                b'MPosControllerLabelPrinter',
                b'checkPrinterStatus:',
                {
                    'arguments': {
                            2: { 'callable': {
                                    'arguments': {
                                    0:{'type': b'^v'},
                                    1:{'type': b'i'},
                                    },
                            '       retval': { 'type': b'v' }
                                    }
                                }
                            }
                }
        )
        labelPrinter.getInstance().checkPrinterStatus_(parseLabelStatus)
    
    labelFrame = tk.LabelFrame(window, text="Label Printer")
    labelOpenBtn = tk.Button(labelFrame, text="Open", command=onLabelOpenBtn, width=10, height=1)
    labelCloseBtn = tk.Button(labelFrame, text="Close", command=onLabelCloseBtn, width=10, height=1)
    labelPrintBtn = tk.Button(labelFrame, text="Print", command=onLabelPrintBtn, width=10, height=1)
    labelCheckStatusBtn = tk.Button(labelFrame, command=onLabelCheckStatus, text="Check Status", width=10, height=1)
    labelOpenBtn.pack()
    labelCloseBtn.pack()
    labelPrintBtn.pack()
    labelCheckStatusBtn.pack()
    
    def onBarcodeScannerOpenBtn():
        barcodeScanner.selectCommandMode_(MPOS_COMMAND_MODE(connectionMode.get()))
        barcodeScanner.selectInterface_address_(getCurrentInterface(), address.get())
        if barcodeScanner.openService_timeout_(getCurrentDeviceId(), 10) == MPOS_RESULT.MPOS_SUCCESS:
            print("OPEN SUCCESS")
        else:
            print("OPEN FAILED")
        
    def onBarcodeScannerCloseBtn():
        if barcodeScanner.closeService_(0) == MPOS_RESULT.MPOS_SUCCESS:
            print("CLOSE SUCCESS")
        else:
            print("CLOSE FAILED")
    
    barcodeScannerFrame = tk.LabelFrame(window, text="Barcode Scanner")
    barcodeScannerOpenBtn = tk.Button(barcodeScannerFrame, text="Open", command=onBarcodeScannerOpenBtn, width=10, height=1)
    barcodeScannerCloseBtn = tk.Button(barcodeScannerFrame, text="Close", command=onBarcodeScannerCloseBtn, width=10, height=1)
    barcodeScannerOpenBtn.pack()
    barcodeScannerCloseBtn.pack()
    
    
    def onUsbSerialOpenBtn():
        usbToSerialDevice.selectCommandMode_(MPOS_COMMAND_MODE(connectionMode.get()))
        usbToSerialDevice.selectInterface_address_(getCurrentInterface(), address.get())
        if usbToSerialDevice.openService_timeout_(getCurrentDeviceId(), 10) == MPOS_RESULT.MPOS_SUCCESS:
            print("OPEN SUCCESS")
        else:
            print("OPEN FAILED")
        
    def onUsbSerialCloseBtn():
        if usbToSerialDevice.closeService_(0) == MPOS_RESULT.MPOS_SUCCESS:
            print("CLOSE SUCCESS")
        else:
            print("CLOSE FAILED")
    
    usbSerialFrame = tk.LabelFrame(window, text="USB to Serial")
    usbSerialOpenBtn = tk.Button(usbSerialFrame, text="Open", command=onUsbSerialOpenBtn, width=10, height=1)
    usbSerialCloseBtn = tk.Button(usbSerialFrame, text="Close", command=onUsbSerialCloseBtn, width=10, height=1)
    usbSerialOpenBtn.pack()
    usbSerialCloseBtn.pack()
    
    def onCustomerDisplayOpenBtn():
        customerDisplay.selectCommandMode(MPOS_COMMAND_MODE(connectionMode.get()))
        customerDisplay.selectInterface(getCurrentInterface(), address.get())
        if customerDisplay.openService(getCurrentDeviceId(), 10) == MPOS_RESULT.MPOS_SUCCESS:
            print("OPEN SUCCESS")
        else:
            print("OPEN FAILED")
        
    def onCustomerDisplayCloseBtn():
        if customerDisplay.closeService() == MPOS_RESULT.MPOS_SUCCESS:
            print("CLOSE SUCCESS")
        else:
            print("CLOSE FAILED")
            
    def onCustomerDisplayTextBtn():
        customerDisplay.displayString("1st Line: HELLO", 16, 0, 1)
        customerDisplay.displayString("2nd Line: HELLO", 16, 0, 2)
    
    def onCustomerDisplayClearBtn():
        customerDisplay.clearScreen()
    
    customerDisplayFrame = tk.LabelFrame(window, text="Customer Display (BCD)")
    customerDisplayOpenBtn = tk.Button(customerDisplayFrame, text="Open", command=onCustomerDisplayOpenBtn, width=10, height=1)
    customerDisplayCloseBtn = tk.Button(customerDisplayFrame, text="Close", command=onCustomerDisplayCloseBtn, width=10, height=1)
    customerDisplayTextBtn = tk.Button(customerDisplayFrame, text="Display Text", command=onCustomerDisplayTextBtn, width=10, height=1)
    customerDisplayClearBtn = tk.Button(customerDisplayFrame, text="Clear", command=onCustomerDisplayClearBtn, width=10, height=1)
    customerDisplayOpenBtn.pack()
    customerDisplayCloseBtn.pack()
    customerDisplayTextBtn.pack()
    customerDisplayClearBtn.pack()
    
    tree.place(x=10, y=10)
    treeVerticalScroll.place(x=580, y=10, height=118)
    addressLabel.place(x=10, y=135)
    addressText.place(x=70, y=135)
    addressLookupButton.place(x=470, y=132)
    
    deviceIdTree.place(x=10, y=175)
    deviceIdTreeVerticalScroll.place(x=580, y=175, height=118)
    deviceIdLabel.place(x=10, y=300)
    deviceIdText.place(x=70, y=300)
    deviceIdInitButton.place(x=340, y=297)
    deviceIdLookupButton.place(x=470, y=297)
    
    connectionMethodLabel.place(x=10, y=330)
    radioViaBgate.place(x=155, y=330)
    radioDirect.place(x=250, y=330)
    
    posFrame.place(x=10, y=360)
    labelFrame.place(x=150, y=360)
    barcodeScannerFrame.place(x=290, y=360)
    usbSerialFrame.place(x=290, y=445)
    customerDisplayFrame.place(x=430, y=360)
    
    window.mainloop()
