
#ifndef ETHERCAT_SLAVE_CIA401_H
#define ETHERCAT_SLAVE_CIA401_H

#include "../ethercat_types.h"
#include <stdint.h>
#include <string.h>

#ifdef __cplusplus
extern "C" {
#endif

#define CIA401_OD_DI                 0x6000
#define CIA401_OD_DI_16BIT           0x6001
#define CIA401_OD_DI_POLARITY        0x6002
#define CIA401_OD_DI_FILTER          0x6003
#define CIA401_OD_DI_INTERRUPT       0x6005
#define CIA401_OD_DI_INTERRUPT_EDGE  0x6006
#define CIA401_OD_DI_32BIT           0x6020

#define CIA401_OD_DO                 0x6200
#define CIA401_OD_DO_16BIT           0x6201
#define CIA401_OD_DO_POLARITY        0x6202
#define CIA401_OD_DO_ERROR_MODE      0x6206
#define CIA401_OD_DO_ERROR_VALUE     0x6207
#define CIA401_OD_DO_32BIT           0x6220

#define CIA401_OD_AI                 0x6400
#define CIA401_OD_AI_16BIT           0x6401
#define CIA401_OD_AI_SI_UNIT         0x6420
#define CIA401_OD_AI_GLOBAL_INTERRUPT 0x6423
#define CIA401_OD_AI_UPPER_LIMIT     0x6424
#define CIA401_OD_AI_LOWER_LIMIT     0x6425

#define CIA401_OD_AO                 0x6411
#define CIA401_OD_AO_32BIT           0x6412
#define CIA401_OD_AO_SI_UNIT         0x6430
#define CIA401_OD_AO_ERROR_MODE      0x6443
#define CIA401_OD_AO_ERROR_VALUE     0x6444

#define CIA401_ERROR_MODE_HOLD       0
#define CIA401_ERROR_MODE_SAFE_VALUE 1

#ifndef DYNAMIC_LOAD

static inline uint8_t cia401_read_di(uint16_t master, uint16_t slave, int channel)
{
    uint8_t group = (uint8_t)(channel / 8 + 1);
    int bit = channel % 8;
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_DI, group, FALSE, &size);
    if (data && size >= 1) {
        uint8_t raw = (uint8_t)data[0];
        FreeMemory(data);
        return (raw >> bit) & 1;
    }
    if (data) FreeMemory(data);
    return 0;
}

static inline BOOL cia401_write_do(uint16_t master, uint16_t slave, int channel, uint8_t value)
{
    uint8_t group = (uint8_t)(channel / 8 + 1);
    int bit = channel % 8;

    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_DO, group, FALSE, &size);
    uint8_t current = 0;
    if (data && size >= 1) {
        current = (uint8_t)data[0];
    }
    if (data) FreeMemory(data);

    if (value)
        current |= (uint8_t)(1 << bit);
    else
        current &= (uint8_t)~(1 << bit);

    return dx_sdo_write(master, slave, CIA401_OD_DO, group, FALSE, &current, 1) == 0 ? FALSE : TRUE;
}

static inline uint16_t cia401_read_di16(uint16_t master, uint16_t slave, uint8_t group)
{
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_DI_16BIT, group, FALSE, &size);
    uint16_t val = 0;
    if (data && size >= 2) {
        memcpy(&val, data, 2);
    }
    if (data) FreeMemory(data);
    return val;
}

static inline uint32_t cia401_read_di32(uint16_t master, uint16_t slave, uint8_t group)
{
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_DI_32BIT, group, FALSE, &size);
    uint32_t val = 0;
    if (data && size >= 4) {
        memcpy(&val, data, 4);
    }
    if (data) FreeMemory(data);
    return val;
}

static inline int16_t cia401_read_ai(uint16_t master, uint16_t slave, int channel)
{
    uint8_t sub = (uint8_t)(channel + 1);
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_AI, sub, FALSE, &size);
    int16_t val = 0;
    if (data && size >= 2) {
        memcpy(&val, data, 2);
    }
    if (data) FreeMemory(data);
    return val;
}

