#ifndef CMODBUSBACKEND_H
#define CMODBUSBACKEND_H
#include <QObject>
#include <QTcpSocket>
#include "ModbusRepository.h"
#include <QTimer>


#define MODBUS_EXCEPTION_FCT_MASK 0x80
#define MODBUS_RETRY_DELAY 2000 //millisecs
#define MODBUS_RETRY_MAX_COUNT 2    //resend an unanswered request this many times
#define MODBUS_MAX_NB_REGISTERS 0x7D

enum eModbusFunctions
{
    MODBUS_FCT_READ_HOLDING_REGISTERS = 3,
    MODBUS_WRITE_SINGLE_REGISTER = 6,
    MODBUS_FCT_WRITE_MULTIPLE_REGISTERS = 16
};

enum eModbusExceptionCodes
{
    MODBUS_EXCEPTION_ILLEGAL_FCT = 1,
    MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS = 2,
    MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE = 3,
    MODBUS_EXCEPTION_SERVER_DEVICE_FAILURE = 4,
    MODBUS_EXCEPTION_ACKNOWLEDGE = 5,
    MODBUS_EXCEPTION_SERVER_DEVICE_BUSY = 6,
    MODBUS_EXCEPTION_MEMORY_PARITY_ERROR = 8,
    MODBUS_EXCEPTION_GATEWAY_PATH_UNAVAILABLE = 10,
    MODBUS_EXCEPTION_GATEWAY_TARGET_DEV_NOT_RESPONDING = 11
};

enum eModbusModes
{
    MODBUS_MASTER_MODE,
    MODBUS_SLAVE_MODE,
    MODBUS_INVALID_MODE
};

class CModbusException
{
public:

};

class CModbusPDU
{
public:
    qint8 mFunctionCode;
    QByteArray mData;
};

class CModbusHeader
{
public:
    qint16 mTransactionID;
    qint16 mProtocolID;
    qint16 mMessageLength;
    qint8 mUnitID;
};

class CModbusTransaction
{
public:
    CModbusHeader mHeader;
    CModbusPDU mPDU;
};

class CModbusRequest : public CModbusTransaction
{
public:
    CModbusRequest();
    ~CModbusRequest();
    int mRetries;
    QTimer *mRequestTimer;
    quint16 mStartAddress, mNbRegisters;    //For convinience...
};

class CModbusBackend : public QObject
{
    Q_OBJECT



private:
enum eModbusMasterSMStates
{
    MODBUS_SM_WAIT_FOR__STATE
};

enum eModbusSlaveSMStates
{

};

public:
    CModbusBackend(CModbusRepository *Repo);
    virtual ~CModbusBackend();
    QTcpSocket *mModbusTCPSocketHandle;
    CModbusRepository *mModbusRepo;

    bool mDataLinkValid;
    int mModbusMode;


    void SetDeviceID(qint8 ID){mDeviceID = ID;}

    //Master (client)
    int SendReadHoldingRegistersRequest(quint16 StartAddress, quint16 RegisterCount);
    int SendWriteHoldingRegistersRequest(quint16 StartAddress, quint16 RegisterCount);
    int SendWriteSingleRegisterRequest(quint16 Address);

    void ModbusLinkDisconnected();


    virtual void RegistersDatabaseUpdated(quint16 StartAdderss, quint16 Length) = 0;

    //Master signals
    virtual void ModbusResponseException(quint8 ExceptionCode, quint8 FctCode);

//    //Slave signals
    virtual void ModbusRequestException(quint8 ExceptionCode, quint8 FctCode);


private:

    qint8 mDeviceID;

    //Slave (server)
    int AnalyzeModbusRequest(CModbusTransaction Transaction);
    int ModbusStateMachine(int Event, QByteArray Data);
    int SendModbusResponse(CModbusTransaction RequestTransaction, QByteArray Data);
    int SendErrorResponse(CModbusTransaction RequestTransaction, quint8 ErrorCode);

    //Master (client)
    QList<CModbusRequest*> mRequestsList;
    int SendModbusRequest(CModbusRequest *Request);
    quint16 GetNewTransactionID();
    int AnalyzeModbusResponse(CModbusTransaction Transaction);

    quint16 mTransactionIDCounter;
    int mModbusRequestTimeout;
    int mModbusMaxRetry;

signals:

//    void RegistersDatabaseUpdated(quint16 StartAdderss, quint16 Length);

//    //Master signals
//    void ModbusResponseException(quint8 ExceptionCode, quint8 FctCode);

////    //Slave signals
//    void ModbusRequestException(quint8 ExceptionCode, quint8 FctCode);

    void ModbusRX();


public slots:
    void ModbusDataReady();
    void RequestTimerExpired();


};


QDataStream &operator<<(QDataStream &out, const CModbusHeader &source);
QDataStream &operator>>(QDataStream &in, CModbusHeader &dest);

#endif // CMODBUSBACKEND_H
