#include "MGCosy.h"

#include <iomanip>
#include <fstream>

#include <TGTab.h>         // TGTab
#include <TGMenu.h>        // TGPopupMenu
#include <TGFrame.h>       // TGGroupFrame
#include <TSystem.h>       // gSystem
#include <TGLabel.h>       // TGLabel
#include <TG3DLine.h>      // TGHorizontal3DLine (TGSplitter)
#include <TGButton.h>      // TGButton
#include <TGTextView.h>    // TGTextView
#include <TGComboBox.h>    // TGComboBox
#include <TGTextEntry.h>   // TGTextEntry
#include <TApplication.h>  // gApplication

#include "MLog.h"
#include "MLogManip.h"

#include "MTime.h"
#include "MString.h"
#include "MAstro.h"

#include "MCosy.h"
#include "MGList.h"
#include "MDriveCom.h"
#include "MGImage.h"
#include "MGAccuracy.h"
//#include "MGAccuracyTime.h"
#include "MGCoordinates.h"
#include "MGSkyPosition.h"
#include "MGVelocity.h"

#include "SlaStars.h"

//#define DEBUG
#undef DEBUG
//#define EXPERT
//#define HAS_DEMO

ClassImp(MGCosy);

using namespace std;

#define IDM_EXIT   1
#define IDM_TEXT   2
#define IDM_ASPECT 3


enum
{
    kPB_POSITION,
    kPB_TRACK,
    kPB_STOP,
    kPB_TRACKPOS,
    kPB_CALCALTAZ,
    kPB_TPOINT,
    kPB_ENDSWITCH,
    kPB_ARM,
    kPB_START,
    kPB_DISPLAY1,
    kPB_DISPLAY2,
    kPB_RAp,
    kPB_RAm,
    kPB_DECp,
    kPB_DECm,
    kPB_ZDp,
    kPB_ZDm,
    kPB_AZp,
    kPB_AZm,
    kPB_LoadBending,
    kPB_ResetBending,
    kPB_CALIBSE,
    kCB_PredefPos,
    kCB_StarList,
    kCB_CelestList,
    kPB_SavePreDef,
    kPB_SaveStar,

    // kLog
    kLogCopy, kLogClear, kLogSelect, kLogSearch, kLogSave, kLogAppend
};

void MGCosy::CreateMenu()
{
    //
    //  the button messages are handled by main frame (this)
    //
    TGMenuBar   *menubar  = new TGMenuBar(this, 1, 1, kHorizontalFrame);
    TGPopupMenu *filemenu = menubar->AddPopup("&File");
    TGPopupMenu *logmenu  = menubar->AddPopup("&Log");

    //
    //  crate the menu bar
    //
    filemenu->AddEntry("Set Aspect", IDM_ASPECT);
    filemenu->AddEntry("Exit", IDM_EXIT);
    filemenu->Associate(this);

    //
    // Log Menu
    //
    logmenu->AddEntry("&Copy Selected", kLogCopy);
    logmenu->AddEntry("&Select All",    kLogSelect);
    logmenu->AddSeparator();
    logmenu->AddEntry("Clear &all",     kLogClear);
    //logmenu->AddSeparator();
    //logmenu->AddEntry("Search",         kLogSearch);
    logmenu->AddSeparator();
    logmenu->AddEntry("Sa&ve",          kLogSave);
    logmenu->AddEntry("Save a&ppend",   kLogAppend);
    logmenu->Associate(this);

    //
    //  Seperator beyond menubar
    //
    TGHorizontal3DLine *linesep = new TGHorizontal3DLine(this);

    //
    // Setup Frames
    //
    TGLayoutHints *laymenubar  = new TGLayoutHints (kLHintsNormal | kLHintsExpandX);

    AddFrame(menubar, (TGLayoutHints*)laymenubar->Clone());
    AddFrame(linesep, (TGLayoutHints*)laymenubar->Clone());

    delete laymenubar;
}