static inline uint16_t cia401_read_ai_unsigned(uint16_t master, uint16_t slave, int channel)
{
    uint8_t sub = (uint8_t)(channel + 1);
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_AI, sub, FALSE, &size);
    uint16_t val = 0;
    if (data && size >= 2) {
        memcpy(&val, data, 2);
    }
    if (data) FreeMemory(data);
    return val;
}

static inline int32_t cia401_read_ai32(uint16_t master, uint16_t slave, int channel)
{
    uint8_t sub = (uint8_t)(channel + 1);
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_AI, sub, FALSE, &size);
    int32_t val = 0;
    if (data) {
        if (size >= 4)
            memcpy(&val, data, 4);
        else if (size >= 2) {
            int16_t v16 = 0;
            memcpy(&v16, data, 2);
            val = (int32_t)v16;
        }
        FreeMemory(data);
    }
    return val;
}

static inline uint32_t cia401_read_ai32_unsigned(uint16_t master, uint16_t slave, int channel)
{
    uint8_t sub = (uint8_t)(channel + 1);
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_AI, sub, FALSE, &size);
    uint32_t val = 0;
    if (data) {
        if (size >= 4)
            memcpy(&val, data, 4);
        else if (size >= 2) {
            uint16_t v16 = 0;
            memcpy(&v16, data, 2);
            val = (uint32_t)v16;
        }
        FreeMemory(data);
    }
    return val;
}

static inline BOOL cia401_write_ao(uint16_t master, uint16_t slave, int channel, int16_t value)
{
    uint8_t sub = (uint8_t)(channel + 1);
    uint8_t buf[2];
    memcpy(buf, &value, 2);
    return dx_sdo_write(master, slave, CIA401_OD_AO, sub, FALSE, buf, 2) != 0 ? TRUE : FALSE;
}

static inline uint16_t cia401_get_di_polarity(uint16_t master, uint16_t slave, uint8_t group)
{
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_DI_POLARITY, group, FALSE, &size);
    uint16_t val = 0;
    if (data && size >= 1) {
        if (size >= 2)
            memcpy(&val, data, 2);
        else
            val = (uint16_t)(uint8_t)data[0];
    }
    if (data) FreeMemory(data);
    return val;
}

static inline uint16_t cia401_get_di_filter(uint16_t master, uint16_t slave, uint8_t group)
{
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_DI_FILTER, group, FALSE, &size);
    uint16_t val = 0;
    if (data && size >= 1) {
        if (size >= 2)
            memcpy(&val, data, 2);
        else
            val = (uint16_t)(uint8_t)data[0];
    }
    if (data) FreeMemory(data);
    return val;
}

static inline uint8_t cia401_get_do_error_mode(uint16_t master, uint16_t slave, uint8_t group)
{
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_DO_ERROR_MODE, (uint8_t)(group + 1), FALSE, &size);
    uint8_t val = 0;
    if (data && size >= 1) {
        val = (uint8_t)data[0];
    }
    if (data) FreeMemory(data);
    return val;
}

static inline uint16_t cia401_get_do_error_value(uint16_t master, uint16_t slave, uint8_t group)
{
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_DO_ERROR_VALUE, (uint8_t)(group + 1), FALSE, &size);
    uint16_t val = 0;
    if (data && size >= 1) {
        if (size >= 2)
            memcpy(&val, data, 2);
        else
            val = (uint16_t)(uint8_t)data[0];
    }
    if (data) FreeMemory(data);
    return val;
}

static inline uint8_t cia401_get_ao_error_mode(uint16_t master, uint16_t slave, int channel)
{
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_AO_ERROR_MODE, (uint8_t)(channel + 1), FALSE, &size);
    uint8_t val = 0;
    if (data && size >= 1) {
        val = (uint8_t)data[0];
    }
    if (data) FreeMemory(data);
    return val;
}

