from enum import Enum
import ctypes
from ctypes import *

class EmCCDType(Enum):
    SP_CCD_SONY = 0
    SP_CCD_TOSHIBA = 1
    SP_CCD_PDA = 2        # Back thinned CCD
    SP_CCD_G9212 = 3      # InGaAs
    SP_CCD_S10420 = 4     # Non Cooled BT Back thinned CCD
    SP_CCD_G92XX_256 = 5
    SP_CCD_S10141 = 6
    SP_CCD_TCD1304AP = 7

class EmTriggerMode(Enum):
    SP_TRIGGER_FREERUN_PREV = 1
    SP_TRIGGER_FREERUN_NEXT = 2
    SP_TRIGGER_SOFTWARE = 3
    SP_TRIGGER_EXTERNAL = 4
    SP_TRIGGER_SYNCHRONOUS = 5

class EmErrorCode(Enum):
    SP_NO_ERROR = 1
    SP_ERROR_DEVICE_IO_CTRL = -101
    SP_ERROR_OPEN_DRIVER = -102
    SP_ERROR_OPEN_FILE = -103
    SP_ERROR_EEPROM_READ = -104
    SP_ERROR_MEMORY_ALLOC = -105
    SP_ERROR_AUTODARK = -106
    SP_ERROR_NOTSUPORT_DEV = -107
    SP_ERROR_INVALIDHANDLE = -108
    SP_ERROR_INPUT_PARAM = -109
    SP_ERROR_SHUTTER_VALUE = -110
    SP_ERROR_FW_POSITION = -111
    SP_EEPROM_READ_ERROR = -112
    SP_ERROR_EEPROM_EMPTY = -113
    SP_ERROR_DATA_LACK = -114
    SP_ERROR_NOTFINDDEVICE = -115
    SP_ERROR_ALREAYDOPENOTHERINF = -116
    SP_ERROR_WAIT_TIMEOUT = -117
    SP_ERROR_SCANNUM_RANGE = -118
    SP_ERROR_SMOOTH_RANGE = -119
    SP_ERROR_INVALIDVALUE = -120
    SP_ERROR_INVALID_INPUTCHANNEL = -121
    SP_ERROR_CHECKINTERFACE = -122
    SP_ERROR_MEMORY_EMPTY = -123
    SP_ERROR_NOTSUPORT_MODE = -124
    SP_ERROR_COM_SETTING = -201
    SP_ERROR_COM_READ = -202
    SP_ERROR_COM_WRITE = -203
    SP_ERROR_COM_OPERATING = -204
    SP_ERROR_COM_NOTMATCHDEV = -205
    SP_ERROR_COM_NOTCONNECTION = -206
    SP_ERROR_ETH_IPSCAN = -301
    SP_ERROR_ETH_NOTMATCHDEV = -302
    SP_ERROR_ETH_SOCKETCREATE = -303
    SP_ERROR_ETH_SOCKETCONNECT = -304
    SP_ERROR_ETH_TIMEOUT = -305
    SP_ERROR_ETH_RECEIVE = -306
    SP_ERROR_ETH_RECVBUFFER = -307
    SP_ERROR_ETH_DISCONNECT = -308
    SP_ERROR_ETH_SENDPACKET = -309
    SP_ERROR_ETH_PACKET_SIZE_SMALL = -310

class EmCCDPixelNumber(Enum):
    SP_CCD_PIXEL_G92XX_256 = 256
    SP_CCD_PIXEL_G92XX_256_REAL = 256
    SP_CCD_PIXEL_G9212 = 512
    SP_CCD_PIXEL_G9212_REAL = 512
    SP_CCD_PIXEL_PDA = 1056
    SP_CCD_PIXEL_PDA_REAL = 1024
    SP_CCD_PIXEL_SONY = 2080
    SP_CCD_PIXEL_SONY_REAL = 2048
    SP_CCD_PIXEL_TOSHIBA = 3680
    SP_CCD_PIXEL_TOSHIBA_REAL = 3648
    SP_CCD_PIXEL_S10420 = 2080
    SP_CCD_PIXEL_S10420_REAL = 2048
    SP_CCD_PIXEL_S10141 = 2080
    SP_CCD_PIXEL_S10141_REAL = 2048
    SP_CCD_PIXEL_TCD1304AP = 3680
    SP_CCD_PIXEL_TCD1304AP_REAL = 3648

