#![allow(non_snake_case)]
use libloading::{ Library, Symbol };
use std::path::{ Path, PathBuf };

/// Pointer to a UTF-16 string (wide char) terminated with `0`.
pub type PCWSTR = *const u16;
/// Mutable pointer to a UTF-16 buffer.
pub type PWSTR = *mut u16;
/// Pointer to an array of UTF-16 string pointers.
pub type PPWSTR = *mut PWSTR;
/// 32-bit boolean, mirrors the Win32 `BOOL` layout used by the C API.
pub type Bool32 = i32;

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpenReturnCodes {
    Ok = 0,
    // --------- Application control and Connection ---------
    ErrorInternal = -1001,
    ErrorVcNotFound = -1002,
    ErrorCreateProcess = -1003,
    ErrorAlreadyRunning = -1004,
    ErrorCloseFailed = -1005,
    ErrorInstanceNotFound = -1006,
    ErrorInstanceBusy = -1007,
    ErrorNoConnection = -1008,
    ErrorVcNotReady = -1009,
    ErrorInvalidParameter = -1010,
    ErrorNoLicenseDeprecated = -1011,
    ErrorConnectionClosed = -1012,
    ErrorS7DosSimulationActive = -1013,
    ErrorVersionIncompatible = -1014,
    ErrorNotSupportedByBackend = -1015,
    WarningWindowEmbedding = 1002,
    WarningVersionClientOld = 1003,
    WarningVersionMiddlewareOld = 1004,
    WarningConnectionCorrupted = 1005,
    // --------- File handling (VCPs, startprofiles, etc.) ---------
    ErrorFileNotFound = -2001,
    ErrorProjectOpen = -2002,
    ErrorLoadFailed = -2003,
    ErrorNoProjectOpen = -2004,
    ErrorWriteProtected = -2005,
    ErrorSimRunning = -2006,
    ErrorWriteFailed = -2007,
    ErrorVcpCloseFailed = -2008,
    ErrorInvalidFile = -2009,
    ErrorCncSwUnavailable = -2010,
    ErrorFileCreationFailed = -2011,
    ErrorFrontendNotInstalled = -2012,
    ErrorFrontendNotLicensedDeprecated = -2013,
    ErrorFilePathTooLong = -2014,
    ErrorInvalidNckscaling = -2015,
    ErrorInvalidNcunumber = -2016,
    // --------- Simulation Control ---------
    ErrorStartSimFailed = -3001,
    ErrorStopSimFailed = -3002,
    ErrorResetSimFailed = -3004,
    ErrorSimSpeedInvalid = -3005,
    ErrorSimNotAvailable = -3006,
    ErrorSimNotRunning = -3007,
    SimRunning = 3001,
    SimNotRunning = 3002,
    // --------- Synchronized Operation ---------
    ErrorEventUnknown = -4001,
    ErrorNoEventsRegistered = -4002,
    ErrorSimInterrupted = -4003,
    // --------- Data Access ---------
    ErrorUnknownElement = -5001,
    ErrorInvalidDictId = -5002,
    ErrorInsufficientMemory = -5003,
    ErrorNoarray = -5004,
    ErrorWrongType = -5005,
    ErrorInsufficientRights = -5006,
    ErrorNotInWait = -5007,
    NoMoreData = 5001,
    // --------- File Operations ---------
    ErrorFileOpInvalidDomain = -6001,
    ErrorFileOpSourcePathNotAbsolute = -6002,
    ErrorFileOpSourceFileNotExists = -6003,
    ErrorFileOpDestinationPathNotAbsolute = -6004,
    ErrorFileOpDestinationAlreadyExists = -6005,
    ErrorFileOpDestinationDirectoryNotExists = -6006,
    ErrorFileOpDirectoryNotExists = -6007,
    ErrorFileOpDirectoryNotEmpty = -6008,
    ErrorFileSelectNotPossible = -6009,
    ErrorChannelNotExists = -6010,
    ErrorFileUsed = -6011,
    // --------- License handling ---------
    ErrorLicenseNoLicense = -7001,
    ErrorLicenseUnknownApplication = -7002,
    ErrorLicenseUnknownFrontend = -7003,
    ErrorLicenseNoConnection = -7004,
    ErrorLicenseNoLicenseHandler = -7005,
    ErrorLicenseErrorAtServerSite = -7006,
    ErrorLicenseOptionalLicenseKeyNotAvailable = -7007,
    ErrorLicenseOptionalLicenseKeyNotGranted = -7008,
    ErrorLicenseOptionalLicenseNotAqcuired = -7009,
    ErrorLicenseOptionalLicenseCheckFailed = -7010,
    ErrorLicenseKeyNotAvailable = -7011,
    ErrorLicenseKeyGrantFailed = -7012,
    ErrorLicenseKeyReleaseFailed = -7013,
    ErrorLicenseInvalid = -7014,
    ErrorLicenseAlreadyInUse = -7015,
    ErrorLicenseCheckFailed = -7016,
    ErrorLicenseAllLicensesInUse = -7017,
    ErrorLicenseUnknownLicense = -7018,
}

