import ctypes
import os
import errno
from scisdk.scisdk_defines import *


class SciSDK:
    scisdk_dll = None
    lib_ptr = None
    libname = None
    
    def __init__(self) -> None:
        # Determina il nome della libreria in base al sistema operativo
        if os.name == 'nt':
            self.libname = "SciSDK_DLL.dll"
        else:
            self.libname = "libscisdk.so"
        
        try:
            self.scisdk_dll = ctypes.CDLL(self.libname)
            init_lib_api = self.scisdk_dll.SCISDK_InitLib
            init_lib_api.restype = ctypes.c_void_p
            self.lib_ptr = init_lib_api()            
        except OSError as e:
            self._handle_load_error(e)
            raise e
    
    def _handle_load_error(self, e: OSError) -> None:
        """Gestisce gli errori di caricamento della libreria con messaggi comprensibili."""
        
        error_code = getattr(e, 'errno', None)
        
        # Messaggio base di installazione
        install_msg = (
            "\n"
            "Please make sure that SciSDK is correctly installed.\n"
            "This pip package only contains the Python wrapper.\n"
            "You need to install the full SciSDK package separately from:\n"
            "  https://www.sci-compiler.com/user-guide/readout/scisdk/"
        )
        
        # Messaggio specifico per piattaforma
        if os.name == 'nt':
            path_hint = (
                "\nOn Windows, ensure that:\n"
                "  - SciSDK is installed\n"
                "  - The DLL path is in your PATH environment variable\n"
                "  - You're using the correct architecture (32/64 bit)"
            )
        elif sys.platform == 'darwin':
            path_hint = (
                "\nOn macOS, ensure that:\n"
                "  - SciSDK is installed\n"
                "  - The library is in /usr/local/lib or DYLD_LIBRARY_PATH is set"
            )
        else:
            path_hint = (
                "\nOn Linux, ensure that:\n"
                "  - SciSDK is installed\n"
                "  - The library is in /usr/lib, /usr/local/lib, or LD_LIBRARY_PATH is set\n"
                "  - Run 'sudo ldconfig' after installation"
            )
        
        # Determina il tipo di errore
        if error_code == errno.ENOENT or "cannot open shared object" in str(e).lower() or "not found" in str(e).lower():
            print(f"Error: {self.libname} not found")
            print(install_msg)
            print(path_hint)
        
        elif error_code == errno.EINVAL or "wrong architecture" in str(e).lower() or "wrong ELF class" in str(e).lower():
            print(f"Error: {self.libname} has the wrong architecture for the current process")
            print(f"Python architecture: {8 * ctypes.sizeof(ctypes.c_void_p)}-bit")
            print("Make sure you have installed the matching SciSDK version (32/64 bit).")
        
        elif (hasattr(errno, 'ELIBBAD') and error_code == errno.ELIBBAD) or "undefined symbol" in str(e).lower():
            print(f"Error: {self.libname} has missing dependencies")
            print("Some required libraries may not be installed.")
            if os.name != 'nt':
                print("Try running: ldd " + self.libname + " to check dependencies")
        
        elif os.name == 'nt' and hasattr(e, 'winerror'):
            # Gestione errori specifici Windows
            winerror = e.winerror
            if winerror == 126:  # ERROR_MOD_NOT_FOUND
                print(f"Error: {self.libname} or one of its dependencies not found")
                print(install_msg)
                print(path_hint)
            elif winerror == 193:  # ERROR_BAD_EXE_FORMAT
                print(f"Error: {self.libname} has incompatible architecture")
                print(f"Python architecture: {8 * ctypes.sizeof(ctypes.c_void_p)}-bit")
                print("Make sure you have installed the matching SciSDK version.")
            else:
                print(f"Error: unable to load {self.libname}")
                print(f"Windows error code: {winerror}")
                print(f"Details: {e}")
                print(install_msg)
        
        else:
            print(f"Error: unable to load {self.libname}")
            print(f"Details: {e}")
            print(install_msg)
            print(path_hint)




        
        
    def AddNewDevice(self, device_path: str, device_model: str, json_file_path: str, name: str) -> int:
        add_new_device_api = self.scisdk_dll.SCISDK_AddNewDevice
        add_new_device_api.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p]
        add_new_device_api.restype = ctypes.c_int
        # convert strings to bytes array
        b_device_path = device_path.encode('utf-8')
        b_device_model = device_model.encode('utf-8')
        b_json_file_path = json_file_path.encode('utf-8')
        b_name = name.encode('utf-8')
        # call C lib function
        err = add_new_device_api(b_device_path, b_device_model, b_json_file_path, b_name, self.lib_ptr)
        return err
        
    def DetachDevice(self, name: str) -> int:
        detach_device_api = self.scisdk_dll.SCISDK_DetachDevice
        detach_device_api.argtypes = [ctypes.c_char_p, ctypes.c_void_p]
        detach_device_api.restype = ctypes.c_int
        # convert string to bytes array
        b_name = name.encode('utf-8')
        # call C lib function
        err = detach_device_api(b_name, self.lib_ptr)
        return err

    def GetComponentList(self, board_name: str, node_type: str, return_json: bool):
        get_components_list_api = self.scisdk_dll.SCISDK_GetComponentList
        get_components_list_api.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p), ctypes.c_bool, ctypes.c_void_p]
        get_components_list_api.restype = ctypes.c_int
        # convert string to bytes array
        board_name_b = board_name.encode('utf-8')
        node_type_b = node_type.encode('utf-8')
        ret_string_char_ptr = ctypes.c_char_p()
        # call C lib function
        err = get_components_list_api(ctypes.c_char_p(board_name_b), ctypes.c_char_p(node_type_b), ctypes.byref(ret_string_char_ptr), ctypes.c_bool(return_json), self.lib_ptr)
        return err, ret_string_char_ptr.value.decode('utf-8')

    def s_error(self, value: int) -> int:
        s_error_api = self.scisdk_dll.SCISDK_s_error
        s_error_api.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p), ctypes.c_void_p]
        s_error_api.restype = ctypes.c_int
        # convert string to bytes array
        ret_string_char_ptr = ctypes.c_char_p()
        err = s_error_api(ctypes.c_int(value), ctypes.byref(ret_string_char_ptr),self.lib_ptr)
        return err, ret_string_char_ptr.value.decode('utf-8')

    def SetParameterInteger(self, path: str, val: int) -> int:
        set_parameter_api = self.scisdk_dll.SCISDK_SetParameterInteger
        set_parameter_api.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.c_void_p]
        set_parameter_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        # call C lib function
        err = set_parameter_api(ctypes.c_char_p(path_b), val, self.lib_ptr)
        return err

    def GetParameterInteger(self, path: str):
        get_parameter_api = self.scisdk_dll.SCISDK_GetParameterInteger
        get_parameter_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_int), ctypes.c_void_p]
        get_parameter_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        ret_int = ctypes.c_int()
        # call C lib function
        err = get_parameter_api(ctypes.c_char_p(path_b), ctypes.byref(ret_int), self.lib_ptr)
        return err, ret_int.value

    def SetParameterUInteger(self, path: str, val: int) -> int:
        set_parameter_api = self.scisdk_dll.SCISDK_SetParameterUInteger
        set_parameter_api.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.c_void_p]
        set_parameter_api.restype = ctypes.c_uint
        # convert string to bytes array
        path_b = path.encode('utf-8')
        # call C lib function
        err = set_parameter_api(ctypes.c_char_p(path_b), val, self.lib_ptr)
        return err

    def GetParameterUInteger(self, path: str):
        get_parameter_api = self.scisdk_dll.SCISDK_GetParameterUInteger
        get_parameter_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_int), ctypes.c_void_p]
        get_parameter_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        ret_int = ctypes.c_uint()
        # call C lib function
        err = get_parameter_api(ctypes.c_char_p(path_b), ctypes.byref(ret_int), self.lib_ptr)
        return err, ret_int.value

    def SetParameterString(self, path: str, value: str):
        set_parameter_api = self.scisdk_dll.SCISDK_SetParameterString
        set_parameter_api.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p]
        set_parameter_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        value_b = value.encode('utf-8')
        # call C lib function
        err = set_parameter_api(ctypes.c_char_p(path_b), ctypes.c_char_p(value_b), self.lib_ptr)
        return err

    def GetParameterString(self, path: str):
        get_parameter_api = self.scisdk_dll.SCISDK_GetParameterString
        get_parameter_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p), ctypes.c_void_p]
        get_parameter_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        value_b = ctypes.c_char_p()
        # call C lib function
        err = get_parameter_api(ctypes.c_char_p(path_b), ctypes.byref(value_b), self.lib_ptr)
        return err, value_b.value.decode('utf-8')

    def SetParameterDouble(self, path: str, value: float):
        set_parameter_api = self.scisdk_dll.SCISDK_SetParameterDouble
        set_parameter_api.argtypes = [ctypes.c_char_p, ctypes.c_double, ctypes.c_void_p]
        set_parameter_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        # call C lib function
        err = set_parameter_api(ctypes.c_char_p(path_b), ctypes.c_double(value), self.lib_ptr)
        return err

    def GetParameterDouble(self, path:str):
        get_parameter_api = self.scisdk_dll.SCISDK_GetParameterDouble
        get_parameter_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_double), ctypes.c_void_p]
        get_parameter_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        value = ctypes.c_double()
        # call C lib function
        err = get_parameter_api(ctypes.c_char_p(path_b), ctypes.byref(value), self.lib_ptr)
        return err, value.value

    def AllocateBuffer(self, path: str, size = None):
        buffer_pointer = None
        buffer_type = 0
        if path.endswith("board_api") :
            res, type = self.GetParameterString(path+"/buffer_type")
        if path.endswith("board_api/") :
            res, type = self.GetParameterString(path+"buffer_type")
        else:
            res, type = self.GetParameterString(path+".buffer_type")

        if(res == 0):
            if type == "SCISDK_OSCILLOSCOPE_DECODED_BUFFER":
                buffer_pointer = ctypes.POINTER(OscilloscopeDecodedBuffer)
                buffer_type = 1
            elif type == "SCISDK_OSCILLOSCOPE_RAW_BUFFER":
                buffer_pointer = ctypes.POINTER(OscilloscopeRawBuffer)
            elif type == "SCISDK_OSCILLOSCOPE_DUAL_DECODED_BUFFER":
                buffer_pointer = ctypes.POINTER(OscilloscopeDualDecodedBuffer)
                buffer_type = 1
            elif type == "SCISDK_OSCILLOSCOPE_DUAL_RAW_BUFFER":
                buffer_pointer = ctypes.POINTER(OscilloscopeDualRawBuffer)
            elif type == "SCISDK_DIGITIZER_DECODED_BUFFER":
                buffer_pointer = ctypes.POINTER(DigitizerDecodedBuffer)
                buffer_type = 1
            elif type == "SCISDK_LIST_RAW_BUFFER":
                buffer_pointer = ctypes.POINTER(ListRawBuffer)
            elif type == "SCISDK_CP_DECODED_BUFFER":
                buffer_pointer = ctypes.POINTER(CustomPacketDecodedBuffer)
                buffer_type = 1
            elif type == "SCISDK_CP_RAW_BUFFER":
                buffer_pointer = ctypes.POINTER(CustomPacketRawBuffer)
            elif type == "SCISDK_RM_RAW_BUFFER":
                buffer_pointer = ctypes.POINTER(RatemeterRawBuffer)
            elif type == "SCISDK_SPECTRUM_DECODED_BUFFER":
                buffer_pointer = ctypes.POINTER(SpectrumDecodedBuffer)
                buffer_type = 1
            elif type == "SCISDK_2DHISTOGRAM_DECODED_BUFFER":
                buffer_pointer = ctypes.POINTER(Histogram2DDecodedBuffer)
                buffer_type = 1
            elif type == "SCISDK_FFT_DECODED_BUFFER":
                buffer_pointer = ctypes.POINTER(FFTDecodedBuffer)
                buffer_type = 1
            elif type == "SCISDK_FFT_RAW_BUFFER":
                buffer_pointer = ctypes.POINTER(FFTRawBuffer)
            elif type == "SCISDK_FRAME_DECODED_BUFFER":
                buffer_pointer = ctypes.POINTER(FramePacketDecodedBuffer)
                buffer_type = 1
            elif type == "SCISDK_FRAME_RAW_BUFFER":
                buffer_pointer = ctypes.POINTER(FramePacketRawBuffer)
            elif type == "SCISDK_CITIROC_DECODED_BUFFER":
                buffer_pointer = ctypes.POINTER(CitirocDecodedBuffer)
                buffer_type = 1
            elif type == "SCISDK_CITIROC_RAW_BUFFER":
                buffer_pointer = ctypes.POINTER(CitirocRawBuffer)
            elif type == "SCISDK_PETIROC_DECODED_BUFFER":
                buffer_pointer = ctypes.POINTER(PetirocDecodedBuffer)
                buffer_type = 1
            elif type == "SCISDK_PETIROC_RAW_BUFFER":
                buffer_pointer = ctypes.POINTER(PetirocRawBuffer)      
            elif type == "SCISDK_FE_SCOPE_EVENT":
                buffer_pointer = ctypes.POINTER(FEScopePacket)
                buffer_type = 1
            elif type == "SCISDK_FE_OPENDPP_EVENT":
                buffer_pointer = ctypes.POINTER(FEOpenDppPacket)
                buffer_type = 1
            elif type == "SCISDK_LOGICANALYSER_DECODED_BUFFER":
                buffer_pointer = ctypes.POINTER(LogicAnalyserDecodedBuffer)
                buffer_type = 1
            else:
                raise Exception("Isn't a valid buffer type") 

            if size == None:
                allocate_buffer_api = self.scisdk_dll.SCISDK_AllocateBuffer
                allocate_buffer_api.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(buffer_pointer), ctypes.c_void_p]    
                allocate_buffer_api.restype = ctypes.c_int
                # convert string to bytes array
                path_b = path.encode('utf-8')
                # call C lib function
                buf_tmp = buffer_pointer()
                err = allocate_buffer_api(ctypes.c_char_p(path_b), ctypes.c_int(buffer_type), ctypes.byref(buf_tmp), self.lib_ptr)
                if err == 0:
                    buffer = buf_tmp.contents
                    return err, buffer   
                else:
                    return err, None
            else:
                allocate_buffer_api = self.scisdk_dll.SCISDK_AllocateBufferSize
                allocate_buffer_api.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(buffer_pointer), ctypes.c_void_p, ctypes.c_int]    
                allocate_buffer_api.restype = ctypes.c_int
                # convert string to bytes array
                path_b = path.encode('utf-8')
                # call C lib function
                buf_tmp = buffer_pointer()
                err = allocate_buffer_api(ctypes.c_char_p(path_b), ctypes.c_int(buffer_type), ctypes.byref(buf_tmp), self.lib_ptr, size)
                if err == 0:
                    buffer = buf_tmp.contents
                    return err, buffer
                else:
                    return err, None



    # read data
    def ReadData(self, path: str, buffer):
        read_data_api = self.scisdk_dll.SCISDK_ReadData
        if type(buffer) == OscilloscopeDecodedBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(OscilloscopeDecodedBuffer), ctypes.c_void_p]    
        elif type(buffer) == OscilloscopeRawBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(OscilloscopeRawBuffer), ctypes.c_void_p]    
        elif type(buffer) == OscilloscopeDualDecodedBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(OscilloscopeDualDecodedBuffer), ctypes.c_void_p]    
        elif type(buffer) == OscilloscopeDualRawBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(OscilloscopeDualRawBuffer), ctypes.c_void_p]    
        elif type(buffer) == DigitizerDecodedBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(DigitizerDecodedBuffer), ctypes.c_void_p]    
        elif type(buffer) == ListRawBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(ListRawBuffer), ctypes.c_void_p]    
        elif type(buffer) == CustomPacketDecodedBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(CustomPacketDecodedBuffer), ctypes.c_void_p]    
        elif type(buffer) == CustomPacketRawBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(CustomPacketRawBuffer), ctypes.c_void_p]    
        elif type(buffer) == RatemeterRawBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(RatemeterRawBuffer), ctypes.c_void_p]    
        elif type(buffer) == SpectrumDecodedBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(SpectrumDecodedBuffer), ctypes.c_void_p]    
        elif type(buffer) == Histogram2DDecodedBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(Histogram2DDecodedBuffer), ctypes.c_void_p]    
        elif type(buffer) == FFTDecodedBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(FFTDecodedBuffer), ctypes.c_void_p]    
        elif type(buffer) == FFTRawBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(FFTRawBuffer), ctypes.c_void_p]  
        elif type(buffer) == FramePacketDecodedBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(FramePacketDecodedBuffer), ctypes.c_void_p]   
        elif type(buffer) == FramePacketRawBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(FramePacketRawBuffer), ctypes.c_void_p]      
        elif type(buffer) == CitirocDecodedBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(CitirocDecodedBuffer), ctypes.c_void_p]    
        elif type(buffer) == CitirocRawBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(CitirocRawBuffer), ctypes.c_void_p]    
        elif type(buffer) == PetirocDecodedBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(PetirocDecodedBuffer), ctypes.c_void_p]    
        elif type(buffer) == PetirocRawBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(PetirocRawBuffer), ctypes.c_void_p]    
        elif type(buffer) == FEScopePacket:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(FEScopePacket), ctypes.c_void_p]    
        elif type(buffer) == FEOpenDppPacket:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(FEOpenDppPacket), ctypes.c_void_p]
        elif type(buffer) == LogicAnalyserDecodedBuffer:
            read_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(LogicAnalyserDecodedBuffer), ctypes.c_void_p]
        else:
            raise Exception(type(buffer).__name__ + " isn't a valid buffer type") 

        read_data_api = self.scisdk_dll.SCISDK_ReadData
        read_data_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        err = read_data_api(ctypes.c_char_p(path_b), ctypes.byref(buffer), self.lib_ptr)
        return err, buffer

    def FreeBuffer(self, path: str, buffer):
        free_buffer_api = self.scisdk_dll.SCISDK_FreeBuffer
        buffer_pointer = None
        buffer_type = 0
        if type(buffer) == OscilloscopeDecodedBuffer:
            buffer_pointer = ctypes.POINTER(OscilloscopeDecodedBuffer)
            buffer_type = 1
        elif type(buffer) == OscilloscopeRawBuffer:
            buffer_pointer = ctypes.POINTER(OscilloscopeRawBuffer)
        elif type(buffer) == OscilloscopeDualDecodedBuffer:
            buffer_pointer = ctypes.POINTER(OscilloscopeDualDecodedBuffer)
            buffer_type = 1
        elif type(buffer) == OscilloscopeDualRawBuffer:
            buffer_pointer = ctypes.POINTER(OscilloscopeDualRawBuffer)
        elif type(buffer) == DigitizerDecodedBuffer:
            buffer_pointer = ctypes.POINTER(DigitizerDecodedBuffer)
            buffer_type = 1
        elif type(buffer) == ListRawBuffer:
            buffer_pointer = ctypes.POINTER(ListRawBuffer)
        elif type(buffer) == CustomPacketDecodedBuffer:
            buffer_pointer = ctypes.POINTER(CustomPacketDecodedBuffer)
            buffer_type = 1
        elif type(buffer) == CustomPacketRawBuffer:
            buffer_pointer = ctypes.POINTER(CustomPacketRawBuffer)
        elif type(buffer) == RatemeterRawBuffer:
            buffer_pointer = ctypes.POINTER(RatemeterRawBuffer)
        elif type(buffer) == SpectrumDecodedBuffer:
            buffer_pointer = ctypes.POINTER(SpectrumDecodedBuffer)
            buffer_type = 1
        elif type(buffer) == Histogram2DDecodedBuffer:
            buffer_pointer = ctypes.POINTER(Histogram2DDecodedBuffer)
            buffer_type = 1            
        elif type(buffer) == FFTDecodedBuffer:
            buffer_pointer = ctypes.POINTER(FFTDecodedBuffer)
            buffer_type = 1
        elif type(buffer) == FFTRawBuffer:
            buffer_pointer = ctypes.POINTER(FFTRawBuffer)
        elif type(buffer) == FramePacketDecodedBuffer:
            buffer_pointer = ctypes.POINTER(FramePacketDecodedBuffer)
            buffer_type = 1
        elif type(buffer) == FramePacketRawBuffer:
            buffer_pointer = ctypes.POINTER(FramePacketRawBuffer)
        elif type(buffer) == CitirocDecodedBuffer:
            buffer_pointer = ctypes.POINTER(CitirocDecodedBuffer)
            buffer_type = 1
        elif type(buffer) == CitirocRawBuffer:
            buffer_pointer = ctypes.POINTER(CitirocRawBuffer)
        elif type(buffer) == PetirocDecodedBuffer:
            buffer_pointer = ctypes.POINTER(PetirocDecodedBuffer)
            buffer_type = 1
        elif type(buffer) == PetirocRawBuffer:
            buffer_pointer = ctypes.POINTER(PetirocRawBuffer)
        elif type(buffer) == FEScopePacket:
            buffer_pointer = ctypes.POINTER(FEScopePacket)
        elif type(buffer) == FEOpenDppPacket:
            buffer_pointer = ctypes.POINTER(FEOpenDppPacket)
        elif type(buffer) == LogicAnalyserDecodedBuffer:
            buffer_pointer = ctypes.POINTER(LogicAnalyserDecodedBuffer)
            buffer_type = 1
        else:
            raise Exception(type(buffer).__name__ + " isn't a valid buffer type") 

        free_buffer_api.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(buffer_pointer), ctypes.c_void_p]    
        free_buffer_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        # call C lib function
        buf_tmp = buffer_pointer(buffer)
        err = free_buffer_api(ctypes.c_char_p(path_b), ctypes.c_int(buffer_type), ctypes.byref(buf_tmp), self.lib_ptr)
        return err

    def ExecuteCommand(self, path: str, value: str):
        execute_command_api = self.scisdk_dll.SCISDK_ExecuteCommand
        execute_command_api.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p]    
        execute_command_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        value_b = value.encode('utf-8')
        # call C lib function
        err = execute_command_api(ctypes.c_char_p(path_b), ctypes.c_char_p(value_b), self.lib_ptr)
        return err

    def DecodeData(self, path: str, buffer_in: OscilloscopeRawBuffer):
        decode_data_api = self.scisdk_dll.SCISDK_DecodeData
        decode_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(OscilloscopeRawBuffer), ctypes.POINTER(OscilloscopeDecodedBuffer), ctypes.c_void_p]
        decode_data_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        # call C lib function
        buffer_out = OscilloscopeDecodedBuffer()
        err = decode_data_api(ctypes.c_char_p(path_b), ctypes.byref(buffer_in), ctypes.byref(buffer_out), self.lib_ptr)
        return err, buffer_out

    def SetRegister(self, path: str, value: int):
        set_register_api = self.scisdk_dll.SCISDK_SetRegister
        set_register_api.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.c_void_p]
        set_register_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        # call C lib function
        err = set_register_api(ctypes.c_char_p(path_b), value, self.lib_ptr)
        return err

    def GetRegister(self, path: str):
        get_register_api = self.scisdk_dll.SCISDK_GetRegister
        get_register_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_int), ctypes.c_void_p]
        get_register_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        ret_int = ctypes.c_int()
        # call C lib function
        err = get_register_api(ctypes.c_char_p(path_b), ctypes.byref(ret_int), self.lib_ptr)
        return err, ret_int.value

    def GetAllParameters(self, path: str):
        get_all_parameters_api = self.scisdk_dll.SCISDK_GetAllParameters
        get_all_parameters_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p), ctypes.c_void_p]
        get_all_parameters_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        ret_char_p = ctypes.c_char_p()
        # call C lib function
        err = get_all_parameters_api(ctypes.c_char_p(path_b), ctypes.byref(ret_char_p), self.lib_ptr)
        return err, ret_char_p.value.decode('utf-8')

    def GetParameterDescription(self, path: str):
        get_parameter_description_api = self.scisdk_dll.SCISDK_GetParameterDescription
        get_parameter_description_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p), ctypes.c_void_p]
        get_parameter_description_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        ret_char_p = ctypes.c_char_p()
        # call C lib function
        err = get_parameter_description_api(ctypes.c_char_p(path_b), ctypes.byref(ret_char_p), self.lib_ptr)
        return err, ret_char_p.value.decode('utf-8')

    def GetParameterListOfValues(self, path: str):
        get_parameter_list_of_values_api = self.scisdk_dll.SCISDK_GetParameterListOfValues
        get_parameter_list_of_values_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p), ctypes.c_void_p]
        get_parameter_list_of_values_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        ret_char_p = ctypes.c_char_p()
        # call C lib function
        err = get_parameter_list_of_values_api(ctypes.c_char_p(path_b), ctypes.byref(ret_char_p), self.lib_ptr)
        return err, ret_char_p.value.decode('utf-8')

    def GetParameterMinimumValue(self, path: str):
        get_parameters_min_value_api = self.scisdk_dll.SCISDK_GetParameterMinimumValue
        get_parameters_min_value_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_double), ctypes.c_void_p]
        get_parameters_min_value_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        ret_double_p = ctypes.c_double()
        # call C lib function
        err = get_parameters_min_value_api(ctypes.c_char_p(path_b), ctypes.byref(ret_double_p), self.lib_ptr)
        return err, ret_double_p.value

    def GetParameterMaximumValue(self, path: str):
        get_parameters_max_value_api = self.scisdk_dll.SCISDK_GetParameterMaximumValue
        get_parameters_max_value_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_double), ctypes.c_void_p]
        get_parameters_max_value_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        ret_double_p = ctypes.c_double()
        # call C lib function
        err = get_parameters_max_value_api(ctypes.c_char_p(path_b), ctypes.byref(ret_double_p), self.lib_ptr)
        return err, ret_double_p.value

    def GetParametersProperties(self, path: str):
        get_parameters_properties_api = self.scisdk_dll.SCISDK_GetParametersProperties
        get_parameters_properties_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p), ctypes.c_void_p]
        get_parameters_properties_api.restype = ctypes.c_int
        # convert string to bytes array
        path_b = path.encode('utf-8')
        ret_char_p = ctypes.c_char_p()
        # call C lib function
        err = get_parameters_properties_api(ctypes.c_char_p(path_b), ctypes.byref(ret_char_p), self.lib_ptr)
        return err, ret_char_p.value.decode('utf-8')

    def FreeLib(self):
        free_lib_api = self.scisdk_dll.SCISDK_FreeLib
        free_lib_api.argtypes = [ctypes.c_void_p]
        free_lib_api.restype = ctypes.c_int
        # call C lib function
        err = free_lib_api(self.lib_ptr)
        return err
    
    def GetAttachedDevicesList(self):
        get_attached_devices_list_api = self.scisdk_dll.SCISDK_GetAttachedDevicesList
        get_attached_devices_list_api.argtypes = [ctypes.POINTER(ctypes.c_char_p), ctypes.c_void_p]
        get_attached_devices_list_api.restype = ctypes.c_int
        # call C lib function
        ret_char_p = ctypes.c_char_p()
        err = get_attached_devices_list_api(ctypes.byref(ret_char_p), self.lib_ptr)
        return err, ret_char_p.value.decode('utf-8')
    
    def GetLibraryVersion(self):
        get_library_version_api = self.scisdk_dll.SCISDK_GetLibraryVersion
        get_library_version_api.argtypes = [ctypes.POINTER(ctypes.c_char_p), ctypes.c_void_p]
        get_library_version_api.restype = ctypes.c_int
        # call C lib function
        ret_char_p = ctypes.c_char_p()
        err = get_library_version_api(ctypes.byref(ret_char_p), self.lib_ptr)
        return err, ret_char_p.value.decode('utf-8')



    def WriteData(self, path: str, buffer):
        """
        Writes data from the given buffer to the specified path in the device.
        
        This function handles the buffer for EmulatorEnergySpectrum type and
        prepares the data before calling the SCISDK_WriteData C function.
        """
        # Define the C function for writing data
        write_data_api = self.scisdk_dll.SCISDK_WriteData

        # Check if the buffer is an instance of EmulatorEnergySpectrum
        if isinstance(buffer, EmulatorEnergySpectrum):

            # Set up the argument types for the C function
            write_data_api.argtypes = [ctypes.c_char_p, ctypes.POINTER(EmulatorEnergySpectrum), ctypes.c_void_p]

            # Convert the path to bytes (C compatible string)
            path_b = path.encode('utf-8')

            # Call the SCISDK_WriteData function with the path and buffer pointer
            err = write_data_api(ctypes.c_char_p(path_b), ctypes.byref(buffer), self.lib_ptr)

            # Return the error code from the C API call
            return err
        else:
            raise Exception(f"Invalid buffer type: {type(buffer).__name__}. Expected EmulatorEnergySpectrum.")


