import usb.core
import usb.util
import sys
import re

dymo_vid = 0x0922       # Dymo vendor ID
dymo_tt_pid = 0x0018    # Dymo Twin Turbo Product ID
dymo_400_pid = 0x0019   # Dymo 400 Product ID
dymo_450_pid = 0x0020   # Dymo 450 Product ID
dymo_450tt_pid = 0x0022 # Dymo 450 Twin Turbo Product ID

dymo_tt = usb.core.find(idVendor=dymo_vid, idProduct=dymo_tt_pid)
dymo_400 = usb.core.find(idVendor=dymo_vid, idProduct=dymo_400_pid)
dymo_450 = usb.core.find(idVendor=dymo_vid, idProduct=dymo_450_pid)
dymo_450_tt = usb.core.find(idVendor=dymo_vid, idProduct=dymo_450tt_pid)

#dymo_tt.serial_number    # Prints device unique ID
#dymo_tt.langids          # Returns language IDs (1033,) for english

# Get descriptor for serial number
# fields: dymo_tt - reference to device to get the data from
#         254 - max buffer for received data (254 max, 30 expected for serial number)
#         0x03 - Data type - 0x03 = string data
#         dymo_tt.iSerialNumber - index of serial number data (0x03)
#         dymo_tt.langids[0] - First item from supported languages (1033)
#usb.control.get_descriptor(dymo_tt, 254, 0x03, dymo_tt.iSerialNumber, dymo_tt.langids[0])
#usb.control.get_descriptor(dymo_450, 254, 0x03, dymo_450.iSerialNumber, dymo_450.langids[0])
dymo_tt_serialdata = usb.control.get_descriptor(dymo_tt, 30, 0x03, 0x03, 1033)
dymo_450_serialdata = usb.control.get_descriptor(dymo_450, 30, 0x03, 0x03, 1033)

#print(dymo_tt_serialdata.hex())
#print(dymo_450_serialdata.hex())

# returns the string representation of the serial number received from the device
#print(dymo_tt_serialdata[2:len(dymo_tt_serialdata)].decode('utf-16-le'))
#print(dymo_450_serialdata[2:30].decode('utf-16-le'))

# returns raw bytes representation of encoded data
#print(dymo_tt_serialdata[2:30].decode('utf-8'))
#print(dymo_450_serialdata[2:30].decode('utf-8'))

def get_serial_raw(dev):
    return usb.control.get_descriptor(dev, 254, usb.DT_STRING, dev.iSerialNumber, dev.langids[0])

def get_device_id(dev):
    # Get the IEEE 1284 Device ID string
    # ref: https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/LegacyCollaterals/01233a.pdf
    GET_DEVICE_ID = 0xa1   # USBPRINTER protocol bmRequestType printer protocol request
    bRequest = 0x00        # USBPRINTER protocol bRequest get_device_id
    GET_PORT_STATUS = 0x01 # USBPRINTER protocol bRequest get_port_status (returns 1 byte)
    SOFT_RESET = 0x21      # USBPRINTER protocol bmRequestType soft reset
    wValue = 0             # USBPRINTER protocol Config Index
    wIndex = 0             # USBPRINTER protocol interface index and alt setting
    wLength = 1023         # Max buffer length
    # ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, msg or buffsize)
    return dev.ctrl_transfer(GET_DEVICE_ID, bRequest, wValue, wIndex, wLength)

def get_serial(dev):
    rawserial = get_serial_raw(dev)
    # Return just the data portion of the descriptor data
    # First byte is length of returned data, second byte is type of returned data (30 and 3 expected)
    rawserial = rawserial[2:len(rawserial)].tobytes()
    if dev.idVendor == 0x0922 and dev.idProduct == 0x0018:
        # Because the Twin Turbo returns invalid serial data, we drop the first 2 bytes
        # and decode the remainder as utf-16-be (big-endian)
        return rawserial[2:len(rawserial)].decode('utf-16-be')
    else:
        return rawserial.decode('utf-16-le')

def fix_serial(uri_line):
    if not uri_line.startswith("direct usb://DYMO/LabelWriter%20Twin%20Turbo?serial="):
        return uri_line

    # Extract existing serial
    match = re.search(r'serial=([^\s]+)', uri_line)
    if not match:
        return uri_line

    serial = match.group(1)

    # If the serial is all question marks, replace it
    if set(serial) == {'?'}:
        # Generate fallback serial using USB bus/device number from URI line
        # CUPS output looks like:
        # direct usb://DYMO/... "DYMO LabelWriter Twin Turbo" usb://...
        # We'll add a stable dummy fallback here
        #dummy_serial = "DYMOFIX-" + str(abs(hash(uri_line)) % 10000)
        new_serial = get_serial()
        uri_line = uri_line.replace(f"serial={serial}", f"serial={dummy_serial}")

    return uri_line

#for line in sys.stdin:
#    sys.stdout.write(fix_serial(line))