impl OpenReturnCodes {
    /// Convenience helper that mirrors the old `Ok` check in the C# wrapper.
    pub fn is_ok(self) -> bool {
        matches!(self, OpenReturnCodes::Ok)
    }
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpenDictionaryElementType {
    Bool = 1,
    Integer8Bit = 2,
    UInteger8Bit = 3,
    Integer16Bit = 4,
    UInteger16Bit = 5,
    Integer32Bit = 6,
    UInteger32Bit = 7,
    Integer64Bit = 8,
    UInteger64Bit = 9,
    Double = 10,
    String = 11,
    IntegerArray32Bit = 12,
    UIntegerArray64Bit = 13,
    BoolArray = 14,
    DoubleArray = 15,
    StringArray = 16,
    UIntegerArray32Bit = 17,
    UIntegerArray16Bit = 18,
    UIntegerArray8Bit = 19,
}

#[repr(u64)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpenEventMaskDefinition {
    Unknown = 0x0000000000000000,
    NckFirstIpo = 0x0000000000000008,
    NckBootReady = 0x0000000000000004,
    NckIpo = 0x0000000000000001,
    NckChannelNewMotion = 0x0000000000000100,
    NckChannelNewBlock = 0x0000000000000200,
    NckChannelConfigChanged = 0x0000000000000400,
    NckChannelStartPositioningMotion = 0x0000000000000800,
    NckChannelEndPositioningMotion = 0x0000000000001000,
    NckChannelLengthSlicePath = 0x0000000000001001,
    NckChannelLengthSlicePositioning = 0x0000000000001002,
    NckChannelAngleSlicePositioning = 0x0000000000001003,
    NckChannelAngleSliceOrientation = 0x0000000000001004,
    NckChannelStateChanged = 0x0000000000001005,
    NckChannelProgramFinished = 0x0000000000001006,
    PlcOb1 = 0x0000000000010000,
    PlcHwConfigChanged = 0x0000000000020000,
    PlcAcyclicCommunication = 0x0000000000040000,
    ContentChanged = 0x0001000000000000,
    ResetStarted = 0x0002000000000000,
    ResetFinished = 0x0004000000000000,
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpenAxisMode {
    None = 0,
    Interpolation = 1,
    Positioning = 2,
    SpeedLeft = 3,
    SpeedRight = 4,
    SpeedStop = 5,
    Following = 6,
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpenAxisReferenceState {
    None = 0,
    NotReferenced = 1,
    Referenced = 2,
    ReferenceNotRequired = 3,
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpenMotionType {
    None = 0,
    Rapid = 1,
    Linear = 2,
    Circle = 3,
    Spline = 4,
    Thread = 5,
    Involute = 6,
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpenCircleDirection {
    None = 0,
    Cw = 1,
    Ccw = 2,
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpenOperatingMode {
    None = 0,
    Auto = 1,
    Jog = 2,
    Mda = 3,
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpenChannelState {
    None = 0,
    Activated = 1,
    Stopped = 2,
    Reseted = 3,
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpenAxisType {
    None = 0,
    Linear = 1,
    Rotatory = 2,
    Modulo = 3,
    Spindle = 4,
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpenProjectStates {
    Closed = 1,
    Opening = 2,
    Open = 3,
    Booting = 4,
    RunningOrPaused = 5,
    ShuttingDown = 6,
    Closing = 7,
    Resetting = 8,
    Saving = 9,
    Error = 10,
}

// Helper type aliases for function signatures
type StartApplicationFn = unsafe extern "C" fn(PCWSTR) -> OpenReturnCodes;
type StopApplicationFn = unsafe extern "C" fn() -> OpenReturnCodes;
type ConnectFn = unsafe extern "C" fn() -> OpenReturnCodes;
type DisconnectFn = unsafe extern "C" fn() -> OpenReturnCodes;
type OpenProjectFn = unsafe extern "C" fn(PCWSTR) -> OpenReturnCodes;
type CreateProjectFn = unsafe extern "C" fn(PCWSTR, PCWSTR) -> OpenReturnCodes;
type CreateProjectWithScalingFn = unsafe extern "C" fn(PCWSTR, PCWSTR, PCWSTR) -> OpenReturnCodes;
type CreateProjectWithScalingNcuNumberFn = unsafe extern "C" fn(
    PCWSTR,
    PCWSTR,
    PCWSTR,
    u32
) -> OpenReturnCodes;
type SaveProjectFn = unsafe extern "C" fn() -> OpenReturnCodes;
type CloseProjectFn = unsafe extern "C" fn() -> OpenReturnCodes;
type BootControllerFn = unsafe extern "C" fn() -> OpenReturnCodes;
type ShutdownControllerFn = unsafe extern "C" fn() -> OpenReturnCodes;
type ResetControllerFn = unsafe extern "C" fn() -> OpenReturnCodes;
type RegisterForEventsFn = unsafe extern "C" fn(
    u32,
    *const OpenEventMaskDefinition
) -> OpenReturnCodes;
type UnregisterEventsFn = unsafe extern "C" fn() -> OpenReturnCodes;
type WaitForEventsFn = unsafe extern "C" fn() -> OpenReturnCodes;
type ContinueSimulationFn = unsafe extern "C" fn() -> OpenReturnCodes;
type GetDictionaryIdFn = unsafe extern "C" fn(PCWSTR, *mut u32) -> OpenReturnCodes;
type RegisterWatchFn = unsafe extern "C" fn(u32, *const u32) -> OpenReturnCodes;
type UnregisterWatchFn = unsafe extern "C" fn() -> OpenReturnCodes;
type GetDictionaryElementTypeFn = unsafe extern "C" fn(u32, *mut u8) -> OpenReturnCodes;
type ReadUnsignedInteger64BitArrayFn = unsafe extern "C" fn(u32, u32, *mut u64) -> OpenReturnCodes;
type ReadSignedInteger32BitArrayFn = unsafe extern "C" fn(u32, u32, *mut i32) -> OpenReturnCodes;
type ReadUnsignedInteger32BitArrayFn = unsafe extern "C" fn(u32, u32, *mut u32) -> OpenReturnCodes;
type ReadUnsignedInteger16BitArrayFn = unsafe extern "C" fn(u32, u32, *mut u16) -> OpenReturnCodes;
type ReadUnsignedInteger8BitArrayFn = unsafe extern "C" fn(u32, u32, *mut u8) -> OpenReturnCodes;
type ReadBoolArrayFn = unsafe extern "C" fn(u32, u32, *mut bool) -> OpenReturnCodes;
type ReadDoubleArrayFn = unsafe extern "C" fn(u32, u32, *mut f64) -> OpenReturnCodes;
type ReadStringFn = unsafe extern "C" fn(u32, u32, PWSTR) -> OpenReturnCodes;
type ReadStringArrayFn = unsafe extern "C" fn(u32, u32, u32, PPWSTR) -> OpenReturnCodes;
type ReadUnsignedInteger16BitFn = unsafe extern "C" fn(u32, *mut u16) -> OpenReturnCodes;
type ReadSignedInteger16BitFn = unsafe extern "C" fn(u32, *mut i16) -> OpenReturnCodes;
type ReadUnsignedInteger32BitFn = unsafe extern "C" fn(u32, *mut u32) -> OpenReturnCodes;
type ReadSignedInteger32BitFn = unsafe extern "C" fn(u32, *mut i32) -> OpenReturnCodes;
type ReadUnsignedInteger64BitFn = unsafe extern "C" fn(u32, *mut u64) -> OpenReturnCodes;
type ReadSignedInteger64BitFn = unsafe extern "C" fn(u32, *mut i64) -> OpenReturnCodes;
type GetNumArrayElementsFn = unsafe extern "C" fn(u32, *mut u32) -> OpenReturnCodes;
type ReadBitFn = unsafe extern "C" fn(u32, *mut Bool32) -> OpenReturnCodes;
type ReadBoolFn = unsafe extern "C" fn(u32, *mut Bool32) -> OpenReturnCodes;
type ReadByteFn = unsafe extern "C" fn(u32, *mut u8) -> OpenReturnCodes;
type ReadDoubleFn = unsafe extern "C" fn(u32, *mut f64) -> OpenReturnCodes;
type ReadWordFn = unsafe extern "C" fn(u32, *mut u16) -> OpenReturnCodes;
type ReadDoubleWordFn = unsafe extern "C" fn(u32, *mut u32) -> OpenReturnCodes;
type ReadLongWordFn = unsafe extern "C" fn(u32, *mut u64) -> OpenReturnCodes;
type ReadNextEventFn = unsafe extern "C" fn(*mut u64) -> OpenReturnCodes;
type ReadNextEventWithNcuIndexFn = unsafe extern "C" fn(*mut u64, *mut i32) -> OpenReturnCodes;
type WriteBitFn = unsafe extern "C" fn(u32, Bool32) -> OpenReturnCodes;
type WriteBoolFn = unsafe extern "C" fn(u32, Bool32) -> OpenReturnCodes;
type WriteByteFn = unsafe extern "C" fn(u32, u8) -> OpenReturnCodes;
type WriteDoubleFn = unsafe extern "C" fn(u32, f64) -> OpenReturnCodes;
type WriteWordFn = unsafe extern "C" fn(u32, u16) -> OpenReturnCodes;
type WriteDoubleWordFn = unsafe extern "C" fn(u32, u32) -> OpenReturnCodes;
type WriteLongWordFn = unsafe extern "C" fn(u32, u64) -> OpenReturnCodes;
type WriteBoolArrayFn = unsafe extern "C" fn(u32, u32, *const bool) -> OpenReturnCodes;
type GetSimulationSpeedFn = unsafe extern "C" fn(*mut u32) -> OpenReturnCodes;
type SetSimulationSpeedFn = unsafe extern "C" fn(u32) -> OpenReturnCodes;
type GetCardPathFn = unsafe extern "C" fn(u32, PWSTR) -> OpenReturnCodes;
type GetCardPathsFn = unsafe extern "C" fn(u32, u32, PPWSTR) -> OpenReturnCodes;
type GetFileFn = unsafe extern "C" fn(PCWSTR, PCWSTR) -> OpenReturnCodes;
type PutFileFn = unsafe extern "C" fn(PCWSTR, PCWSTR) -> OpenReturnCodes;
type SelectFileFn = unsafe extern "C" fn(PCWSTR, PCWSTR) -> OpenReturnCodes;
type DeleteFileFn = unsafe extern "C" fn(PCWSTR) -> OpenReturnCodes;
type DeleteDirectoryFn = unsafe extern "C" fn(PCWSTR) -> OpenReturnCodes;
type CreateDirectoryFn = unsafe extern "C" fn(PCWSTR) -> OpenReturnCodes;
type ListDirectoryFn = unsafe extern "C" fn(PCWSTR, PCWSTR) -> OpenReturnCodes;
type GetVersionInformationFn = unsafe extern "C" fn(PCWSTR) -> OpenReturnCodes;
type GetProjectInformationFn = unsafe extern "C" fn(PCWSTR, PCWSTR) -> OpenReturnCodes;
type GetCurrentProjectInformationFn = unsafe extern "C" fn(PCWSTR) -> OpenReturnCodes;
type GetCurrentProjectStateFn = unsafe extern "C" fn(*mut OpenProjectStates) -> OpenReturnCodes;
type ImportProjectSettingsFn = unsafe extern "C" fn(PCWSTR, PCWSTR) -> OpenReturnCodes;
type ExportProjectSettingsFn = unsafe extern "C" fn(PCWSTR) -> OpenReturnCodes;
type WriteUnsignedInteger8BitArrayFn = unsafe extern "C" fn(u32, u32, *const u8) -> OpenReturnCodes;
type GetNumberOfNcusFn = unsafe extern "C" fn(*mut u32) -> OpenReturnCodes;

fn load_symbol<T: Copy>(lib: &'static Library, name: &[u8]) -> Result<T, libloading::Error> {
    unsafe {
        let symbol: Symbol<T> = lib.get(name)?;
        Ok(*symbol)
    }
}

pub struct OpenApi {
    _lib: &'static Library, // keep leaked library alive so function pointers remain valid
    start_application: StartApplicationFn,
    stop_application: StopApplicationFn,
    connect: ConnectFn,
    disconnect: DisconnectFn,
    open_project: OpenProjectFn,
    create_project: CreateProjectFn,
    create_project_with_scaling: CreateProjectWithScalingFn,
    create_project_with_scaling_ncu_number: CreateProjectWithScalingNcuNumberFn,
    save_project: SaveProjectFn,
    close_project: CloseProjectFn,
    boot_controller: BootControllerFn,
    shutdown_controller: ShutdownControllerFn,
    reset_controller: ResetControllerFn,
    register_for_events: RegisterForEventsFn,
    unregister_events: UnregisterEventsFn,
    wait_for_events: WaitForEventsFn,
    continue_simulation: ContinueSimulationFn,
    get_dictionary_id: GetDictionaryIdFn,
    register_watch: RegisterWatchFn,
    unregister_watch: UnregisterWatchFn,
    get_dictionary_element_type: GetDictionaryElementTypeFn,
    read_unsigned_integer_64_bit_array: ReadUnsignedInteger64BitArrayFn,
    read_signed_integer_32_bit_array: ReadSignedInteger32BitArrayFn,
    read_unsigned_integer_32_bit_array: ReadUnsignedInteger32BitArrayFn,
    read_unsigned_integer_16_bit_array: ReadUnsignedInteger16BitArrayFn,
    read_unsigned_integer_8_bit_array: ReadUnsignedInteger8BitArrayFn,
    read_bool_array: ReadBoolArrayFn,
    read_double_array: ReadDoubleArrayFn,
    read_string: ReadStringFn,
    read_string_array: ReadStringArrayFn,
    read_unsigned_integer_16_bit: ReadUnsignedInteger16BitFn,
    read_signed_integer_16_bit: ReadSignedInteger16BitFn,
    read_unsigned_integer_32_bit: ReadUnsignedInteger32BitFn,
    read_signed_integer_32_bit: ReadSignedInteger32BitFn,
    read_unsigned_integer_64_bit: ReadUnsignedInteger64BitFn,
    read_signed_integer_64_bit: ReadSignedInteger64BitFn,
    get_num_array_elements: GetNumArrayElementsFn,
    read_bit: ReadBitFn,
    read_bool: ReadBoolFn,
    read_byte: ReadByteFn,
    read_double: ReadDoubleFn,
    read_word: ReadWordFn,
    read_double_word: ReadDoubleWordFn,
    read_long_word: ReadLongWordFn,
    read_next_event: ReadNextEventFn,
    read_next_event_with_ncu_index: ReadNextEventWithNcuIndexFn,
    write_bit: WriteBitFn,
    write_bool: WriteBoolFn,
    write_byte: WriteByteFn,
    write_double: WriteDoubleFn,
    write_word: WriteWordFn,
    write_double_word: WriteDoubleWordFn,
    write_long_word: WriteLongWordFn,
    write_bool_array: WriteBoolArrayFn,
    get_simulation_speed: GetSimulationSpeedFn,
    set_simulation_speed: SetSimulationSpeedFn,
    get_card_path: GetCardPathFn,
    get_card_paths: GetCardPathsFn,
    get_file: GetFileFn,
    put_file: PutFileFn,
    select_file: SelectFileFn,
    delete_file: DeleteFileFn,
    delete_directory: DeleteDirectoryFn,
    create_directory: CreateDirectoryFn,
    list_directory: ListDirectoryFn,
    get_version_information: GetVersionInformationFn,
    get_project_information: GetProjectInformationFn,
    get_current_project_information: GetCurrentProjectInformationFn,
    get_current_project_state: GetCurrentProjectStateFn,
    import_project_settings: ImportProjectSettingsFn,
    export_project_settings: ExportProjectSettingsFn,
    write_unsigned_integer_8_bit_array: WriteUnsignedInteger8BitArrayFn,
    get_number_of_ncus: GetNumberOfNcusFn,
}

impl OpenApi {
    /// Load DLL from a path, binding required symbols.
    ///
    /// If `path` resolves to an empty string (e.g. `""`), the default `bin/open.dll`
    /// relative to the current working directory is used.
    pub unsafe fn load<P: AsRef<Path>>(path: P) -> Result<Self, libloading::Error> {
        // Load the DLL
        let provided_path = path.as_ref();
        let fallback_path;
        let dll_path = if provided_path.as_os_str().is_empty() {
            fallback_path = PathBuf::from("bin").join("open.dll");
            fallback_path.as_path()
        } else {
            provided_path
        };
        let lib = Library::new(dll_path)?;
        // Extend the library's lifetime to 'static by leaking it.
        // (Alternatively: design OpenApi with lifetimes and avoid leaking.)
        let lib: &'static Library = Box::leak(Box::new(lib));

        let start_application = load_symbol::<StartApplicationFn>(lib, b"startApplication")?;
        let stop_application = load_symbol::<StopApplicationFn>(lib, b"stopApplication")?;
        let connect = load_symbol::<ConnectFn>(lib, b"connect")?;
        let disconnect = load_symbol::<DisconnectFn>(lib, b"disconnect")?;
        let open_project = load_symbol::<OpenProjectFn>(lib, b"openProject")?;
        let create_project = load_symbol::<CreateProjectFn>(lib, b"createProject")?;
        let create_project_with_scaling = load_symbol::<CreateProjectWithScalingFn>(
            lib,
            b"createProjectWithScaling"
        )?;
        let create_project_with_scaling_ncu_number =
            load_symbol::<CreateProjectWithScalingNcuNumberFn>(
                lib,
                b"createProjectWithScalingNCUNumber"
            )?;
        let save_project = load_symbol::<SaveProjectFn>(lib, b"saveProject")?;
        let close_project = load_symbol::<CloseProjectFn>(lib, b"closeProject")?;
        let boot_controller = load_symbol::<BootControllerFn>(lib, b"bootController")?;
        let shutdown_controller = load_symbol::<ShutdownControllerFn>(lib, b"shutdownController")?;
        let reset_controller = load_symbol::<ResetControllerFn>(lib, b"resetController")?;
        let register_for_events = load_symbol::<RegisterForEventsFn>(lib, b"registerForEvents")?;
        let unregister_events = load_symbol::<UnregisterEventsFn>(lib, b"unregisterEvents")?;
        let wait_for_events = load_symbol::<WaitForEventsFn>(lib, b"waitForEvents")?;
        let continue_simulation = load_symbol::<ContinueSimulationFn>(lib, b"continueSimulation")?;
        let get_dictionary_id = load_symbol::<GetDictionaryIdFn>(lib, b"getDictionaryId")?;
        let register_watch = load_symbol::<RegisterWatchFn>(lib, b"registerWatch")?;
        let unregister_watch = load_symbol::<UnregisterWatchFn>(lib, b"unregisterWatch")?;
        let get_dictionary_element_type = load_symbol::<GetDictionaryElementTypeFn>(
            lib,
            b"getDictionaryElementType"
        )?;
        let read_unsigned_integer_64_bit_array = load_symbol::<ReadUnsignedInteger64BitArrayFn>(
            lib,
            b"readUnsignedInteger64BitArray"
        )?;
        let read_signed_integer_32_bit_array = load_symbol::<ReadSignedInteger32BitArrayFn>(
            lib,
            b"readSignedInteger32BitArray"
        )?;
        let read_unsigned_integer_32_bit_array = load_symbol::<ReadUnsignedInteger32BitArrayFn>(
            lib,
            b"readUnsignedInteger32BitArray"
        )?;
        let read_unsigned_integer_16_bit_array = load_symbol::<ReadUnsignedInteger16BitArrayFn>(
            lib,
            b"readUnsignedInteger16BitArray"
        )?;
        let read_unsigned_integer_8_bit_array = load_symbol::<ReadUnsignedInteger8BitArrayFn>(
            lib,
            b"readUnsignedInteger8BitArray"
        )?;
        let read_bool_array = load_symbol::<ReadBoolArrayFn>(lib, b"readBoolArray")?;
        let read_double_array = load_symbol::<ReadDoubleArrayFn>(lib, b"readDoubleArray")?;
        let read_string = load_symbol::<ReadStringFn>(lib, b"readString")?;
        let read_string_array = load_symbol::<ReadStringArrayFn>(lib, b"readStringArray")?;
        let read_unsigned_integer_16_bit = load_symbol::<ReadUnsignedInteger16BitFn>(
            lib,
            b"readUnsignedInteger16Bit"
        )?;
        let read_signed_integer_16_bit = load_symbol::<ReadSignedInteger16BitFn>(
            lib,
            b"readSignedInteger16Bit"
        )?;
        let read_unsigned_integer_32_bit = load_symbol::<ReadUnsignedInteger32BitFn>(
            lib,
            b"readUnsignedInteger32Bit"
        )?;
        let read_signed_integer_32_bit = load_symbol::<ReadSignedInteger32BitFn>(
            lib,
            b"readSignedInteger32Bit"
        )?;
        let read_unsigned_integer_64_bit = load_symbol::<ReadUnsignedInteger64BitFn>(
            lib,
            b"readUnsignedInteger64Bit"
        )?;
        let read_signed_integer_64_bit = load_symbol::<ReadSignedInteger64BitFn>(
            lib,
            b"readSignedInteger64Bit"
        )?;
        let get_num_array_elements = load_symbol::<GetNumArrayElementsFn>(
            lib,
            b"getNumArrayElements"
        )?;
        let read_bit = load_symbol::<ReadBitFn>(lib, b"readBit")?;
        let read_bool = load_symbol::<ReadBoolFn>(lib, b"readBool")?;
        let read_byte = load_symbol::<ReadByteFn>(lib, b"readByte")?;
        let read_double = load_symbol::<ReadDoubleFn>(lib, b"readDouble")?;
        let read_word = load_symbol::<ReadWordFn>(lib, b"readWord")?;
        let read_double_word = load_symbol::<ReadDoubleWordFn>(lib, b"readDoubleWord")?;
        let read_long_word = load_symbol::<ReadLongWordFn>(lib, b"readLongWord")?;
        let read_next_event = load_symbol::<ReadNextEventFn>(lib, b"readNextEvent")?;
        let read_next_event_with_ncu_index = load_symbol::<ReadNextEventWithNcuIndexFn>(
            lib,
            b"readNextEventWithNcuIndex"
        )?;
        let write_bit = load_symbol::<WriteBitFn>(lib, b"writeBit")?;
        let write_bool = load_symbol::<WriteBoolFn>(lib, b"writeBool")?;
        let write_byte = load_symbol::<WriteByteFn>(lib, b"writeByte")?;
        let write_double = load_symbol::<WriteDoubleFn>(lib, b"writeDouble")?;
        let write_word = load_symbol::<WriteWordFn>(lib, b"writeWord")?;
        let write_double_word = load_symbol::<WriteDoubleWordFn>(lib, b"writeDoubleWord")?;
        let write_long_word = load_symbol::<WriteLongWordFn>(lib, b"writeLongWord")?;
        let write_bool_array = load_symbol::<WriteBoolArrayFn>(lib, b"writeBoolArray")?;
        let get_simulation_speed = load_symbol::<GetSimulationSpeedFn>(lib, b"getSimulationSpeed")?;
        let set_simulation_speed = load_symbol::<SetSimulationSpeedFn>(lib, b"setSimulationSpeed")?;
        let get_card_path = load_symbol::<GetCardPathFn>(lib, b"getCardPath")?;
        let get_card_paths = load_symbol::<GetCardPathsFn>(lib, b"getCardPaths")?;
        let get_file = load_symbol::<GetFileFn>(lib, b"getFile")?;
        let put_file = load_symbol::<PutFileFn>(lib, b"putFile")?;
        let select_file = load_symbol::<SelectFileFn>(lib, b"selectFile")?;
        let delete_file = load_symbol::<DeleteFileFn>(lib, b"deleteFile")?;
        let delete_directory = load_symbol::<DeleteDirectoryFn>(lib, b"deleteDirectory")?;
        let create_directory = load_symbol::<CreateDirectoryFn>(lib, b"createDirectory")?;
        let list_directory = load_symbol::<ListDirectoryFn>(lib, b"listDirectory")?;
        let get_version_information = load_symbol::<GetVersionInformationFn>(
            lib,
            b"getVersionInformation"
        )?;
        let get_project_information = load_symbol::<GetProjectInformationFn>(
            lib,
            b"getProjectInformation"
        )?;
        let get_current_project_information = load_symbol::<GetCurrentProjectInformationFn>(
            lib,
            b"getCurrentProjectInformation"
        )?;
        let get_current_project_state = load_symbol::<GetCurrentProjectStateFn>(
            lib,
            b"getCurrentProjectState"
        )?;
        let import_project_settings = load_symbol::<ImportProjectSettingsFn>(
            lib,
            b"importProjectSettings"
        )?;
        let export_project_settings = load_symbol::<ExportProjectSettingsFn>(
            lib,
            b"exportProjectSettings"
        )?;
        let write_unsigned_integer_8_bit_array = load_symbol::<WriteUnsignedInteger8BitArrayFn>(
            lib,
            b"writeUnsignedInteger8BitArray"
        )?;
        let get_number_of_ncus = load_symbol::<GetNumberOfNcusFn>(lib, b"getNumberOfNCUs")?;

        Ok(Self {
            _lib: lib,
            start_application,
            stop_application,
            connect,
            disconnect,
            open_project,
            create_project,
            create_project_with_scaling,
            create_project_with_scaling_ncu_number,
            save_project,
            close_project,
            boot_controller,
            shutdown_controller,
            reset_controller,
            register_for_events,
            unregister_events,
            wait_for_events,
            continue_simulation,
            get_dictionary_id,
            register_watch,
            unregister_watch,
            get_dictionary_element_type,
            read_unsigned_integer_64_bit_array,
            read_signed_integer_32_bit_array,
            read_unsigned_integer_32_bit_array,
            read_unsigned_integer_16_bit_array,
            read_unsigned_integer_8_bit_array,
            read_bool_array,
            read_double_array,
            read_string,
            read_string_array,
            read_unsigned_integer_16_bit,
            read_signed_integer_16_bit,
            read_unsigned_integer_32_bit,
            read_signed_integer_32_bit,
            read_unsigned_integer_64_bit,
            read_signed_integer_64_bit,
            get_num_array_elements,
            read_bit,
            read_bool,
            read_byte,
            read_double,
            read_word,
            read_double_word,
            read_long_word,
            read_next_event,
            read_next_event_with_ncu_index,
            write_bit,
            write_bool,
            write_byte,
            write_double,
            write_word,
            write_double_word,
            write_long_word,
            write_bool_array,
            get_simulation_speed,
            set_simulation_speed,
            get_card_path,
            get_card_paths,
            get_file,
            put_file,
            select_file,
            delete_file,
            delete_directory,
            create_directory,
            list_directory,
            get_version_information,
            get_project_information,
            get_current_project_information,
            get_current_project_state,
            import_project_settings,
            export_project_settings,
            write_unsigned_integer_8_bit_array,
            get_number_of_ncus,
        })
    }
    #[inline]
    pub unsafe fn start_application(&self, str_profile_file_name: PCWSTR) -> OpenReturnCodes {
        (self.start_application)(str_profile_file_name)
    }

    #[inline]
    pub unsafe fn stop_application(&self) -> OpenReturnCodes {
        (self.stop_application)()
    }

    #[inline]
    pub unsafe fn connect(&self) -> OpenReturnCodes {
        (self.connect)()
    }

    #[inline]
    pub unsafe fn disconnect(&self) -> OpenReturnCodes {
        (self.disconnect)()
    }

    #[inline]
    pub unsafe fn open_project(&self, str_file_name: PCWSTR) -> OpenReturnCodes {
        (self.open_project)(str_file_name)
    }

    #[inline]
    pub unsafe fn create_project(
        &self,
        str_file_name: PCWSTR,
        str_version: PCWSTR
    ) -> OpenReturnCodes {
        (self.create_project)(str_file_name, str_version)
    }

    #[inline]
    pub unsafe fn create_project_with_scaling(
        &self,
        str_file_name: PCWSTR,
        str_version: PCWSTR,
        str_nck_scaling: PCWSTR
    ) -> OpenReturnCodes {
        (self.create_project_with_scaling)(str_file_name, str_version, str_nck_scaling)
    }

    #[inline]
    pub unsafe fn create_project_with_scaling_ncu_number(
        &self,
        str_file_name: PCWSTR,
        str_version: PCWSTR,
        str_nck_scaling: PCWSTR,
        ncu_number: u32
    ) -> OpenReturnCodes {
        (self.create_project_with_scaling_ncu_number)(
            str_file_name,
            str_version,
            str_nck_scaling,
            ncu_number
        )
    }

    #[inline]
    pub unsafe fn save_project(&self) -> OpenReturnCodes {
        (self.save_project)()
    }

    #[inline]
    pub unsafe fn close_project(&self) -> OpenReturnCodes {
        (self.close_project)()
    }

    #[inline]
    pub unsafe fn boot_controller(&self) -> OpenReturnCodes {
        (self.boot_controller)()
    }

    #[inline]
    pub unsafe fn shutdown_controller(&self) -> OpenReturnCodes {
        (self.shutdown_controller)()
    }

    #[inline]
    pub unsafe fn reset_controller(&self) -> OpenReturnCodes {
        (self.reset_controller)()
    }

    #[inline]
    pub unsafe fn register_for_events(
        &self,
        event_list_length: u32,
        event_list: *const OpenEventMaskDefinition
    ) -> OpenReturnCodes {
        (self.register_for_events)(event_list_length, event_list)
    }

    #[inline]
    pub unsafe fn unregister_events(&self) -> OpenReturnCodes {
        (self.unregister_events)()
    }

    #[inline]
    pub unsafe fn wait_for_events(&self) -> OpenReturnCodes {
        (self.wait_for_events)()
    }

    #[inline]
    pub unsafe fn continue_simulation(&self) -> OpenReturnCodes {
        (self.continue_simulation)()
    }

    #[inline]
    pub unsafe fn get_dictionary_id(
        &self,
        dictionary_element: PCWSTR,
        dictionary_id: *mut u32
    ) -> OpenReturnCodes {
        (self.get_dictionary_id)(dictionary_element, dictionary_id)
    }

    #[inline]
    pub unsafe fn register_watch(
        &self,
        watch_list_length: u32,
        dictionary_id_list: *const u32
    ) -> OpenReturnCodes {
        (self.register_watch)(watch_list_length, dictionary_id_list)
    }

    #[inline]
    pub unsafe fn unregister_watch(&self) -> OpenReturnCodes {
        (self.unregister_watch)()
    }

    #[inline]
    pub unsafe fn get_dictionary_element_type(
        &self,
        dictionary_id: u32,
        value: *mut u8
    ) -> OpenReturnCodes {
        (self.get_dictionary_element_type)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_unsigned_integer_64_bit_array(
        &self,
        dictionary_id: u32,
        value_list_max_length: u32,
        value_list: *mut u64
    ) -> OpenReturnCodes {
        (self.read_unsigned_integer_64_bit_array)(dictionary_id, value_list_max_length, value_list)
    }

    #[inline]
    pub unsafe fn read_signed_integer_32_bit_array(
        &self,
        dictionary_id: u32,
        value_list_max_length: u32,
        value_list: *mut i32
    ) -> OpenReturnCodes {
        (self.read_signed_integer_32_bit_array)(dictionary_id, value_list_max_length, value_list)
    }

    #[inline]
    pub unsafe fn read_unsigned_integer_32_bit_array(
        &self,
        dictionary_id: u32,
        value_list_max_length: u32,
        value_list: *mut u32
    ) -> OpenReturnCodes {
        (self.read_unsigned_integer_32_bit_array)(dictionary_id, value_list_max_length, value_list)
    }

    #[inline]
    pub unsafe fn read_unsigned_integer_16_bit_array(
        &self,
        dictionary_id: u32,
        value_list_max_length: u32,
        value_list: *mut u16
    ) -> OpenReturnCodes {
        (self.read_unsigned_integer_16_bit_array)(dictionary_id, value_list_max_length, value_list)
    }

    #[inline]
    pub unsafe fn read_unsigned_integer_8_bit_array(
        &self,
        dictionary_id: u32,
        value_list_max_length: u32,
        value_list: *mut u8
    ) -> OpenReturnCodes {
        (self.read_unsigned_integer_8_bit_array)(dictionary_id, value_list_max_length, value_list)
    }

    #[inline]
    pub unsafe fn read_bool_array(
        &self,
        dictionary_id: u32,
        value_list_max_length: u32,
        value_list: *mut bool
    ) -> OpenReturnCodes {
        (self.read_bool_array)(dictionary_id, value_list_max_length, value_list)
    }

    #[inline]
    pub unsafe fn read_double_array(
        &self,
        dictionary_id: u32,
        value_list_max_length: u32,
        value_list: *mut f64
    ) -> OpenReturnCodes {
        (self.read_double_array)(dictionary_id, value_list_max_length, value_list)
    }

    #[inline]
    pub unsafe fn read_string(
        &self,
        dictionary_id: u32,
        string_max_length: u32,
        string_buffer: PWSTR
    ) -> OpenReturnCodes {
        (self.read_string)(dictionary_id, string_max_length, string_buffer)
    }

    #[inline]
    pub unsafe fn read_string_array(
        &self,
        dictionary_id: u32,
        string_array_max_length: u32,
        string_max_length: u32,
        string_array: PPWSTR
    ) -> OpenReturnCodes {
        (self.read_string_array)(
            dictionary_id,
            string_array_max_length,
            string_max_length,
            string_array
        )
    }

    #[inline]
    pub unsafe fn read_unsigned_integer_16_bit(
        &self,
        dictionary_id: u32,
        value: *mut u16
    ) -> OpenReturnCodes {
        (self.read_unsigned_integer_16_bit)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_signed_integer_16_bit(
        &self,
        dictionary_id: u32,
        value: *mut i16
    ) -> OpenReturnCodes {
        (self.read_signed_integer_16_bit)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_unsigned_integer_32_bit(
        &self,
        dictionary_id: u32,
        value: *mut u32
    ) -> OpenReturnCodes {
        (self.read_unsigned_integer_32_bit)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_signed_integer_32_bit(
        &self,
        dictionary_id: u32,
        value: *mut i32
    ) -> OpenReturnCodes {
        (self.read_signed_integer_32_bit)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_unsigned_integer_64_bit(
        &self,
        dictionary_id: u32,
        value: *mut u64
    ) -> OpenReturnCodes {
        (self.read_unsigned_integer_64_bit)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_signed_integer_64_bit(
        &self,
        dictionary_id: u32,
        value: *mut i64
    ) -> OpenReturnCodes {
        (self.read_signed_integer_64_bit)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn get_num_array_elements(
        &self,
        dictionary_id: u32,
        num_array_elements: *mut u32
    ) -> OpenReturnCodes {
        (self.get_num_array_elements)(dictionary_id, num_array_elements)
    }

    #[inline]
    pub unsafe fn read_bit(&self, dictionary_id: u32, value: *mut Bool32) -> OpenReturnCodes {
        (self.read_bit)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_bool(&self, dictionary_id: u32, value: *mut Bool32) -> OpenReturnCodes {
        (self.read_bool)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_byte(&self, dictionary_id: u32, value: *mut u8) -> OpenReturnCodes {
        (self.read_byte)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_double(&self, dictionary_id: u32, value: *mut f64) -> OpenReturnCodes {
        (self.read_double)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_word(&self, dictionary_id: u32, value: *mut u16) -> OpenReturnCodes {
        (self.read_word)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_double_word(&self, dictionary_id: u32, value: *mut u32) -> OpenReturnCodes {
        (self.read_double_word)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_long_word(&self, dictionary_id: u32, value: *mut u64) -> OpenReturnCodes {
        (self.read_long_word)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn read_next_event(&self, event: *mut u64) -> OpenReturnCodes {
        (self.read_next_event)(event)
    }

    #[inline]
    pub unsafe fn read_next_event_with_ncu_index(
        &self,
        event: *mut u64,
        ncu_index: *mut i32
    ) -> OpenReturnCodes {
        (self.read_next_event_with_ncu_index)(event, ncu_index)
    }

    #[inline]
    pub unsafe fn write_bit(&self, dictionary_id: u32, value: Bool32) -> OpenReturnCodes {
        (self.write_bit)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn write_bool(&self, dictionary_id: u32, value: Bool32) -> OpenReturnCodes {
        (self.write_bool)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn write_byte(&self, dictionary_id: u32, value: u8) -> OpenReturnCodes {
        (self.write_byte)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn write_double(&self, dictionary_id: u32, value: f64) -> OpenReturnCodes {
        (self.write_double)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn write_word(&self, dictionary_id: u32, value: u16) -> OpenReturnCodes {
        (self.write_word)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn write_double_word(&self, dictionary_id: u32, value: u32) -> OpenReturnCodes {
        (self.write_double_word)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn write_long_word(&self, dictionary_id: u32, value: u64) -> OpenReturnCodes {
        (self.write_long_word)(dictionary_id, value)
    }

    #[inline]
    pub unsafe fn write_bool_array(
        &self,
        dictionary_id: u32,
        value_list_length: u32,
        value_list: *const bool
    ) -> OpenReturnCodes {
        (self.write_bool_array)(dictionary_id, value_list_length, value_list)
    }

    #[inline]
    pub unsafe fn get_simulation_speed(&self, percent: *mut u32) -> OpenReturnCodes {
        (self.get_simulation_speed)(percent)
    }

    #[inline]
    pub unsafe fn set_simulation_speed(&self, percent: u32) -> OpenReturnCodes {
        (self.set_simulation_speed)(percent)
    }

    #[inline]
    pub unsafe fn get_card_path(
        &self,
        card_path_max_length: u32,
        card_path: PWSTR
    ) -> OpenReturnCodes {
        (self.get_card_path)(card_path_max_length, card_path)
    }

    #[inline]
    pub unsafe fn get_card_paths(
        &self,
        card_paths_array_max_length: u32,
        card_path_max_length: u32,
        card_paths: PPWSTR
    ) -> OpenReturnCodes {
        (self.get_card_paths)(card_paths_array_max_length, card_path_max_length, card_paths)
    }

    #[inline]
    pub unsafe fn get_file(
        &self,
        source_file_path: PCWSTR,
        destination_file_path: PCWSTR
    ) -> OpenReturnCodes {
        (self.get_file)(source_file_path, destination_file_path)
    }

    #[inline]
    pub unsafe fn put_file(
        &self,
        source_file_path: PCWSTR,
        destination_file_path: PCWSTR
    ) -> OpenReturnCodes {
        (self.put_file)(source_file_path, destination_file_path)
    }

    #[inline]
    pub unsafe fn select_file(
        &self,
        source_file_path: PCWSTR,
        channel_name: PCWSTR
    ) -> OpenReturnCodes {
        (self.select_file)(source_file_path, channel_name)
    }

    #[inline]
    pub unsafe fn delete_file(&self, file_path: PCWSTR) -> OpenReturnCodes {
        (self.delete_file)(file_path)
    }

    #[inline]
    pub unsafe fn delete_directory(&self, directory_path: PCWSTR) -> OpenReturnCodes {
        (self.delete_directory)(directory_path)
    }

    #[inline]
    pub unsafe fn create_directory(&self, directory_path: PCWSTR) -> OpenReturnCodes {
        (self.create_directory)(directory_path)
    }

    #[inline]
    pub unsafe fn list_directory(
        &self,
        directory_path: PCWSTR,
        result_file_path: PCWSTR
    ) -> OpenReturnCodes {
        (self.list_directory)(directory_path, result_file_path)
    }

    #[inline]
    pub unsafe fn get_version_information(&self, destination_file_path: PCWSTR) -> OpenReturnCodes {
        (self.get_version_information)(destination_file_path)
    }

    #[inline]
    pub unsafe fn get_project_information(
        &self,
        vcp_file_path: PCWSTR,
        destination_file_path: PCWSTR
    ) -> OpenReturnCodes {
        (self.get_project_information)(vcp_file_path, destination_file_path)
    }

    #[inline]
    pub unsafe fn get_current_project_information(
        &self,
        destination_file_path: PCWSTR
    ) -> OpenReturnCodes {
        (self.get_current_project_information)(destination_file_path)
    }

    #[inline]
    pub unsafe fn get_current_project_state(
        &self,
        project_state: *mut OpenProjectStates
    ) -> OpenReturnCodes {
        (self.get_current_project_state)(project_state)
    }

    #[inline]
    pub unsafe fn import_project_settings(
        &self,
        file_name: PCWSTR,
        import_content_file_name: PCWSTR
    ) -> OpenReturnCodes {
        (self.import_project_settings)(file_name, import_content_file_name)
    }

    #[inline]
    pub unsafe fn export_project_settings(&self, file_name: PCWSTR) -> OpenReturnCodes {
        (self.export_project_settings)(file_name)
    }

    #[inline]
    pub unsafe fn write_unsigned_integer_8_bit_array(
        &self,
        dictionary_id: u32,
        value_list_max_length: u32,
        value_list: *const u8
    ) -> OpenReturnCodes {
        (self.write_unsigned_integer_8_bit_array)(dictionary_id, value_list_max_length, value_list)
    }

    #[inline]
    pub unsafe fn get_number_of_ncus(&self, number_of_ncus: *mut u32) -> OpenReturnCodes {
        (self.get_number_of_ncus)(number_of_ncus)
    }
}