static inline int16_t cia401_get_ao_error_value(uint16_t master, uint16_t slave, int channel)
{
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_AO_ERROR_VALUE, (uint8_t)(channel + 1), FALSE, &size);
    int16_t val = 0;
    if (data && size >= 2) {
        memcpy(&val, data, 2);
    }
    if (data) FreeMemory(data);
    return val;
}

static inline uint8_t cia401_read_do(uint16_t master, uint16_t slave, int channel)
{
    uint8_t group = (uint8_t)(channel / 8 + 1);
    int bit = channel % 8;
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_DO, group, FALSE, &size);
    uint8_t val = 0;
    if (data && size >= 1) {
        val = ((uint8_t)data[0] >> bit) & 1;
    }
    if (data) FreeMemory(data);
    return val;
}

static inline uint16_t cia401_read_do16(uint16_t master, uint16_t slave, uint8_t group)
{
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_DO_16BIT, group, FALSE, &size);
    uint16_t val = 0;
    if (data && size >= 2) memcpy(&val, data, 2);
    if (data) FreeMemory(data);
    return val;
}

static inline BOOL cia401_write_do16(uint16_t master, uint16_t slave, uint8_t group, uint16_t value)
{
    uint8_t buf[2];
    memcpy(buf, &value, 2);
    return dx_sdo_write(master, slave, CIA401_OD_DO_16BIT, group, FALSE, buf, 2) != 0 ? TRUE : FALSE;
}

static inline uint32_t cia401_read_do32(uint16_t master, uint16_t slave, uint8_t group)
{
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_DO_32BIT, group, FALSE, &size);
    uint32_t val = 0;
    if (data && size >= 4) memcpy(&val, data, 4);
    if (data) FreeMemory(data);
    return val;
}

static inline BOOL cia401_write_do32(uint16_t master, uint16_t slave, uint8_t group, uint32_t value)
{
    uint8_t buf[4];
    memcpy(buf, &value, 4);
    return dx_sdo_write(master, slave, CIA401_OD_DO_32BIT, group, FALSE, buf, 4) != 0 ? TRUE : FALSE;
}

static inline BOOL cia401_set_di_polarity(uint16_t master, uint16_t slave, uint8_t group, uint8_t polarity)
{
    return dx_sdo_write(master, slave, CIA401_OD_DI_POLARITY, group, FALSE, &polarity, 1) != 0 ? TRUE : FALSE;
}

static inline BOOL cia401_set_di_filter(uint16_t master, uint16_t slave, uint8_t group, uint8_t filter)
{
    return dx_sdo_write(master, slave, CIA401_OD_DI_FILTER, group, FALSE, &filter, 1) != 0 ? TRUE : FALSE;
}

static inline BOOL cia401_set_do_error_mode(uint16_t master, uint16_t slave, uint8_t group, uint8_t mode)
{
    return dx_sdo_write(master, slave, CIA401_OD_DO_ERROR_MODE, (uint8_t)(group + 1), FALSE, &mode, 1) != 0 ? TRUE : FALSE;
}

static inline BOOL cia401_set_do_error_value(uint16_t master, uint16_t slave, uint8_t group, uint8_t value)
{
    return dx_sdo_write(master, slave, CIA401_OD_DO_ERROR_VALUE, (uint8_t)(group + 1), FALSE, &value, 1) != 0 ? TRUE : FALSE;
}

static inline int16_t cia401_read_ao(uint16_t master, uint16_t slave, int channel)
{
    uint8_t sub = (uint8_t)(channel + 1);
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_AO, sub, FALSE, &size);
    int16_t val = 0;
    if (data && size >= 2) memcpy(&val, data, 2);
    if (data) FreeMemory(data);
    return val;
}

