#ifndef MODBUSVALUE_H
#define MODBUSVALUE_H

#include <stdint.h>
#include "StringUtils.h"
#include "types.h"
#include "enums.h"
#include "String.h"
#include "Mudbus.h"
#include <ArduinoLog.h>
#include "xmath.h"

typedef enum
  {
    E_MQ_SLOT_R_1 = 1,
    E_MQ_SLOT_R_2,
    E_MQ_SLOT_RE,
    E_MQ_SLOT_W_1,
    E_MQ_SLOT_W_2,
    E_MQ_SLOT_WE,
  } E_MQ_SLOT;

class ModbusGateway
{
public:
    ModbusGateway() : addr(-1), fn(-1), nbAddr(1), registerMode(MB_REGISTER_MODE::E_MB_REGISTER_MODE_NONE), mudbus(NULL) {}
    ModbusGateway(short _addr) : addr(_addr), nbAddr(1), registerMode(MB_REGISTER_MODE::E_MB_REGISTER_MODE_NONE), mudbus(NULL)
    {
    }
    ModbusGateway(short _addr, short _fc) : addr(_addr), fn(_fc), registerMode(MB_REGISTER_MODE::E_MB_REGISTER_MODE_NONE), mudbus(NULL)
    {
    }
    void setGateway(Mudbus *mudbus)
    {
        this->mudbus = mudbus;
    }

    short getAddress()
    {
        return addr;
    }

    short getNumberAddresses()
    {
        return nbAddr;
    }

    void setNumberAddresses(short _nbAddr)
    {
        nbAddr = _nbAddr;
    }

    void setAddress(short _addr)
    {
        addr = _addr;
    }
    MB_FC getFunctionCode()
    {
        return fn;
    }
    void setFunctionCode(MB_FC fc)
    {
        fn = fc;
    }
    const char *getRegisterMode()
    {
        if (registerMode == MB_REGISTER_MODE::E_MB_REGISTER_MODE_NONE)
        {
            return "--";
        }
        else if (registerMode == MB_REGISTER_MODE::E_MB_REGISTER_MODE_READ)
        {
            return "R-";
        }
        else if (registerMode == MB_REGISTER_MODE::E_MB_REGISTER_MODE_WRITE)
        {
            return "-W";
        }
        else if (registerMode == MB_REGISTER_MODE::E_MB_REGISTER_MODE_READ_WRITE)
        {
            return "RW";
        }
        return "";
    }
    void setRegisterMode(MB_REGISTER_MODE mode)
    {
        registerMode = mode;
    }
    short addr;
    short nbAddr;
    short registerMode;
    MB_FC fn;
    Mudbus *mudbus;
    virtual String getRegisterDescription(short flags = 0) { return ""; };

    // Message Queue
    uchar lastMQSlot;
};

template <typename T>
class ModbusValue : virtual public ModbusGateway
{
public:
    virtual void onSet(const T &value) = 0;
    virtual void onChange(const T &value) = 0;
    virtual T netVal() = 0;
};

template <>
class ModbusValue<int[]> : virtual public ModbusGateway
{
public:
    ModbusValue(int _addr) : ModbusGateway(_addr)
    {
    }
    ModbusValue(int _addr, int _fc) : ModbusGateway(_addr, _fc)
    {
        addr = _addr;
        fn = _fc;
    }
    void onSet(const int value[], int size)
    {
        if (mudbus && RANGE(addr + size, 0, 125))
        {
            for (int i = 0; i < size; i++)
            {
                mudbus->R[addr + i] = value[i];
            }
        }
    }
    int netVal(int index)
    {
        if (mudbus && RANGE(index, 0, 125))
        {
            return mudbus->R[index];
        }
        return 0;
    }
};

template <>
class ModbusValue<int> : virtual public ModbusGateway
{
private:
public:
    ModbusValue() {}
    ModbusValue(int _addr) : ModbusGateway(_addr)
    {
        addr = _addr;
    }
    ModbusValue(int _addr, int _fc) : ModbusGateway(_addr, _fc)
    {
        addr = _addr;
        fn = _fc;
    }
    void onSet(const int &value)
    {
        if (mudbus)
        {
            mudbus->R[addr] = value;
        }
    }
    short netVal()
    {
        if (mudbus)
        {
            return mudbus->R[addr];
        }
        return 0;
    }
};

template <>
class ModbusValue<bool> : virtual public ModbusGateway
{
public:
    ModbusValue(int _addr) : ModbusGateway(_addr)
    {
        addr = _addr;
    }
    ModbusValue(int _addr, int _fc) : ModbusGateway(_addr, _fc)
    {
        addr = _addr;
        fn = _fc;
    }
    void onSet(const bool &value)
    {
        if (mudbus)
        {
            mudbus->C[addr] = value;
        }
    }
    void onChange(const bool &value)
    {
    }
    bool netVal()
    {
        if (mudbus)
        {
            return mudbus->C[addr];
        }
        return false;
    }
};

#endif