
#ifndef ETHERCAT_UTILS_DARRA_ESI_H
#define ETHERCAT_UTILS_DARRA_ESI_H

#pragma once

#ifndef DYNAMIC_LOAD
#define DYNAMIC_LOAD
#endif
#include "../ethercat.h"
#include "../internal/ordinals.h"

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

#define _DARRA_ESI_STRINGIFY(x) #x
#define _DARRA_ESI_TOSTR(x) _DARRA_ESI_STRINGIFY(x)

typedef int  (*esi_pfn_load_file_t)(const char*);
typedef int  (*esi_pfn_load_directory_t)(const char*);
typedef void (*esi_pfn_clear_t)(void);
typedef int  (*esi_pfn_get_loaded_count_t)(void);
typedef int  (*esi_pfn_bind_to_slave_t)(uint16_t, uint16_t, const char*);
typedef int  (*esi_pfn_auto_match_all_t)(uint16_t);
typedef int  (*esi_pfn_apply_all_t)(uint16_t);
typedef int  (*esi_pfn_register_startup_t)(uint16_t, uint16_t);
typedef int  (*esi_pfn_get_dc_sync_mode_t)(uint16_t, uint16_t);

typedef struct {
    esi_pfn_load_file_t        LoadFile;
    esi_pfn_load_directory_t   LoadDirectory;
    esi_pfn_clear_t            Clear;
    esi_pfn_get_loaded_count_t GetLoadedCount;
    esi_pfn_bind_to_slave_t    BindToSlave;
    esi_pfn_auto_match_all_t   AutoMatchAll;
    esi_pfn_apply_all_t        ApplyAllSlaves;
    esi_pfn_register_startup_t RegisterStartupParameters;
    esi_pfn_get_dc_sync_mode_t GetDeviceDcSyncMode;
} esi_ctx_t;

static inline int esi_init(esi_ctx_t* ctx, const dll_t* dll)
{
    if (!ctx || !dll || !dll->handle) return 0;

    ctx->LoadFile        = (esi_pfn_load_file_t)        GET_PROC(dll->handle, "D_" _DARRA_ESI_TOSTR(ORD_EcEsi_LoadFile));
    ctx->LoadDirectory   = (esi_pfn_load_directory_t)   GET_PROC(dll->handle, "D_" _DARRA_ESI_TOSTR(ORD_EcEsi_LoadDirectory));
    ctx->Clear           = (esi_pfn_clear_t)            GET_PROC(dll->handle, "D_" _DARRA_ESI_TOSTR(ORD_EcEsi_Clear));
    ctx->GetLoadedCount  = (esi_pfn_get_loaded_count_t) GET_PROC(dll->handle, "D_" _DARRA_ESI_TOSTR(ORD_EcEsi_GetLoadedCount));
    ctx->BindToSlave     = (esi_pfn_bind_to_slave_t)    GET_PROC(dll->handle, "D_" _DARRA_ESI_TOSTR(ORD_EcEsi_BindToSlave));
    ctx->AutoMatchAll    = (esi_pfn_auto_match_all_t)   GET_PROC(dll->handle, "D_" _DARRA_ESI_TOSTR(ORD_EcEsi_AutoMatchAll));
    ctx->ApplyAllSlaves  = (esi_pfn_apply_all_t)        GET_PROC(dll->handle, "D_" _DARRA_ESI_TOSTR(ORD_EcEsi_ApplyAllSlaves));
    ctx->RegisterStartupParameters = (esi_pfn_register_startup_t)
        GET_PROC(dll->handle, "D_" _DARRA_ESI_TOSTR(ORD_EcEsi_RegisterStartupParameters));
    ctx->GetDeviceDcSyncMode = (esi_pfn_get_dc_sync_mode_t)
        GET_PROC(dll->handle, "D_" _DARRA_ESI_TOSTR(ORD_EcEsi_GetDeviceDcSyncMode));

    return (ctx->LoadFile && ctx->BindToSlave) ? 1 : 0;
}

static inline int esi_dll_load_file(const esi_ctx_t* ctx, const char* file_path)
{
    return (ctx && ctx->LoadFile) ? ctx->LoadFile(file_path) : 0;
}

static inline int esi_dll_load_directory(const esi_ctx_t* ctx, const char* dir_path)
{
    return (ctx && ctx->LoadDirectory) ? ctx->LoadDirectory(dir_path) : 0;
}

static inline void esi_dll_clear(const esi_ctx_t* ctx)
{
    if (ctx && ctx->Clear) ctx->Clear();
}

static inline int esi_dll_get_loaded_count(const esi_ctx_t* ctx)
{
    return (ctx && ctx->GetLoadedCount) ? ctx->GetLoadedCount() : 0;
}

static inline int esi_dll_bind_to_slave(const esi_ctx_t* ctx,
                                           uint16_t master_index, uint16_t slave_index,
                                           const char* file_path)
{
    return (ctx && ctx->BindToSlave) ? ctx->BindToSlave(master_index, slave_index, file_path) : 0;
}

static inline int esi_dll_auto_match_all(const esi_ctx_t* ctx, uint16_t master_index)
{
    return (ctx && ctx->AutoMatchAll) ? ctx->AutoMatchAll(master_index) : 0;
}

static inline int esi_dll_apply_all_slaves(const esi_ctx_t* ctx, uint16_t master_index)
{
    return (ctx && ctx->ApplyAllSlaves) ? ctx->ApplyAllSlaves(master_index) : 0;
}

static inline int esi_dll_register_startup_parameters(const esi_ctx_t* ctx,
                                                         uint16_t master_index, uint16_t slave_index)
{
    return (ctx && ctx->RegisterStartupParameters)
        ? ctx->RegisterStartupParameters(master_index, slave_index) : -1;
}

static inline int esi_dll_get_device_dc_sync_mode(const esi_ctx_t* ctx,
                                                     uint16_t master_index, uint16_t slave_index)
{
    return (ctx && ctx->GetDeviceDcSyncMode)
        ? ctx->GetDeviceDcSyncMode(master_index, slave_index) : -2;
}

static inline int esi_dll_has_esi_dc_sync(const esi_ctx_t* ctx, const dll_t* dll,
                                             uint16_t master_index, uint16_t slave_index)
{
    int mode = esi_dll_get_device_dc_sync_mode(ctx, master_index, slave_index);
    if (mode >= 0)
        return (mode >= 2) ? 1 : 0;
    if (mode == -1)
        return 0;

    return (dll && dll->GetSlaveHasDC) ? (dll->GetSlaveHasDC(master_index, slave_index) != 0) : 0;
}

#ifdef __cplusplus
}
#endif

#endif