static inline int32_t cia401_read_ao32(uint16_t master, uint16_t slave, int channel)
{
    uint8_t sub = (uint8_t)(channel + 1);
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_AO_32BIT, sub, FALSE, &size);
    int32_t val = 0;
    if (data && size >= 4) memcpy(&val, data, 4);
    if (data) FreeMemory(data);
    return val;
}

static inline BOOL cia401_write_ao32(uint16_t master, uint16_t slave, int channel, int32_t value)
{
    uint8_t sub = (uint8_t)(channel + 1);
    uint8_t buf[4];
    memcpy(buf, &value, 4);
    return dx_sdo_write(master, slave, CIA401_OD_AO_32BIT, sub, FALSE, buf, 4) != 0 ? TRUE : FALSE;
}

static inline BOOL cia401_set_ao_error_mode(uint16_t master, uint16_t slave, int channel, uint8_t mode)
{
    return dx_sdo_write(master, slave, CIA401_OD_AO_ERROR_MODE, (uint8_t)(channel + 1), FALSE, &mode, 1) != 0 ? TRUE : FALSE;
}

static inline BOOL cia401_set_ao_error_value(uint16_t master, uint16_t slave, int channel, int16_t value)
{
    uint8_t buf[2];
    memcpy(buf, &value, 2);
    return dx_sdo_write(master, slave, CIA401_OD_AO_ERROR_VALUE, (uint8_t)(channel + 1), FALSE, buf, 2) != 0 ? TRUE : FALSE;
}

static inline BOOL cia401_get_global_interrupt_enable(uint16_t master, uint16_t slave)
{
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_AI_GLOBAL_INTERRUPT, 0x00, FALSE, &size);
    BOOL val = FALSE;
    if (data && size >= 1) {
        val = data[0] != 0 ? TRUE : FALSE;
    }
    if (data) FreeMemory(data);
    return val;
}

static inline BOOL cia401_set_global_interrupt_enable(uint16_t master, uint16_t slave, BOOL enabled)
{
    uint8_t val = enabled ? 1 : 0;
    return dx_sdo_write(master, slave, CIA401_OD_AI_GLOBAL_INTERRUPT, 0x00, FALSE, &val, 1) != 0 ? TRUE : FALSE;
}

static inline int16_t cia401_get_ai_upper_limit(uint16_t master, uint16_t slave, int channel)
{
    uint8_t sub = (uint8_t)(channel + 1);
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_AI_UPPER_LIMIT, sub, FALSE, &size);
    int16_t val = 0;
    if (data && size >= 2) {
        memcpy(&val, data, 2);
    }
    if (data) FreeMemory(data);
    return val;
}

static inline BOOL cia401_set_ai_upper_limit(uint16_t master, uint16_t slave, int channel, int16_t value)
{
    uint8_t sub = (uint8_t)(channel + 1);
    uint8_t buf[2];
    memcpy(buf, &value, 2);
    return dx_sdo_write(master, slave, CIA401_OD_AI_UPPER_LIMIT, sub, FALSE, buf, 2) != 0 ? TRUE : FALSE;
}

static inline int16_t cia401_get_ai_lower_limit(uint16_t master, uint16_t slave, int channel)
{
    uint8_t sub = (uint8_t)(channel + 1);
    int size = 0;
    char* data = dx_sdo_read(master, slave, CIA401_OD_AI_LOWER_LIMIT, sub, FALSE, &size);
    int16_t val = 0;
    if (data && size >= 2) {
        memcpy(&val, data, 2);
    }
    if (data) FreeMemory(data);
    return val;
}

static inline BOOL cia401_set_ai_lower_limit(uint16_t master, uint16_t slave, int channel, int16_t value)
{
    uint8_t sub = (uint8_t)(channel + 1);
    uint8_t buf[2];
    memcpy(buf, &value, 2);
    return dx_sdo_write(master, slave, CIA401_OD_AI_LOWER_LIMIT, sub, FALSE, buf, 2) != 0 ? TRUE : FALSE;
}

#endif

#ifdef __cplusplus
}
#endif

#endif
