#ifndef COSY_NodeDrv
#define COSY_NodeDrv

#ifndef ROOT_TString
#include <TString.h>
#endif

#ifndef COSY_Log
#include "log.h"
#endif

#ifdef __CINT__
typedef Byte_t   BYTE_t;
typedef UShort_t WORD_t;
typedef Short_t  WORDS_t;
typedef UInt_t   LWORD_t;
typedef Int_t    LWORDS_t;
#else
#include "gendef.h"
#endif

typedef struct timeval timeval_t;

class Network;
class NodeGuard;

class NodeDrv : public Log
{
private:
    Network *fNetwork;
    BYTE_t   fId;

    TString fName;

    int fError;

    Bool_t  fIsZombie; // A Zombie node is a node which doesn't answer...

    NodeGuard *fGuard;

    Int_t   fGuardTime;      // Guardtime [ms]
    Int_t   fLifeTimeFactor;

protected:
    void SetError(int err) { fError = err; }
    void DelError()        { fError = 0; }

    Int_t GetLifeTimeFactor() const { return fLifeTimeFactor; }
    Int_t GetGuardTime() const      { return fGuardTime; }

public:
    NodeDrv(BYTE_t nodeid, const char *name=NULL, MLog &out=gLog);
    virtual ~NodeDrv();

    BYTE_t      GetId() const       { return fId;   }
    const char *GetNodeName() const { return fName; }
    Network    *GetNetwork()        { return fNetwork; }

    virtual void Init() = 0;
    virtual bool InitDevice(Network *net);
    virtual void StopDevice() = 0;
    virtual bool Reboot();
    virtual void CheckConnection() = 0;

    int  GetError() const { return fError; }
    bool HasError() const { return fError; }

    bool IsZombieNode() const { return fIsZombie; }
    void SetZombie(bool stopguard=true);

    virtual void HandleSDO(WORD_t idx, BYTE_t subidx, LWORD_t val, timeval_t *tv);
    virtual void HandleSDOOK(WORD_t idx, BYTE_t subidx, LWORD_t data, timeval_t *tv);
    virtual void HandleSDOError(LWORD_t data);

    virtual void HandlePDO1(BYTE_t *data, timeval_t *tv) {}
    virtual void HandlePDO2(BYTE_t *data, timeval_t *tv) {}
    virtual void HandlePDO3(BYTE_t *data, timeval_t *tv) {}
    virtual void HandlePDO4(BYTE_t *data, timeval_t *tv) {}
    virtual void HandleNodeguard(timeval_t *tv);
    virtual void HandleEmergency(timeval_t *tv) {}

    bool SendPDO1(BYTE_t data[8]);
    bool SendPDO2(BYTE_t data[8]);
    bool SendPDO1(BYTE_t m0=0, BYTE_t m1=0, BYTE_t m2=0, BYTE_t m3=0,
                  BYTE_t m4=0, BYTE_t m5=0, BYTE_t m6=0, BYTE_t m7=0);
    bool SendPDO2(BYTE_t m0=0, BYTE_t m1=0, BYTE_t m2=0, BYTE_t m3=0,
                  BYTE_t m4=0, BYTE_t m5=0, BYTE_t m6=0, BYTE_t m7=0);

    bool SendSDO(WORD_t idx, BYTE_t subidx, BYTE_t val, bool store=true);
    bool SendSDO(WORD_t idx, BYTE_t subidx, WORD_t val, bool store=true);
    bool SendSDO(WORD_t idx, BYTE_t subidx, LWORD_t val, bool store=true);

    bool SendSDO(WORD_t idx, BYTE_t val);
    bool SendSDO(WORD_t idx, WORD_t val);
    bool SendSDO(WORD_t idx, LWORD_t val=0);

    bool SendNMT(BYTE_t cmd);
    virtual void SendNodeguard();

    bool RequestSDO(WORD_t idx, BYTE_t subidx=0);

    void WaitForNextPdo1();
    void WaitForNextPdo2();
    void WaitForNextPdo3();
    void WaitForNextPdo4();

    // void WaitForSdos();
    bool WaitForSdo(WORD_t idx, BYTE_t subidx=0, WORDS_t timeout=500, bool zombie=true);

    void EnableCanMsg(BYTE_t fcode);

    void StartGuarding(Bool_t real=kTRUE);
    void StartGuarding(Int_t ms, Int_t ltf, Bool_t real=kTRUE);
    void StopGuarding();

    ClassDef(NodeDrv, 0) // base class for an object describing hardware
};

#endif