class EmScanMode(Enum):
    SP_SCAN_ALLDEVICE = 0
    SP_SCAN_CANNECTABLE = 1

class EmInterfaceType(Enum):
    SP_INTERFACE_USB = 0
    SP_INTERFACE_ETHERNET = 1

class EmNETMode(Enum):
    SP_NETMODE_DHCP = 0
    SP_NETMODE_STATIC = 1

class EmCalWavelength(Enum):
    SP_CALWL_COEFFICIENT = 0
    SP_CALWL_CALPOINT = 1

class _DeviceList(Structure):
    _fields_ = [
        ('sInterfaceType', c_short),
        ('strCOM', c_char * 100),
        ('strModel', c_char * 100),
        ('strSerial', c_char * 100),
        ('strIPAddr', c_char * 100),
    ]

class _DevInformation(Structure):
    _fields_ = [
        ('strCOM', c_char * 100),
        ('strModel', c_char * 100),
        ('strSerial', c_char * 100),
        ('strIPAddr', c_char * 100),
        ('strStaticIPAddr', c_char * 100),
        ('strMACAddr', c_char * 100),
        ('dWLTable', c_double * 3648),
        ('iDummyPixel', c_int),
        ('iInttime', c_int),
        ('iTimeavg', c_int),
        ('iTotPixel', c_int),
        ('iRealPixel', c_int),
        ('sTrgMode', c_short),
        ('sInterfaceType', c_short),
        ('sNetMode', c_short),
        ('sChannel', c_short),
        ('sCCDType', c_short),
    ]