void MGCosy::CreateLabel(TGCompositeFrame *f)
{
    const int x = 180;
    const int y = 20;

    TGLabel *l;

    l = new TGLabel(f, "UTC:");
    l->Move(x-60, y);
    fList->Add(l);

    l = new TGLabel(f, "Mjd:");
    l->Move(x-60, y+17);
    fList->Add(l);

    fUtc = new TGLabel(f, "0000/00/00 00:00:00.0");
    fMjd = new TGLabel(f, "00000.000000");
    fUtc->Move(x-25, y);
    fMjd->Move(x-25, y+17);
    fList->Add(fUtc);
    fList->Add(fMjd);

    l = new TGLabel(f, "Encoder-Az:");
    l->Move(x-60, y+57);
    fList->Add(l);

    l = new TGLabel(f, "Encoder-Zd:");
    l->Move(x-60, y+40);
    fList->Add(l);

    fWeather = new TGLabel(f, "");
    fWeather->MoveResize(10, y+272, 280, 16);
    fList->Add(fWeather);

    fLabel1 = new TGLabel(f, "0000000000");
    fLabel2 = new TGLabel(f, "0000000000");
    fLabel1->SetTextJustify(kTextRight);
    fLabel2->SetTextJustify(kTextRight);
    fLabel1->Move(x+30, y+57);
    fLabel2->Move(x+30, y+40);
    fList->Add(fLabel1);
    fList->Add(fLabel2);

    l = new TGLabel(f, "Ra (estimated):");
    l->Move(x-60, y+57+23);
    fList->Add(l);

    l = new TGLabel(f, "Ra (nominal):");
    l->Move(x-60, y+57+40);
    fList->Add(l);

    l = new TGLabel(f, "Dec (estimated):");
    l->Move(x-60, y+57+40+23);
    fList->Add(l);

    l = new TGLabel(f, "Dec (nominal):");
    l->Move(x-60, y+57+40+40);
    fList->Add(l);

    l = new TGLabel(f, "Zd (nominal):");
    l->Move(x-60, y+57+80+23);
    fList->Add(l);

    l = new TGLabel(f, "Az (nominal):");
    l->Move(x-60, y+57+80+40);
    fList->Add(l);

    fRaEst  = new TGLabel(f, "+000h 00.0m");
    fDecEst = new TGLabel(f, "+000d 00.0m");
    fRaSoll = new TGLabel(f, "+000h 00.0m");
    fDecSoll = new TGLabel(f, "+000d 00.0m");
    fZdSoll = new TGLabel(f, "+000d 00.0m");
    fAzSoll = new TGLabel(f, "+000d 00.0m");
    fRaEst->SetTextJustify(kTextRight);
    fDecEst->SetTextJustify(kTextRight);
    fRaSoll->SetTextJustify(kTextRight);
    fDecSoll->SetTextJustify(kTextRight);
    fZdSoll->SetTextJustify(kTextRight);
    fAzSoll->SetTextJustify(kTextRight);
    fRaEst->Move(x+30, y+57+23);
    fRaSoll->Move(x+30, y+57+40);
    fDecEst->Move(x+30, y+57+40+23);
    fDecSoll->Move(x+30, y+57+40+40);
    fZdSoll->Move(x+30, y+57+80+23);
    fAzSoll->Move(x+30, y+57+80+40);
    fList->Add(fRaEst);
    fList->Add(fDecEst);
    fList->Add(fRaSoll);
    fList->Add(fDecSoll);
    fList->Add(fZdSoll);
    fList->Add(fAzSoll);

    fArmed     = new TGLabel(f, "Armed");
    fError     = new TGLabel(f, "Error");
    fMoving    = new TGLabel(f, "Moving");
    fTracking  = new TGLabel(f, "Tracking");
    fStopping  = new TGLabel(f, "Stopping");
    fStopped   = new TGLabel(f, "Stopped");
    //fAvailDkc1 = new TGLabel(f, "- DKC/Zd -");
    //fAvailDkc2 = new TGLabel(f, "- DKC/Az -");
    fAvailTcp  = new TGLabel(f, "- TCP/IP -");

    fDkcReady1    = new TGLabel(f, "bb");
    fDkcReady2    = new TGLabel(f, "bb");
    fDkcEmergency = new TGLabel(f, "Emergency");
    fDkcPcMode    = new TGLabel(f, "PC mode");
    fDkcRF1       = new TGLabel(f, "AF");
    fDkcRF2       = new TGLabel(f, "AF");
    fDkcBrake     = new TGLabel(f, "Zd Brake");
    fDkcPower     = new TGLabel(f, "Power on");

    fMsgAz     = new TGLabel(f, " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
    fMsgZd     = new TGLabel(f, " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

    ULong_t color;

    gClient->GetColorByName("Magenta", color);
    fArmed->SetBackgroundColor(color);
    gClient->GetColorByName("Red", color);
    fError->SetBackgroundColor(color);
    fDkcReady1->SetBackgroundColor(color);
    fDkcReady2->SetBackgroundColor(color);
    fDkcEmergency->SetBackgroundColor(color);
    fDkcPcMode->SetBackgroundColor(color);
    fDkcRF1->SetBackgroundColor(color);
    fDkcRF2->SetBackgroundColor(color);
    fDkcBrake->SetBackgroundColor(color);
    fDkcPower->SetBackgroundColor(color);
    fAvailTcp->SetBackgroundColor(color);
    gClient->GetColorByName("LightBlue", color);
    fMoving->SetBackgroundColor(color);
    gClient->GetColorByName("Blue", color);
    fTracking->SetBackgroundColor(color);
    gClient->GetColorByName("Orange", color);
    fStopping->SetBackgroundColor(color);
    gClient->GetColorByName("Green", color);
    fStopped->SetBackgroundColor(color);

    gClient->GetColorByName("White", color);
    fMsgAz->SetBackgroundColor(color);
    fMsgZd->SetBackgroundColor(color);

    fArmed   ->MoveResize(10,  20,        70, 20);
    fError   ->MoveResize(10,  20+25,     70, 20);
    fMoving  ->MoveResize(10,  20+25+20,  70, 20);
    fTracking->MoveResize(10,  20+25+40,  70, 20);
    fStopping->MoveResize(10,  20+25+60,  70, 20);
    fStopped ->MoveResize(10,  20+25+80,  70, 20);

    fAvailTcp    ->MoveResize( 10, 80+20+102, 75, 20);

    fDkcEmergency->MoveResize( 10, 80+20+124, 75, 20);
    fDkcPcMode   ->MoveResize( 89, 80+20+124, 75, 20);
    fDkcPower    ->MoveResize(168, 80+20+124, 75, 20);
    fDkcBrake    ->MoveResize(247, 80+20+124, 75, 20);

    fDkcReady1->MoveResize( 10,    248+2, 16, 16);
    fDkcReady2->MoveResize( 10,    268+0, 16, 16);
    fDkcRF1   ->MoveResize( 10+18, 248+2, 18, 16);
    fDkcRF2   ->MoveResize( 10+18, 268+0, 18, 16);

    fMsgAz->MoveResize(10+38, 248+2, 310-34, 16); // x-95
    fMsgZd->MoveResize(10+38, 268+0, 310-34, 16); // x-95

    fMsgAz->SetTextJustify(kTextLeft);
    fMsgZd->SetTextJustify(kTextLeft);

    fList->Add(fArmed);
    fList->Add(fError);
    fList->Add(fMoving);
    fList->Add(fTracking);
    fList->Add(fStopping);
    fList->Add(fStopped);
    //fList->Add(fAvailDkc1);
    //fList->Add(fAvailDkc2);
    fList->Add(fAvailTcp);

    fList->Add(fDkcReady1   );
    fList->Add(fDkcReady2   );
    fList->Add(fDkcEmergency);
    fList->Add(fDkcPcMode   );
    fList->Add(fDkcRF1      );
    fList->Add(fDkcRF2      );
    fList->Add(fDkcBrake    );
    fList->Add(fDkcPower    );

    fList->Add(fMsgZd);
    fList->Add(fMsgAz);
}

void MGCosy::CreatePredefinedPos(TGCompositeFrame *tf1, const char *prepos)
{
    TGComboBox *box = new TGComboBox(tf1, kCB_PredefPos);
    box->Resize(120, 20);
    box->Associate(this);

    TGLayoutHints *lay = new TGLayoutHints(kLHintsLeft|kLHintsTop,
                                            27, 0, 200, 0);
    tf1->AddFrame(box, lay);

    fList->Add(box);
    fList->Add(lay);

    ifstream fin(prepos);
    if (!fin)
    {
        gLog << err << "ERROR: Predifined positions in '" << prepos << "' not found." << endl;
        return;
    }

    int i=0;
    while (1)
    {
        Double_t zd, az;
        fin >> zd >> az;

        TString str;
        str.ReadLine(fin);
        if (!fin)
            break;

        box->AddEntry(str, i++);
        fPreDefList.Add(zd, az);
    }
}

void MGCosy::CreateStarList(TGCompositeFrame *tf1)
{
    TGComboBox *box = new TGComboBox(tf1, kCB_StarList);
    box->Resize(170, 20);
    box->Associate(this);

    TGLayoutHints *lay = new TGLayoutHints(kLHintsLeft|kLHintsTop,
                                           27, 0, 70, 0);
    tf1->AddFrame(box, lay);

    fList->Add(box);
    fList->Add(lay);

    ifstream fin("stars.txt");
    if (!fin)
    {
        gLog << err << "ERROR: Star List 'stars.txt' not found." << endl;
        return;
    }

    int i=0;
    while (1)
    {
        Int_t h, m, s, d, am, as;
        fin >> h >> m >> s >> d >> am >> as;

        TString str;
        str.ReadLine(fin);
        if (!fin)
            break;

        box->AddEntry(str, i++);

        fStarList.Add(MAstro::Hms2Hor(h, m, s), MAstro::Dms2Deg(d, am, as));
    }
}

void MGCosy::CreateCelestList(TGCompositeFrame *tf1)
{
    TGComboBox *box = new TGComboBox(tf1, kCB_CelestList);
    box->Resize(170, 20);
    box->Associate(this);

    TGLayoutHints *lay = new TGLayoutHints(kLHintsLeft|kLHintsTop,
                                           27, 0, 70, 0);
    tf1->AddFrame(box, lay);

    fList->Add(box);
    fList->Add(lay);

    box->AddEntry("Moon",    3);
    box->AddEntry("Mercury", 1);
    box->AddEntry("Venus",   2);
    box->AddEntry("Mars",    4);
    box->AddEntry("Jupiter", 5);
    box->AddEntry("Saturn",  6);
    box->AddEntry("Uranus",  7);
    box->AddEntry("Neptune", 8);
    box->AddEntry("Pluto",   9);

    box->Select(3);
}

void MGCosy::CreateTabs(const char *prepos)
{
    TGCompositeFrame *tf1 = fTab->AddTab("Position");
    TGCompositeFrame *tf2 = fTab->AddTab("Track");
    TGCompositeFrame *tf7 = fTab->AddTab("Celest");
    TGCompositeFrame *tf4 = fTab->AddTab("Calib");
#ifdef EXPERT
    TGCompositeFrame *tf5 = fTab->AddTab("Test SE");
    TGCompositeFrame *tf6 = fTab->AddTab("Gear");
#endif
#ifdef HAS_DEMO
    /*TGCompositeFrame *tf3 =*/ fTab->AddTab("Demo");
#endif

    fCZdAz = new MGCoordinates(tf1, kETypeZdAz);
    fCZdAz->Move(27, 105);
    fList->Add(fCZdAz);

    CreatePredefinedPos(tf1, prepos);
    CreateStarList(tf4);
    CreateCelestList(tf7);

    fCRaDec = new MGCoordinates(tf2, kETypeRaDec);
    fCRaDec->Move(27, 105);
    fList->Add(fCRaDec);

    fCCalib = new MGCoordinates(tf4, kETypeRaDec);
    fCCalib->Move(27, 105);
    fList->Add(fCCalib);

    const int x=15;
    const int y=12;
    const int h=16;

    TGLabel *l1 = new TGLabel(tf1, "Move the telescope to a position given in");
    TGLabel *l2 = new TGLabel(tf1, "local coordinates. The given coordinates");
    TGLabel *l3 = new TGLabel(tf1, "are pointing coordinates. Enter the coordinates");
    TGLabel *l4 = new TGLabel(tf1, "manually or choose a predefined position");
    TGLabel *l5 = new TGLabel(tf1, "from the combobox below.");
    l1->Move(x, y);
    l2->Move(x, y+h);
    l3->Move(x, y+2*h);
    l4->Move(x, y+3*h);
    l5->Move(x, y+4*h);
    fList->Add(l1);
    fList->Add(l2);
    fList->Add(l3);
    fList->Add(l4);
    fList->Add(l5);

    l1 = new TGLabel(tf2, "Track a position given in sky coordinates.");
    l2 = new TGLabel(tf2, "Right Ascension and declination must be given");
    l3 = new TGLabel(tf2, "in the FK5, J2000 coordinate system.");
    l1->Move(x, y);
    l2->Move(x, y+h);
    l3->Move(x, y+2*h);
    fList->Add(l1);
    fList->Add(l2);
    fList->Add(l3);

    l1 = new TGLabel(tf4, "Start the calibration using the Start button.");
    l2 = new TGLabel(tf4, "Write a coordinate pair to a TPoint file using");
    l3 = new TGLabel(tf4, "the TPoint button.");
    //l3 = new TGLabel(tf4, "the TPoint button. To set the Shaftencoder offset");
    //l4 = new TGLabel(tf4, "use the Calib SE button.");
    l1->Move(x, y);
    l2->Move(x, y+h);
    l3->Move(x, y+2*h);
    //l4->Move(x, y+3*h);
    fList->Add(l1);
    fList->Add(l2);
    fList->Add(l3);
    //fList->Add(l4);

    l1 = new TGLabel(tf7, "Track a celestial object by object name.");
//    l2 = new TGLabel(tf7, "Right Ascension and declination must be given");
//    l3 = new TGLabel(tf7, "in the FK5, J2000 coordinate system.");
    l1->Move(x, y);
//    l2->Move(x, y+h);
//    l3->Move(x, y+2*h);
    fList->Add(l1);
//    fList->Add(l2);
//    fList->Add(l3);


#ifdef EXPERT
    l1 = new TGLabel(tf5, "START starts histograming the differences of");
    l2 = new TGLabel(tf5, "the two shaftencoders at the elevation axis.");
    l3 = new TGLabel(tf5, "Use STOP to stop histograming and display the");
    l4 = new TGLabel(tf5, "results on the screen.");
    l1->Move(x, y);
    l2->Move(x, y+h);
    l3->Move(x, y+2*h);
    l4->Move(x, y+3*h);
    fList->Add(l1);
    fList->Add(l2);
    fList->Add(l3);
    fList->Add(l4);

    l1 = new TGLabel(tf6, "FIXME Text missing / Only one axis?");
    l1->Move(x, y);
    fList->Add(l1);
#endif

    //
    // light green: 3   light red:  2   blue?:      2
    // dark green:  8   dark red:  50   dark blue?: 1

    //
    // blue:  0-7, 9, 10,11, 14, 15, 18, 19, 22, 23, 27, 31, 33-39, 41-43
    //        46, 47
    // green: 8, 12,13, 16, 17, 20, 21, 24, 25, 26, 28, 29, 30, 40, 44
    //        45, 48
    // gray:  31, 113-
    // red:   164, 192, 224, 232
    //
    TGTextButton *but;

    but= new TGTextButton(tf2, "Ra -",  kPB_RAm);
    but->MoveResize(25, 210, 50, 25);
    but->SetToolTipText("Right ascension -= 1'");
    but->Associate(this);
    fList->Add(but);
    but= new TGTextButton(tf2, "RA +",  kPB_RAp);
    but->MoveResize(90, 210, 50, 25);
    but->SetToolTipText("Right ascension += 1'");
    but->Associate(this);
    fList->Add(but);
    but= new TGTextButton(tf2, "DEC +",  kPB_DECp);
    but->MoveResize(55, 185, 50, 25);
    but->SetToolTipText("Declination += 1'");
    but->Associate(this);
    fList->Add(but);
    but= new TGTextButton(tf2, "DEC -",  kPB_DECm);
    but->MoveResize(55, 235, 50, 25);
    but->SetToolTipText("Declination -= 1'");
    but->Associate(this);
    fList->Add(but);

    but = new TGTextButton(tf1, "Move'n'Track", kPB_TRACKPOS);
    but->MoveResize(25, 242, 100, 25);
    but->SetToolTipText("Move telescope to a Zd/Az position and start tracking.");
    but->Associate(this);
    fList->Add(but);

    but= new TGTextButton(tf2, "Calc Zd/Az",  kPB_CALCALTAZ);
    but->MoveResize(165, 197, 80, 25);
    but->SetToolTipText("Calculate Zd/Az corresponding to Ra/Dec.");
    but->Associate(this);
    fList->Add(but);

    but = new TGTextButton(tf4, "TPoint", kPB_TPOINT);
    but->MoveResize(139, 213, 62, 25);
    but->SetToolTipText("Trigger writing a tpoint coordinate pair.");
    but->Associate(this);
    fList->Add(but);

    but = new TGTextButton(tf4, "Endswitch", kPB_ENDSWITCH);
    but->MoveResize(204, 213, 62, 25);
    but->SetToolTipText("Print coordinates in 'Endswitch-Position' units.");
    but->Associate(this);
    fList->Add(but);

#ifdef EXPERT
    but = new TGTextButton(tf1, "Arm", kPB_ARM);
    but->MoveResize(204, 213, 62, 25);
    but->SetToolTipText("Arm the telescop (allow for movement).");
    but->Associate(this);
    fList->Add(but);

    ULong_t col;
    gClient->GetColorByName("Magenta", col);
    but->SetBackgroundColor(col);

    but= new TGTextButton(tf1, "New Position",  kPB_SavePreDef);
    but->MoveResize(165, 197, 80, 25);
    but->SetToolTipText("Save new predefined position.");
    but->Associate(this);
    fList->Add(but);

    but= new TGTextButton(tf4, "New", kPB_SaveStar);
    but->MoveResize(211, 69, 60, 23);
    but->SetToolTipText("Save new Source position.");
    but->Associate(this);
    fList->Add(but);

    but = new TGTextButton(tf5, "Display", kPB_DISPLAY1);
    but->MoveResize(160, 197, 80, 25);
    but->SetToolTipText("Display Histogram.");
    but->Associate(this);
    fList->Add(but);

    but = new TGTextButton(tf6, "Display", kPB_DISPLAY2);
    but->MoveResize(160, 197, 80, 25);
    but->SetToolTipText("Display Histogram.");
    but->Associate(this);
    fList->Add(but);

    but = new TGTextButton(tf4, "Load", kPB_LoadBending);
    but->MoveResize(151, 185, 50, 25);
    but->SetToolTipText("Load bending corrections from file 'bending.txt'");
    but->Associate(this);
    fList->Add(but);

    but = new TGTextButton(tf4, "Reset", kPB_ResetBending);
    but->MoveResize(206, 185, 50, 25);
    but->SetToolTipText("Reset bending correction (coefficients=0)");
    but->Associate(this);
    fList->Add(but);

    but= new TGTextButton(tf4, "Zd -",  kPB_ZDm);
    but->MoveResize(25, 210, 50, 25);
    but->SetToolTipText("Zenith Distance -= 1SE");
    but->Associate(this);
    fList->Add(but);
    but= new TGTextButton(tf4, "Zd +",  kPB_ZDp);
    but->MoveResize(90, 210, 50, 25);
    but->SetToolTipText("Zenith Distance += 1SE");
    but->Associate(this);
    fList->Add(but);
    but= new TGTextButton(tf4, "Az +",  kPB_AZp);
    but->MoveResize(55, 185, 50, 25);
    but->SetToolTipText("Azimuth += 1SE");
    but->Associate(this);
    fList->Add(but);
    but= new TGTextButton(tf4, "Az -",  kPB_AZm);
    but->MoveResize(55, 235, 50, 25);
    but->SetToolTipText("Azimuth -= 1SE");
    but->Associate(this);
    fList->Add(but);
#endif

    ULong_t color;

    but = new TGTextButton(this, "Start", kPB_START);
    gClient->GetColorByName("Green", color);
    but->SetBackgroundColor(color);
    but->MoveResize(147, 295, 62, 25);
    but->SetToolTipText("Start a telescope movement.");
    fList->Add(but);

    but = new TGTextButton(this, "Stop",  kPB_STOP);

    gClient->GetColorByName("Red", color);
    but->SetBackgroundColor(color);
    but->MoveResize(212, 295, 62, 25);
    but->SetToolTipText("Stop any movement of telescope.");
    fList->Add(but);

    /*
     const Double_t ca   = 0; // Left-Right Collimation Error
     const Double_t an   = 0; // Azimuth Axis Misalignment (N-S)
     const Double_t aw   = 0; // Azimuth Axis Misalignment (E-W)
     const Double_t npae = 0; // Az-El Nonperpendicularity
     const Double_t nrx  = 0; // Nasmyth rotator displacement, horizontan
     const Double_t nry  = 0; // Nasmyth rotator displacement, vertical
     const Double_t crx  = 0; // Alt/Az Coude Displacement (N-S)
     const Double_t cry  = 0; // Alt/Az Coude Displacement (E-W)
    l1 = new TGLabel(tf5, "CA");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x, y+2*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    l1 = new TGLabel(tf5, "AN");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x, y+3*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    l1 = new TGLabel(tf5, "AW");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x, y+4*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    l1 = new TGLabel(tf5, "NPAE");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x, y+5*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    l1 = new TGLabel(tf5, "NRX");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x, y+6*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    l1 = new TGLabel(tf5, "NRY");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x, y+7*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    l1 = new TGLabel(tf5, "CRX");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x, y+8*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    l1 = new TGLabel(tf5, "CRY");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x, y+9*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    const Double_t ie   = 0; // Index Error in Elevation
    const Double_t ia   = 0; // Index Error in Azimuth
    const Double_t eces = 0; // Elevation Centering Error (sin)
    const Double_t ecec = 0; // Elevation Centering Error (cos)
    const Double_t aces = 0; // Azimuth Centering Error (sin)
    const Double_t acec = 0; // Azimuth Centering Error (cos)
    l1 = new TGLabel(tf5, "IE");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x+150, y+3*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    l1 = new TGLabel(tf5, "IA");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x+150, y+4*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    l1 = new TGLabel(tf5, "ECES");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x+150, y+5*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    l1 = new TGLabel(tf5, "ECEC");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x+150, y+6*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    l1 = new TGLabel(tf5, "ACES");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x+150, y+4*h*1.5);
    l1->MapWindow();
    fList->Add(l1);
    l1 = new TGLabel(tf5, "IA");
    l1->SetTextJustify(kTextLeft);
    l1->Move(x+150, y+4*h*1.5);
    l1->MapWindow();
    fList->Add(l1);


    TGTextEntry *entry = new TGTextEntry(tf5, "****", kEF_BendIE);
    entry->Associate(this);
    entry->Move(x+50, y+2*h*1.5);
    entry->MapWindow();
    fList->Add(entry);

    entry = new TGTextEntry(tf5, "*****", kEF_BendIA);
    entry->Associate(this);
    entry->Move(x+50, y+3*h*1.5);
    entry->MapWindow();
    fList->Add(entry);
    */
}

MGCosy::MGCosy(MObservatory::LocationName_t key, const char *prepos, MsgQueue *q)
    : TGMainFrame(gClient->GetRoot(), 1, 1), fObservatory(key), fQueue(q), fCelestObj(3)
{
    // This means that all objects added with AddFrame are deleted
    // automatically, including all LayoutHints.
    SetCleanup();

    fList = new MGList;
    fList->SetOwner();

    fFont = gVirtualX->LoadQueryFont("7x13bold");

    CreateMenu();

    TGHorizontalFrame *f1 = new TGHorizontalFrame(this, 0, 0);
    TGHorizontalFrame *f2 = new TGHorizontalFrame(this, 0, 0);
    f1->SetCleanup();
    f2->SetCleanup();
    //fList->Add(f1);
    //fList->Add(f2);

    fTab = new TGTab(f1, 300, 300);
    //fList->Add(fTab);

    fSkyPosition = new MGSkyPosition(fObservatory, f1, 300);

    fAccuracy = new MGAccuracy(f1, 300);
    fAccuracy->SetColorScale(1);
    fAccuracy->DrawText("1.0'", "2.0'", "3.0'", "Ctrl Deviation [min]");
    //fAccuracy->DrawText("0.5se", "1.0se", "2.0se", "");
    fAccuracy->DrawCircles();

    fImage = new MGImage(f2, 768/2-1, 576/2-1, kRaisedFrame);
    //fVelocity = new MGVelocity(f2, "Velocity [U_{mot}/min]", 300);
    //    fOffset      = new MGVelocity   (f, "Offset se-re [']", 300);
//  fAccuracyTime = new MGAccuracyTime(f2, 300);

    //fList->Add(fSkyPosition);
    //fList->Add(fAccuracy);
    //  fList->Add(fAccuracyTime);
    //  fList->Add(fVelocity);
    //  fList->Add(fOffset);
    //fList->Add(fImage);

    TGGroupFrame *frame = new TGGroupFrame(f2, "Status");
    // frame->SetCleanup();
    //fList->Add(frame);

    fLog = new TGTextView(f2, 300, 300);
    if (fFont)
        fLog->SetFont(fFont);
    //fList->Add(fLog);

    TGLayoutHints *hints1  = new TGLayoutHints(kLHintsNormal  | kLHintsExpandX|kLHintsExpandY, 6, 6, 6, 3);
    TGLayoutHints *hints2  = new TGLayoutHints(kLHintsNormal  | kLHintsExpandX|kLHintsExpandY, 6, 6, 3, 6);

    TGLayoutHints *hints1a = new TGLayoutHints(kLHintsLeft    | kLHintsCenterY|kLHintsExpandX|kLHintsExpandY);
    TGLayoutHints *hints2a = new TGLayoutHints(kLHintsCenterX | kLHintsCenterY|kLHintsExpandX|kLHintsExpandY, 6, 6);
    TGLayoutHints *hints3a = new TGLayoutHints(kLHintsRight   | kLHintsCenterY|kLHintsExpandX|kLHintsExpandY);
    TGLayoutHints *hints1b = new TGLayoutHints(kLHintsLeft    | kLHintsCenterY|kLHintsExpandX|kLHintsExpandY);
    TGLayoutHints *hints2b = new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, 6, 6);
    TGLayoutHints *hints3b = new TGLayoutHints(kLHintsRight   | kLHintsCenterY|kLHintsExpandX|kLHintsExpandY);

    /*
    fList->Add(hints1);
    fList->Add(hints2);
    fList->Add(hints1a);
    fList->Add(hints2a);
    fList->Add(hints3a);
    fList->Add(hints1b);
    fList->Add(hints2b);
    fList->Add(hints3b);
    */
    // Layout upper frame
    f1->AddFrame(fTab,         hints1a);
    f1->AddFrame(fSkyPosition, hints2a);
    f1->AddFrame(fAccuracy,    hints3a);

    // Layout upper frame
    f2->AddFrame(frame,         hints1b);
    f2->AddFrame(fImage,        hints2b);
    //f2->AddFrame(fVelocity,    hints2b);
    //f2->AddFrame(fAccuracyTime, hints2b);
    f2->AddFrame(fLog,          hints3b);

    // Layout window
    AddFrame(f1, hints1);
    AddFrame(f2, hints2);

    CreateTabs(prepos);
    CreateLabel(frame);

    
    //
    //   Map the window, set up the layout, etc.
    //
    const Int_t ww = 1010;
    const Int_t hh =  680;
    SetWMSizeHints(ww, hh, ww, hh, 1, 1);  // set the smallest and biggest size of the Main frame

    SetWindowName("Cosy Main Window");
    SetIconName("Cosy");

#ifdef DEBUG
    cout << "MGCosy: Map Window" << endl;
#endif
    MapSubwindows();
    Resize(ww, hh); //GetDefaultSize());
    MapWindow();
}

// ======================================================================

MGCosy::~MGCosy()
{
    /*
    TIter Next(fList);
    TObject *o=0;
    while((o=Next()))
    {
        cout << o << " " << flush;
        cout << o->ClassName() << " " << flush;
        cout << o->GetName() << " " << endl;
        delete o;
    }
    */
#ifdef DEBUG
    cout << "MGCosy::~MGCosy called." << endl;
#endif
    delete fList;
#ifdef DEBUG
    cout << "Deleting MGCosy::fFont" << endl;
#endif
    if (fFont)
        gVirtualX->DeleteFont(fFont);
#ifdef DEBUG
    cout << "MGCosy::~MGCosy done." << endl;
#endif
}

void MGCosy::SetLabelColor(TGLabel *label, Bool_t col)
{
    ULong_t red, green;

    gClient->GetColorByName("Red",   red);
    gClient->GetColorByName("Green", green);

    if (col && label->TestBit(BIT(14)))
        return;

    if (!col && !label->TestBit(BIT(14)))
        return;

    col ? label->SetBit(BIT(14)) : label->ResetBit(BIT(14));

    label->UnmapWindow();
    label->SetBackgroundColor(col ? green : red);
    label->MapWindow();
}

// ======================================================================
void MGCosy::EnableLabel(TGLabel *label, Bool_t stat)
{
    stat ? label->MapWindow() : label->UnmapWindow();

    /*
    TGGC *fRedTextGC(TGButton::GetDefaultGC())
        // Set foreground color in graphics context for drawing of
        // TGlabel and TGButtons with text in red.
        ULong_t red;
    gClient->GetColorByName("red", red);
    fRedTextGC.SetForeground(red);
    */
}

void MGCosy::UpdateRaDec(ZdAz &pos, double mjd, RaDec &radec)
{
    static Int_t rai=~0;
    static Int_t deci=~0;
    static Int_t ras=~0;
    static Int_t decs=~0;

    static SlaStars sla(fObservatory);
    sla.SetMjd(mjd);

    RaDec rd = sla.CalcRaDec(pos*2*TMath::Pi()/360);

    char text[64];

    UShort_t h, d;
    Double_t hm, dm;
    Char_t   sh, sd;

    // Calculate Display values
    MAstro::Rad2Hm(rd.Ra(),  sh, h, hm);
    MAstro::Rad2Dm(rd.Dec(), sd, d, dm);

    rd.Ra(rd.Ra()  * 12/TMath::Pi());
    rd.Dec(rd.Dec()*180/TMath::Pi());

    RaDec test = rd*600;
    if (rai!=(int)test.Ra())
    {
        rai = (int)test.Ra();
        //sprintf(text, "%c%dh %.1fm", rd.Ra()<0?'-':'+', abs((int)rd.Ra()), 0.1*(abs((int)test.Ra())%600));
        sprintf(text, "%c%dh %.1fm", sh, h, hm);
        fRaEst->SetText(new TGString(text));
    }
    if (deci!=(int)test.Dec())
    {
        deci = (int)test.Dec();
        //sprintf(text, "%c%dd %.1fm", rd.Dec()<0?'-':'+' , abs((int)rd.Dec()), 0.1*(abs((int)test.Dec())%600));
        sprintf(text, "%c%dd %.1fm", sd, d, dm);
        fDecEst->SetText(new TGString(text));
    }

    // Align RaDec
    radec.Ra(radec.Ra()  * 12/TMath::Pi());
    radec.Dec(radec.Dec()*180/TMath::Pi());
    if (radec.Dec()>90|| radec.Dec()<-90)
    {
        radec.Ra(radec.Ra()+12);
        radec.Dec(180-radec.Dec());
    }
    radec.Ra(fmod((radec.Ra()+48), 24));

    // Calculate display values
    MAstro::Hor2Hm(radec.Ra(),  sh, h, hm);
    MAstro::Deg2Dm(radec.Dec(), sd, d, dm);

    test = radec*600;
    if (ras!=(int)test.Ra())
    {
        ras = (int)test.Ra();
        //sprintf(text, "%c%dh %.1fm", radec.Ra()<0?'-':'+', abs((int)radec.Ra()), 0.1*(abs((int)test.Ra())%600));
        sprintf(text, "%c%dh %.1fm", sh, h, hm);
        fRaSoll->SetText(new TGString(text));
    }
    if (decs!=(int)test.Dec())
    {
        decs = (int)test.Dec();
        //sprintf(text, "%c%dd %.1fm", radec.Dec()<0?'-':'+' , abs((int)radec.Dec()), 0.1*(abs((int)test.Dec())%600));
        sprintf(text, "%c%dd %.1fm", sd, d, dm);
        fDecSoll->SetText(new TGString(text));
    }
}
/*
void MGCosy::UpdateOffset(ZdAz &off)
{
    static Int_t zd=~0;
    static Int_t az=~0;

    char text[21];

    if (zd!=(Int_t)off.Zd())
    {
        zd = (Int_t)off.Zd();
        sprintf(text, "%dre", zd);
        fOffsetZd->SetText(new TGString(text));
    }
    if (az!=(Int_t)off.Az())
    {
        az = (Int_t)off.Az();
        sprintf(text, "%dre", az);
        fOffsetAz->SetText(new TGString(text));
    }
}
*/
void MGCosy::UpdateZdAz(ZdAz &soll)
{
    static Int_t zd=~0;
    static Int_t az=~0;

    char text[21];

    UShort_t z, a;
    Double_t zm, am;
    Char_t   sz, sa;

    MAstro::Rad2Dm(soll.Zd(), sz, z, zm);
    MAstro::Rad2Dm(soll.Az(), sa, a, am);

    const ZdAz test = soll*kRad2Deg*600;
    if (zd!=(int)test.Zd())
    {
        zd = (int)test.Zd();
        sprintf(text, "%c%dd %.1fm", sz, z, zm);
        fZdSoll->SetText(new TGString(text));
    }
    if (az!=(int)test.Az())
    {
        az = (int)test.Az();
        sprintf(text, "%c%dd %.1fm", sa, a, am);
        fAzSoll->SetText(new TGString(text));
    }
}

double MGCosy::UpdateTime()
{
    MTime time;
    time.Now();

    static TString oldtxt;

    TString text(time.GetString());
    text.Remove(text.Last('.')+2);

    if (text!=oldtxt)
    {
        fUtc->SetText(new TGString(text));
        oldtxt = text;
    }

    static ULong_t oldmjd=0;
    const double mjd = time.GetMjd();

    if (oldmjd != (ULong_t)(1000000*mjd))
    {
        fMjd->SetText(new TGString(MString::Format("%12.6f", mjd)));
        oldmjd = (ULong_t)(1000000*mjd);
    }

    return mjd;
}

void MGCosy::Update(ZdAz pos, ZdAz acc, /*ZdAz vel, ZdAz off,*/ RaDec radec,
                    ZdAz soll, UInt_t stat, UInt_t pdo3, UInt_t stat2, Bool_t armed, const TString &statzd, const TString &stataz)
{
    // acc [rad]
    acc *= kRad2Deg;   // [deg]

    const double mjd = UpdateTime();
    fSkyPosition->Update(pos, mjd);
    //UpdateOffset(off);
    UpdateRaDec(pos, mjd, radec);
    UpdateZdAz(soll);

    EnableLabel(fError,    stat&MDriveCom::kError);
    EnableLabel(fMoving,   stat&MDriveCom::kMoving);
    EnableLabel(fTracking, stat&MDriveCom::kTracking);
    EnableLabel(fStopping, stat&MDriveCom::kStopping);
    EnableLabel(fStopped,  stat&MDriveCom::kStopped);
    EnableLabel(fRaSoll,   stat&MDriveCom::kTracking);
    EnableLabel(fDecSoll,  stat&MDriveCom::kTracking);
    EnableLabel(fZdSoll,   stat&MDriveCom::kMoving || stat&MDriveCom::kTracking);
    EnableLabel(fAzSoll,   stat&MDriveCom::kMoving || stat&MDriveCom::kTracking);

    SetLabelColor(fAvailTcp, stat2);

    if (!stat2)
        pdo3 = 0;

    //   1 X Ab
    //   2 1
    //   4 Emergency
    //   8 OverVolt
    //  10 Move (Drehen-soll)
    //  20 X Af
    //  40 1
    //  80 Power on Az
    // ------------------
    // 100 NOT UPS Alarm
    // 200 UPS on Battery
    // 400 UPS charging

    SetLabelColor(fDkcReady1,     pdo3&0x00000001);
    SetLabelColor(fDkcReady2,     pdo3&0x00010000);
    SetLabelColor(fDkcEmergency, (pdo3&0x00040004)==0x00040004);
    SetLabelColor(fDkcPcMode,    (pdo3&0x00100010)!=0x00100010);
    SetLabelColor(fDkcRF1,        pdo3&0x00000020);
    SetLabelColor(fDkcRF2,        pdo3&0x00200000);
    SetLabelColor(fDkcBrake,     (pdo3&0x00400040)==0x00400040);
    SetLabelColor(fDkcPower,     (pdo3&0x00800080)==0x00800080);

    EnableLabel(fArmed, armed);

    fMsgZd->SetText(new TGString(TString(" Zd: ")+statzd));
    fMsgAz->SetText(new TGString(TString(" Az: ")+stataz));


    if (stat&MDriveCom::kTracking)
    {
        fAccuracy->Update(pos, acc);
        // fAccuracyTime->Update(pos, acc);
        // fVelocity->Update(vel);
        fAccuracy->MapWindow();
        // fAccuracyTime->MapWindow();
        // fVelocity->MapWindow();
    }
    else
    {
        fAccuracy->UnmapWindow();
        // fAccuracyTime->UnmapWindow();
        // fVelocity->UnmapWindow();
    }
    //    stat&kTracking ? fOffset->MapWindow()   : fOffset->UnmapWindow();

    //unsigned char empty[(768/2-1)*(576/2-1)];
    //fImage->DrawImg(empty);
    //fImage->DoRedraw();

   // if (IsMapped())
   //     fImage->DoRedraw();
}

void MGCosy::UpdateWeather(const MCeCoCom &com)
{
    ULong_t color;
    switch (com.GetWeatherStatus())
    {
    case 0:
        color=TGFrame::GetDefaultFrameBackground();
        break;
    case 1:
        gClient->GetColorByName("Yellow", color);
        break;
    case 2:
        gClient->GetColorByName("Orange", color);
        break;
    default:
        gClient->GetColorByName("Red", color);
        break;
    }

    if (com.HasAlarm())
    {
        static int i=0;
        if ((i++/4)%2==0)
            color = TGFrame::GetDefaultFrameBackground();
    }

    const TString old(fWeather->GetText()->GetString());
    const TString txt(com.GetWeather());

    // Color is only updated if the text has changed
    if (old!=txt || color!=fWeather->GetBackground())
        fWeather->SetText(txt);

    if (color!=fWeather->GetBackground())
        fWeather->SetBackgroundColor(color);
}

void MGCosy::CloseWindow()
{
    // Got close message for this MainFrame. Calls parent CloseWindow()
    // (which destroys the window) and terminate the application.
    // The close message is generated by the window manager when its close
    // window menu item is selected.

    // gSystem->ExitLoop();
    // gSystem->DispatchOneEvent(kTRUE);

    // TGMainFrame::CloseWindow();
    cout << "Closing window - waiting until all nodes are stopped." << endl;
    fQueue->PostMsg(WM_QUIT);
    cout << "Closing window - done." << endl;
    // gApplication->Terminate(0);
}

void MGCosy::StartTrack(bool track)
{
    cout << "Start tracking." << endl;

    XY xy0 = fCRaDec->GetCoordinates();
    XY xy1 = track ? xy0 : fCCalib->GetCoordinates();

    RaDec dest0(xy0.X()*15., xy0.Y()); // xy.X()  [h]->[ø]
    RaDec dest1(xy1.X()*15., xy1.Y()); // xy.X()  [h]->[ø]

    cout << "TrackPos: " << dest0.Ra() << "\xb0 " << dest0.Dec() << "\xb0" << endl;
    cout << "CalibPos: " << dest1.Ra() << "\xb0 " << dest1.Dec() << "\xb0" << endl;

    RaDec dest[2] = { dest0, dest1 };

    fQueue->PostMsg(WM_TRACK, &dest, sizeof(dest));

    cout << "PostMsg (WM_Track) returned." << endl;
}

void MGCosy::StartCelest()
{
    cout << "Start celest." << endl;

    Double_t tx[3] = { (double)fCelestObj, 0, 0 };

    fQueue->PostMsg(WM_CELEST, tx, sizeof(Double_t)*3);

    cout << "PostMsg (WM_CELEST) returned." << endl;
}

void MGCosy::StartTrackPos(Long_t mp1)
{
    cout << "Start tracking Zd/Az pos." << endl;

    const XY &xy = fCZdAz->GetCoordinates();

    ZdAz za(xy.X(), xy.Y());

    switch (mp1)
    {
    case kPB_ZDm:
        za.Zd(za.Zd()-360/16384.);
        break;
    case kPB_ZDp:
        za.Zd(za.Zd()+360/16384.);
        break;
    case kPB_AZm:
        za.Az(za.Az()-360/16384.);
        break;
    case kPB_AZp:
        za.Az(za.Az()+360/16384.);
        break;
    }

    cout << za.Zd() << "\xb0 " << za.Az() << "\xb0" << endl;

    fQueue->PostMsg(WM_TRACKPOS, &za, sizeof(za));

    cout << "PostMsg (WM_TrackPos) returned." << endl;
}

void MGCosy::StartPos()
{
    cout << "Start positioning." << endl;

    XY xy = fCZdAz->GetCoordinates();
    ZdAz dest(xy.X(), xy.Y());

    cout << dest.Zd() << kDEG << " " << dest.Az() << kDEG << endl;

    fQueue->PostMsg(WM_POSITION, &dest, sizeof(dest));

    cout << "PostMsg (WM_Position) returned." << endl;
}

//
// ************************** For demo purpose **********************
//
#ifdef HAS_DEMO

#include <TRandom.h>
class MDemo : public MThread
{
private:
    MsgQueue *fQueue;
    TRandom  fRand;

public:
    MDemo() : MThread(false) {}

    void SetQueue(MsgQueue *q) { fQueue = q; }

    bool Wait(int s, int m)
    {
        int i = 0;
        while (!HasStopFlag() && i++<m)
            usleep(s);
        if (HasStopFlag())
            return false;
        return true;
    }

    bool Move(float zd, float az, int s, int m)
    {
        ZdAz dest1(zd, az);
        fQueue->PostMsg(WM_POSITION1, &dest1, sizeof(dest1));
        return Wait(s, m); // 30s?
    }

    virtual void *Thread()
    {
        if (!Move(90, -65, 1000000, 27))
            return NULL;

        Int_t i=1;
        while (i-->0)
        {
            //Timer tm;
            //tm.Now();

            if (!Move(75, -80, 1000000, 13))
                break;
            if (!Move(60, -65, 1000000, 13))
                break;
            if (!Move(75, -50, 1000000, 13))
                break;
            if (!Move(90, -65, 1000000, 13))
                break;
        }
        if (!Move(60, -65, 1000000, 17))
            return NULL;
        if (!Move(90, -65, 1000000, 19))
            return NULL;
        if (!Move(90, -10, 1000000, 28))
            return NULL;

        cout << "Demo Thread: done." << endl;
        return NULL;
    }
};

MDemo demo;

void MGCosy::StartDemo()
{
    cout << "Start Demo." << endl;

    demo.SetQueue(fQueue);
    demo.Start();

    cout << "PostMsg (WM_Demo) returned." << endl;
}

void StopDemo()
{
    cout << "Stopping demo." << endl;
    demo.Stop();
}
#endif //HAS_DEMO

/*
void MGCosy::StartCalib()
{
    cout << "Start Calibration." << endl;

    XY xy = fCCalib->GetCoordinates();
    RaDec pos(xy.X()*360/24, xy.Y());

    fQueue->PostMsg(WM_CALIB, &pos, sizeof(pos));

    cout << "PostMsg (WM_Calib) returned." << endl;
}
*/
void MGCosy::StartTPoint()
{
    cout << "Start writing tpoint pair." << endl;

    XY xy = fCCalib->GetCoordinates();
    RaDec pos(xy.X()*360/24, xy.Y());

    //fQueue->PostMsg(WM_TPOINT, &pos, sizeof(pos));
    fQueue->Proc(WM_TPOINT, &pos);

    cout << "PostMsg (WM_TPoint) returned." << endl;
}
//
// ******************************************************************
//

Bool_t MGCosy::ProcessMessage(Long_t msg, Long_t mp1, Long_t mp2)
{
    //
    // This processing is serialized!
    //
    switch (GET_MSG(msg))
    {
    case kC_COMMAND:
        switch (GET_SUBMSG(msg))
        {
        case kCM_COMBOBOX:
            switch (mp1)
            {
            case kCB_PredefPos:
                {
                    MStar *pos = fPreDefList[mp2];
                    if (pos)
                        fCZdAz->SetCoordinates(ZdAz(pos->GetX(), pos->GetY()));
                    return kTRUE;
                }
            case kCB_StarList:
                {
                    MStar *pos = fStarList[mp2];
                    if (pos)
                        fCCalib->SetCoordinates(RaDec(pos->GetX(), pos->GetY()));
                    return kTRUE;
                }
            case kCB_CelestList:
                {
                    fCelestObj = mp2;
                    return kTRUE;
                }
            }
            return kTRUE;

        case kCM_TAB:
            //cout << "Tab: " << mp1 << endl;
            return kTRUE;

        case kCM_BUTTON:

            switch (mp1)
            {
                /*
            case kPB_POSITION:
                Start(0);
                return kTRUE;

            case kPB_TRACK:
                Start(1);
                return kTRUE;
             */
            case kPB_START:
                switch (fTab->GetCurrent())
                {
                case 0:
                    StartPos();
                    return kTRUE;
                case 1:
                    StartTrack(true);
                    return kTRUE;
                case 2:
                    StartCelest();
                    return kTRUE;
                case 3:
                    fCRaDec->SetCoordinates(fCCalib->GetCoordinates());
                    StartTrack(false);
                    return kTRUE;
                case 4:
                    fQueue->Proc(WM_TESTSE, (void*)1);
                    return kTRUE;
                case 5:
                    fQueue->Proc(WM_GEAR, (void*)1);
                    return kTRUE;
#ifdef HAS_DEMO
                case 6:
                    StartDemo();
                    return kTRUE;
#endif
                }
                return kTRUE;

            case kPB_STOP:
                cout << "Sending stop movement msg." << endl;
#ifdef HAS_DEMO
                StopDemo();
#endif
                fQueue->PostMsg(WM_STOP);
                if (fTab->GetCurrent()==4)
                    fQueue->Proc(WM_TESTSE);
                if (fTab->GetCurrent()==5)
                    fQueue->Proc(WM_GEAR);
                cout << "PostMsg (WM_Stop) returned." << endl;
                return kTRUE;

            case kPB_CALCALTAZ:
                {
                    XY xy = fCRaDec->GetCoordinates();
                    fQueue->Proc(WM_CALCALTAZ, &xy);
                }
                return kTRUE;

            case kPB_SavePreDef:
                {
                    ofstream fout("prepos.txt", ios::app);
                    XY za = fCZdAz->GetCoordinates();
                    fout << setprecision(7) << za.X() << " \t" << za.Y();
                    fout << " New Position" << endl;
                }
                return kTRUE;
            case kPB_SaveStar:
                {
                    ofstream fout("stars.txt", ios::app);
                    XY za = fCCalib->GetCoordinates();

                    UShort_t d, m, s;
                    Char_t sgn;
                    MAstro::Deg2Dms(za.X(), sgn, d, m, s);
                    if (sgn=='-') fout << sgn;
                    fout << d << " " << m << " " << s << " \t";
                    MAstro::Deg2Dms(za.Y(), sgn, d, m, s);
                    if (sgn=='-') fout << sgn;
                    fout << d << " " << m << " " << s << " \t";
                    fout << " New Star/Source" << endl;
                }
                return kTRUE;

            case kPB_RAp:
            case kPB_RAm:
            case kPB_DECp:
            case kPB_DECm:
                {
                    XY xy = fCRaDec->GetCoordinates();
                    switch (mp1)
                    {
                    case kPB_RAp:
                        xy.Set(xy.X()+1./60, xy.Y());
                        break;
                    case kPB_RAm:
                        xy.Set(xy.X()-1./60, xy.Y());
                        break;
                    case kPB_DECp:
                        xy.Set(xy.X(), xy.Y()+1./60);
                        break;
                    case kPB_DECm:
                        xy.Set(xy.X(), xy.Y()-1./60);
                        break;
                    }
                    fCRaDec->SetCoordinates(xy);
                    StartTrack(false);
                    /*
                     RaDec dest(xy.X()*15., xy.Y()); // xy.X()  [h]->[ø]
                     fQueue->PostMsg(WM_TRACK, &dest, sizeof(dest));
                     //fQueue->Proc(WM_NEWTRACK, &dest);
                     */
                    //cout << "New Ra/Dec: " << dest.Ra() << "h " << dest.Dec() << "\xb0" << endl;
                }
                return kTRUE;

            case kPB_ZDp:
            case kPB_ZDm:
            case kPB_AZp:
            case kPB_AZm:
                StartTrackPos(mp1);
                return kTRUE;

            case kPB_TPOINT:
                StartTPoint();
                return kTRUE;

            case kPB_ENDSWITCH:
                fQueue->Proc(WM_ENDSWITCH);
                return kTRUE;
                /*
            case kPB_CALIBSE:
                StartCalib();
                return kTRUE;
                */
            case kPB_LoadBending:
                fQueue->Proc(WM_LOADBENDING);
                return kTRUE;
            case kPB_ResetBending:
                fQueue->Proc(WM_RESETBENDING);
                return kTRUE;
                /*
            case kPB_HOME:
                fQueue->PostMsg(WM_HOME, 0, 0);
                return kTRUE;
                */
            case kPB_TRACKPOS:
                StartTrackPos();
                return kTRUE;
            case kPB_ARM:
                fQueue->PostMsg(WM_ARM);
                return kTRUE;
            case kPB_DISPLAY1:
            case kPB_DISPLAY2:
                fQueue->PostMsg(WM_DISPLAY);
                return kTRUE;

            default:
                return kTRUE;
            }
            return kTRUE;

        case kCM_MENU:

            switch (mp1)
            {
            case IDM_EXIT:
                cout << "IDM_EXIT: Posting WM_QUIT." << endl;
                fQueue->PostMsg(WM_QUIT);
                cout << "IDM_EXIT: WM_QUIT done." << endl;
                //cout << "Idm_Exit." << endl;
                //CloseWindow();
                return kTRUE;
            case IDM_ASPECT:
                {
                    Int_t w = GetWidth();
                    Int_t h = GetHeight();
                    if (w>940*h/660)
                        w = 940*h/660;
                    else
                        h = 660*w/940;
                    Resize(w, h);
                }
                return kTRUE;
            case kLogClear:
                fLog->Clear();
                return kTRUE;
            case kLogCopy:
                fLog->Copy();
                return kTRUE;
            case kLogSelect:
                fLog->SelectAll();
                return kTRUE;
            case kLogSearch:
                //virtual Bool_t Search(const char *string, Bool_t direction, Bool_t caseSensitive);
                return kTRUE;
            case kLogSave:
                cout << "Saving log... " << flush;
                if (fLog->GetText()->Save("cosylog.txt"))
                    cout << "done." << endl;
                else
                    cout << "failed!" << endl;
                return kTRUE;

            case kLogAppend:
                cout << "Appending log... " << flush;
                if (fLog->GetText()->Append("cosylog.txt"))
                    cout << "done." << endl;
                else
                    cout << "failed!" << endl;
                return kTRUE;
            }
            return kTRUE;
        }
    }

    //
    // Start a thread to process the message.
    //
    // fQueue->PostMsg(msg, mp1, mp2);
    return kTRUE;

}
