#ifndef COSY_MCosy
#define COSY_MCosy

#include "coord.h"
#include "msgqueue.h"
#include "MThread.h"
#include "MBending.h"

#ifndef COSY_Network
#include "network.h"
#endif
#ifndef MARS_MObservatory
#include "MObservatory.h"
#endif
#ifndef ROOT_TMutex
#include <TMutex.h>
#endif

#define kDEG ((char)0x9c)  // Linux 'ø'

#define WM_WAIT         WM_NULL
//#define WM_PRESET       0x1000
#define WM_POSITION     0x1001
#define WM_TRACK        0x1002
#define WM_STOP         0x1003
//#define WM_CALIB        0x1004
#define WM_TPOINT       0x1005
#define WM_NEWTRACK     0x1006
#define WM_LOADBENDING  0x1007
#define WM_RESETBENDING 0x1008
#define WM_CALCALTAZ    0x1009
#define WM_HOME         0x100a
#define WM_TESTSE       0x100b
#define WM_GEAR         0x100c
#define WM_DISPLAY      0x100d
#define WM_TRACKPOS     0x100e
#define WM_POSITION1    0x100f
#define WM_ENDSWITCH    0x1010

class ShaftEncoder;
class Macs;
class MGCosy;
class MCosy;
class TH1;
class MStarguider;
class MDriveCom;

class MTTalk : public MThread
{
private:
    MCosy *fCosy;

    void *Thread();

public:
    MTTalk(MCosy *cosy) : MThread(false), fCosy(cosy)
    {
        SetPriority(10);
        Detach();
        Start();
    }
    ~MTTalk() { std::cout << "~MTTalk::MTTalk" << std::endl; }
};

class TTimer;
class MCosy : public Network, public MsgQueue, public TObject
{
    friend class MTTalk;
    friend class MPointing;
    friend class MTracking;

private:
    const MObservatory::LocationName_t fObservatory;

    MStarguider *fStarguider;

    ShaftEncoder *fZd1;
    ShaftEncoder *fZd2;
    ShaftEncoder *fAz;

    Macs *fMac1;
    Macs *fMac2;
    Macs *fMac3;

    MGCosy    *fWin;
    MDriveCom *fCom;

    TTimer    *fUpdateGui;
    MTTalk    *fTTalk;    // should be outsourced, like the starguider.
                          // with a generic interface to both...
    ZdAz fTrackingPos;    // [deg] Current tracking position
    ZdAz fTrackingPosRaw; // [deg] Raw tracking position

    TMutex fMutexGui;

    enum BackgroundAction_t
    {
        kBgdNone,
        //kBgdTracking,
        kBgdSeTest,
        kBgdSeTestDispl,
        kBgdGear,
        kBgdGearDispl
    };

    BackgroundAction_t fBackground;    // Flag for present backgroundthread

    ZdAz  fTrackingError; // [rad] Tracking Offset between SE and calc-pos
    ZdAz  fZdAzSoll;      // [rad] Soll position when moving
    RaDec fRaDec;         // Position to track
    ZdAz  fAccuracy;      // Actual accuracy of Tracking
    ZdAz  fVelocity;      // Actual velocity of Tracking
    ZdAz  fMin;
    ZdAz  fMax;

    TH1  *fHist;
    Bool_t fTriggerDisplay;

    XY kResSE;   // describing the resolution of the system [se/U_tel]
    XY kResRE;   // describing the resolution of the system [re/U_mot]
    XY kGear;    // describing the resolution of the system [U_mot/U_tel]
    XY kGearTot; // describing the resolution of the system [re/U_tel]

    MBending fBending;

    UInt_t fStatus;

    ofstream *fOutTp;
    MLog     *fOutRep;

    ZdAz AlignTrackingPos(ZdAz pointing) const;
    Bool_t CheckRange(const ZdAz &d) const;
    Double_t Starguider(Double_t mjd, ZdAz &dest) const;

    void SetStatus(UInt_t stat) { fStatus = stat; }
    UInt_t GetStatus() const { return fStatus; }

    ZdAz GetRePos();
    ZdAz GetRePosPdo();
    ZdAz GetSePos() const; // [se]
    // FIXME: Must depend on the Shaftencoder mounted
    ZdAz GetSePosRad() const { return GetSePos()*TMath::TwoPi()/16384; } // [rad]

    void InitSync();

    void TalkThread();
    void TalkThreadSeTest();
    void TalkThreadGear();

    void DisplayHistTestSe(Bool_t del=kTRUE);
    void DisplayHistGear(Bool_t del=kTRUE);

    int  SetPosition(const ZdAz &dst, Bool_t track=kFALSE);
    void TrackPosition(const RaDec &dst); // ra, dec [rad]

    void TerminateApp();

    int StopWaitingForSDO() const;
    void CheckForError();

    void StopMovement();

    void WaitForEndMovement();

    void Constructor(Int_t id1, Int_t id2, Int_t id3, Int_t id4, Int_t id5, Int_t id6);
    //void ConstructorSE(Int_t id1, Int_t id2, Int_t id3);
    //void ConstructorDemo();

    void ReadConfig();

    bool CheckNetwork();

public:
    MCosy(/*int mode,*/ const char *dev, const int baud, MLog &out=gLog);
    ~MCosy();

    void Start();
    void Stop();

    void *Proc(int msg, void *mp);

    Bool_t HandleTimer(TTimer *t);

    ZdAz GetPointingPos(void) const;
    void SetStarguider(MStarguider *s) { fStarguider = s; }

    static TString GetFileName(const char *name);

    MGCosy *GetWin() { return fWin; }

    ZdAz GetTrackingPosDeg() const { return fTrackingPos; };
    ZdAz GetTrackingPosRaw() const { return fTrackingPosRaw; };
    void SetTrackingPosRE(ZdAz za);

    AltAz GetAltAzDeg() const
    {
        ZdAz sepos = GetSePos()*TMath::TwoPi()/kResSE;
        AltAz za1(TMath::Pi()/2-sepos.Zd(), sepos.Az());
        za1 *= kRad2Deg;
        return za1;
    }

    MLog *GetOutRep() { return fOutRep; }

    //rwagner
    MDriveCom *GetDriveCom() { return fCom; }

    // static ZdAz CorrectTarget(const ZdAz &src, const ZdAz &dst);
    //    static ZdAz RaDec2ZdAz(const double mjd, const RaDec &pos, const RaDec &pm=RaDec(0,0));

    ClassDef(MCosy, 0)
};

#endif