class SPdbUSBm:
    def __init__(self, dll_path):
        # DLL loading
        # Set the file path
        self.dll = ctypes.WinDLL(dll_path)
        self._setup_functions()

    def _setup_functions(self):
        # Function declarations
        self.dll.spNScanDevice.argtypes = [c_short]
        self.dll.spNScanDevice.restype = c_short

        self.dll.spNScanDevice_IP.argtypes = [ctypes.c_char_p, c_short, c_short]
        self.dll.spNScanDevice_IP.restype = c_short

        self.dll.spNConnect.argtypes = [c_short, c_char_p]
        self.dll.spNConnect.restype = c_short

        self.dll.spNGetDeviceList.argtypes = [POINTER(c_byte)]
        self.dll.spNGetDeviceList.restype = c_short

        self.dll.spNGetDevParam.argtypes = [POINTER(_DevInformation), c_short]
        self.dll.spNGetDevParam.restype = c_short

        self.dll.spNDevInfo.argtypes = [ctypes.c_char_p, ctypes.c_char_p, POINTER(c_short), c_short]
        self.dll.spNDevInfo.restype = c_short

        self.dll.spNGetCCDType.argtypes = [POINTER(c_short), c_short]
        self.dll.spNGetCCDType.restype = c_short

        self.dll.spNGetEEPROM.argtypes = [ctypes.c_char_p, c_short]
        self.dll.spNGetEEPROM.restype = c_short

        self.dll.spNSetIntTime.argtypes = [c_int, c_short]
        self.dll.spNSetIntTime.restype = c_short

        self.dll.spNSetDBLIntTime.argtypes = [c_double, c_short]
        self.dll.spNSetDBLIntTime.restype = c_short

        self.dll.spNSetTimeAvg.argtypes = [c_short, c_short]
        self.dll.spNSetTimeAvg.restype = c_short

        self.dll.spNSetTrgMode.argtypes = [c_short, c_short]
        self.dll.spNSetTrgMode.restype = c_short

        self.dll.spNSetDevice.argtypes = [c_int, c_short, c_short, c_short]
        self.dll.spNSetDevice.restype = c_short

        self.dll.spNGetWLTable.argtypes = [POINTER(c_double), c_short]
        self.dll.spNGetWLTable.restype = c_short

        self.dll.spNReadDataEx.argtypes = [POINTER(c_int), c_short]
        self.dll.spNReadDataEx.restype = c_short

        self.dll.spNGetNETInfo.argtypes = [ctypes.c_char_p, ctypes.c_char_p, POINTER(c_short), c_short]
        self.dll.spNGetNETInfo.restype = c_short

        self.dll.spNCheckConnection.argtypes = [c_short]
        self.dll.spNCheckConnection.restype = c_bool

        self.dll.spNDevClose.argtypes = [c_short]
        self.dll.spNDevClose.restype = c_short

        self.dll.spNSetShutterPos.argtypes = [c_short, c_short]
        self.dll.spNSetShutterPos.restype = c_short

        self.dll.spNAutoDark.argtypes = [c_short, c_short]
        self.dll.spNAutoDark.restype = c_short

        self.dll.spNReadDataExOutTrg.argtypes = [POINTER(c_int), c_short]
        self.dll.spNReadDataExOutTrg.restype = c_short

        self.dll.spNSetOutTrgPin.argtypes = [c_short, c_short, c_short]
        self.dll.spNSetOutTrgPin.restype = c_short

        self.dll.spNGetErrorString.argtypes = [c_short]
        self.dll.spNGetErrorString.restype = c_char_p

        self.dll.spNStartBurstData.argtypes = [c_int, c_short]
        self.dll.spNStartBurstData.restype = c_short

        self.dll.spNStartSyncTrgData.argtypes = [c_int, c_short]
        self.dll.spNStartSyncTrgData.restype = c_short

        self.dll.spNStopSyncTrgData.argtypes = [c_short]
        self.dll.spNStopSyncTrgData.restype = c_short

        self.dll.spNGetBurstSingleData.argtypes = [POINTER(c_int), c_short]
        self.dll.spNGetBurstSingleData.restype = c_short

        self.dll.spNGetBurstData_DotNet.argtypes = [c_int, POINTER(c_byte),c_short]
        self.dll.spNGetBurstData_DotNet.restype = c_short

        self.dll.spNPolyFit.argtypes = [POINTER(c_double), POINTER(c_double), c_short, POINTER(c_double), c_short]
        self.dll.spNPolyFit.restype = c_short

        self.dll.spNPolyCalc.argtypes = [POINTER(c_double), c_short, c_double, POINTER(c_double)]
        self.dll.spNPolyCalc.restype = c_short

        self.dll.spNReadWLCalCoeff_User.argtypes = [POINTER(c_double), POINTER(c_short), c_short]
        self.dll.spNReadWLCalCoeff_User.restype = c_short

        self.dll.spNWriteWLCalCoeff_User.argtypes = [POINTER(c_double), c_short, c_short]
        self.dll.spNWriteWLCalCoeff_User.restype = c_short

        self.dll.spNReadWLCalPoint_User.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_short), c_short]
        self.dll.spNReadWLCalPoint_User.restype = c_short

        self.dll.spNWriteWLCalPoint_User.argtypes = [POINTER(c_double), POINTER(c_double), c_short, c_short]
        self.dll.spNWriteWLCalPoint_User.restype = c_short

        self.dll.spNGetWLTable_User.argtypes = [POINTER(c_double), c_short, c_short]
        self.dll.spNGetWLTable_User.restype = c_short

        self.dll.spNCalBinningAvg.argtypes = [c_short, POINTER(c_int), c_short, POINTER(c_double)]
        self.dll.spNCalBinningAvg.restype = c_short

    def NScanDevice(self, scanMode=1):
        return self.dll.spNScanDevice(c_short(scanMode))

    def NScanDevice_IP(self, pcNetworkID, starthostID, endHostID):
        pcNetworkID_bytes = pcNetworkID.encode('utf-8')
        # c_char array generation
        pcNetworkID_arr = (c_char * (len(pcNetworkID_bytes) + 1))()  # null ending character including
        pcNetworkID_arr[:] = pcNetworkID_bytes + b'\0'  # null ending including
        return self.dll.spNScanDevice_IP(pcNetworkID_arr, c_short(starthostID), c_short(endHostID))

    def NGetDeviceList(self, stDevList):
        # Create a byte array for the device list structure
        device_array_bytes = (c_byte * sizeof(stDevList))()  # Create a byte array

        # Copy the stDevList to the byte array
        memmove(device_array_bytes, addressof(stDevList), sizeof(stDevList))

        # Call the DLL function to get the device list
        result = self.dll.spNGetDeviceList(device_array_bytes)

        # Copy the results back to the original structure
        memmove(addressof(stDevList), device_array_bytes, sizeof(stDevList))

        return result

    def NGetDevParam(self, stDevInfo, sChannel):
        return self.dll.spNGetDevParam(byref(stDevInfo), c_short(sChannel))

    def NConnect(self, sInterFaceType, strConnectAddr):
        return self.dll.spNConnect(c_short(sInterFaceType), strConnectAddr)

    def NDevInfo(self, Model, Serial, sInterfaceType, Channel):
        model_str = create_string_buffer(Model)
        serial_str = create_string_buffer(Serial)
        interface_type = c_short(sInterfaceType)
        return self.dll.spNDevInfo(model_str, serial_str, byref(interface_type), c_short(Channel))

    def NGetCCDType(self, sCCDType, Channel):
        return self.dll.spNGetCCDType(byref(c_short(sCCDType)), c_short(Channel))

    def NGetEEPROM(self, pcEEPData, Channel):
        eeprom_data = create_string_buffer(pcEEPData)
        return self.dll.spNGetEEPROM(eeprom_data, c_short(Channel))

    def NSetIntTime(self, lIntTime, Channel):
        return self.dll.spNSetIntTime(c_int(lIntTime), c_short(Channel))

    def NSetDBLIntTime(self, dIntTime, Channel):
        return self.dll.spNSetDBLIntTime(c_double(dIntTime), c_short(Channel))

    def NSetTimeAvg(self, sAvgTime, Channel):
        return self.dll.spNSetTimeAvg(c_short(sAvgTime), c_short(Channel))

    def NSetTrgMode(self, sTrgMode, Channel):
        return self.dll.spNSetTrgMode(c_short(sTrgMode), c_short(Channel))

    def NSetShutterPos(self, sCodeShutter, Channel):
        return self.dll.spNSetShutterPos(c_short(sCodeShutter), c_short(Channel))

    def NSetDevice(self, lIntTime, sAverage, sTrgMode, Channel):
        return self.dll.spNSetDevice(c_int(lIntTime), c_short(sAverage), c_short(sTrgMode), c_short(Channel))

    def NGetWLTable(self, WLTable, Channel):
        result = self.dll.spNGetWLTable(WLTable, c_short(Channel))
        return result

    def NReadDataEx(self, lpArray, Channel):
        return self.dll.spNReadDataEx(lpArray, c_short(Channel))

    def NGetNETInfo(self, pcIPAddr, pcMACAddr, sNetMode, Channel):
        ip_addr = create_string_buffer(pcIPAddr)
        mac_addr = create_string_buffer(pcMACAddr)
        net_mode = c_short(sNetMode)
        return self.dll.spNGetNETInfo(ip_addr, mac_addr, byref(net_mode), c_short(Channel))

    def NCheckConnection(self, Channel):
        return bool(self.dll.spNCheckConnection(c_short(Channel)))

    def NDevClose(self, Channel):
        return self.dll.spNDevClose(c_short(Channel))

    def NAutoDark(self, sAutoDark, Channel):
        return self.dll.spNAutoDark(c_short(sAutoDark), c_short(Channel))

    def NReadDataExOutTrg(self, lpArray, Channel):
        data_arr = (c_int * len(lpArray))(*lpArray)
        return self.dll.spNReadDataExOutTrg(data_arr, c_short(Channel))

    def NSetOutTrgPin(self, sOutPin, sDelay, Channel):
        return self.dll.spNSetOutTrgPin(c_short(sOutPin), c_short(sDelay), c_short(Channel))

    def NGetErrorString(self, sErrorCode):
        return self.dll.spNGetErrorString(c_short(sErrorCode)).decode('utf-8')

    def NStartBurstData(self, iCount, sChannel):
        return self.dll.spNStartBurstData(c_int(iCount), c_short(sChannel))

    def NStartSyncTrgData(self, iCount, sChannel):
        return self.dll.spNStartSyncTrgData(c_int(iCount), c_short(sChannel))

    def NStopSyncTrgData(self, sChannel):
        return self.dll.spNStopSyncTrgData(c_short(sChannel))

    def NGetBurstSingleData(self, plData, sChannel):
        return self.dll.spNGetBurstSingleData(plData, c_short(sChannel))

    def NGetBurstData(self, iCount, plDataList, sChannel,size):
        flat_data = [item for sublist in plDataList for item in sublist]
        byte_array = (c_byte * (len(flat_data) * sizeof(c_int)))()

        # copy c_int values to c_byte array 
        for i in range(len(flat_data)):
            byte_array[i] = flat_data[i]
        # Call the DLL function to get the device list
        result = self.dll.spNGetBurstData_DotNet(c_int(iCount), byte_array, c_short(sChannel))

        for i in range(iCount):
            for j in range(size):
                plDataList[i][j] = c_int.from_buffer(byte_array, (i * size + j) * sizeof(c_int))
        return result

    def NPolyFit(self, x, y, sNumPts, pdCoeffs, sOrder):
        return self.dll.spNPolyFit(x, y, c_short(sNumPts), pdCoeffs, c_short(sOrder))

    def NPolyCalc(self, pdCoefs, sOrder, x, y):
        temp_y = c_double()
        result = self.dll.spNPolyCalc(pdCoefs, c_short(sOrder), c_double(x),byref(temp_y))
        y = temp_y
        return result

    def NReadWLCalCoeff_User(self, pdCoefs, sOrder, sChannel):
        tmep = c_short()
        result= self.dll.spNReadWLCalCoeff_User(pdCoefs, byref(tmep), c_short(sChannel))
        sOrder = tmep
        return result

    def NWriteWLCalCoeff_User(self, pdCoefs, sOrder, sChannel):
        return self.dll.spNWriteWLCalCoeff_User(pdCoefs, c_short(sOrder), c_short(sChannel))

    def NReadWLCalPoint_User(self, pdPixel, pdWavelength, sPointNum, sChannel):
        temp = c_short()
        result = self.dll.spNReadWLCalPoint_User(pdPixel, pdWavelength, byref(temp), c_short(sChannel))
        sPointNum = temp
        return result

    def NWriteWLCalPoint_User(self, pdPixel, pdWavelength, sPointNum, sChannel):
        return self.dll.spNWriteWLCalPoint_User(pdPixel, pdWavelength, c_short(sPointNum), c_short(sChannel))

    def NGetWLTable_User(self, pdWLTable, sSelMemory, sChannel):
        return self.dll.spNGetWLTable_User(pdWLTable, c_short(sSelMemory), c_short(sChannel))

    def NCalBinningAvg(self, sCCDType, lpInputData, sBinning, dpOutputData):
        return self.dll.spNCalBinningAvg(c_short(sCCDType), lpInputData, c_short(sBinning), dpOutputData)
