#include "MGCosy.h"

#include <iostream.h>

#include "msgqueue.h"
#include "coord.h"

#include <TROOT.h>

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

//#include "timer.h"         // Timer

#include "MCosy.h"
#include "MGList.h"
#include "MGAccuracy.h"
#include "MGCoordinates.h"
#include "MGSkyPosition.h"
#include "MGVelocity.h"

#include "SlaStars.h"

#undef DEBUG

ClassImp(MGCosy);

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

enum
{
    kPB_POSITION,
    kPB_TRACK,
    kPB_STOP,
    kPB_HOME,
    kPB_CALCALTAZ,
    kPB_TPOINT,
    kPB_START,
    kPB_DISPLAY1,
    kPB_DISPLAY2,
    kPB_RAp,
    kPB_RAm,
    kPB_DECp,
    kPB_DECm,
    kPB_LoadBending,
    kPB_ResetBending,
    kPB_CALIBSE,
    kCB_PredefPos

};

void MGCosy::CreateMenu()
{
    fLayMenuBar  = new TGLayoutHints (kLHintsNormal | kLHintsExpandX);
    fLayMenuItem = new TGLayoutHints (kLHintsNormal, 0, 4, 0, 0);

    //
    //  crate the menu bar
    //
    TGPopupMenu *fFileMenu = new TGPopupMenu(gClient->GetRoot());
    fFileMenu->AddEntry("Set Aspect", IDM_ASPECT);
    fFileMenu->AddEntry("Exit", IDM_EXIT);
    fFileMenu->Associate(this);
    fList->Add(fFileMenu);

    //
    //  the button messages are handled by main frame (this)
    //
    TGMenuBar *fMenuBar = new TGMenuBar(this, 1, 1, kHorizontalFrame);
    fMenuBar->AddPopup("File", fFileMenu, fLayMenuItem);
    AddFrame(fMenuBar, fLayMenuBar);
    fList->Add(fMenuBar);

    //
    //  Seperator beyond menubar
    //

    TGHorizontal3DLine *fLineSep = new TGHorizontal3DLine(this);
    AddFrame(fLineSep, fLayMenuBar);
    fList->Add(fLineSep);
}

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

    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, "SE-Az:");
    l->Move(x-60, y+40);
    fList->Add(l);

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

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

    fLabel1 = new TGLabel*[3];
    fLabel1[0] = new TGLabel(f, "00000"); // Max: 16384
    fLabel1[1] = new TGLabel(f, "000");   // Max: 256
    fLabel1[2] = new TGLabel(f, "000");   // Max: 256
    fLabel1[0]->SetTextJustify(kTextRight);
    fLabel1[1]->SetTextJustify(kTextRight);
    fLabel1[2]->SetTextJustify(kTextRight);
    fLabel1[0]->Move(x,    y+40);
    fLabel1[1]->Move(x+43, y+40);
    fLabel1[2]->Move(x+70, y+40);
    fList->Add(fLabel1[0]);
    fList->Add(fLabel1[1]);
    fList->Add(fLabel1[2]);
    //f->AddFrame(fLabel1[0]);
    //f->AddFrame(fLabel1[1]);
    //f->AddFrame(fLabel1[2]);

    fLabel2 = new TGLabel*[3];
    fLabel2[0] = new TGLabel(f, "00000");
    fLabel2[1] = new TGLabel(f, "000");
    fLabel2[2] = new TGLabel(f, "000");
    fLabel2[0]->SetTextJustify(kTextRight);
    fLabel2[1]->SetTextJustify(kTextRight);
    fLabel2[2]->SetTextJustify(kTextRight);
    fLabel2[0]->Move(x,    y+57);
    fLabel2[1]->Move(x+43, y+57);
    fLabel2[2]->Move(x+70, y+57);
    fList->Add(fLabel2[0]);
    fList->Add(fLabel2[1]);
    fList->Add(fLabel2[2]);
    //f->AddFrame(fLabel2[0]);
    //f->AddFrame(fLabel2[1]);
    //f->AddFrame(fLabel2[2]);

    fLabel3 = new TGLabel*[3];
    fLabel3[0] = new TGLabel(f, "00000");
    fLabel3[1] = new TGLabel(f, "000");
    fLabel3[2] = new TGLabel(f, "000");
    fLabel3[0]->SetTextJustify(kTextRight);
    fLabel3[1]->SetTextJustify(kTextRight);
    fLabel3[2]->SetTextJustify(kTextRight);
    fLabel3[0]->Move(x,    y+74);
    fLabel3[1]->Move(x+43, y+74);
    fLabel3[2]->Move(x+70, y+74);
    fList->Add(fLabel3[0]);
    fList->Add(fLabel3[1]);
    fList->Add(fLabel3[2]);
    //f->AddFrame(fLabel3[0]);
    //f->AddFrame(fLabel3[1]);
    //f->AddFrame(fLabel3[2]);

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

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

    fOffsetZd = new TGLabel(f, "000000000");
    fOffsetAz = new TGLabel(f, "000000000");
    fOffsetZd->SetTextJustify(kTextRight);
    fOffsetAz->SetTextJustify(kTextRight);
    fOffsetZd->Move(x+15, y+102);
    fOffsetAz->Move(x+15, y+119);
    fList->Add(fOffsetZd);
    fList->Add(fOffsetAz);

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

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

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

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

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

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

    fRaEst  = new TGLabel(f, "+000h 00.0m");
    fDecEst = new TGLabel(f, "+000h 00.0m");
    fRaSoll = new TGLabel(f, "+000h 00.0m");
    fDecSoll = new TGLabel(f, "+000h 00.0m");
    fZdSoll = new TGLabel(f, "+000h 00.0m");
    fAzSoll = new TGLabel(f, "+000h 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+142);
    fRaSoll->Move(x+30, y+159);
    fDecEst->Move(x+30, y+182);
    fDecSoll->Move(x+30, y+199);
    fZdSoll->Move(x+30, y+222);
    fAzSoll->Move(x+30, y+239);
    fList->Add(fRaEst);
    fList->Add(fDecEst);
    fList->Add(fRaSoll);
    fList->Add(fDecSoll);
    fList->Add(fZdSoll);
    fList->Add(fAzSoll);

    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");
    fAvailMac1 = new TGLabel(f, "- MAC1 -");
    fAvailMac2 = new TGLabel(f, "- MAC2 -");
    fAvailMac3 = new TGLabel(f, "- MAC3 -");
    fAvailSe1  = new TGLabel(f, "-SE/Zd1-");
    fAvailSe2  = new TGLabel(f, "-SE/Zd2-");
    fAvailSe3  = new TGLabel(f, "- SE/Az -");

    ULong_t color;

    gClient->GetColorByName("Red", color);
    fError->SetBackgroundColor(color);
    fAvailMac1->SetBackgroundColor(color);
    fAvailMac2->SetBackgroundColor(color);
    fAvailMac3->SetBackgroundColor(color);
    fAvailSe1->SetBackgroundColor(color);
    fAvailSe2->SetBackgroundColor(color);
    fAvailSe3->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);

    fError   ->Move(10, 25);
    fMoving  ->Move(10, 25+20);
    fTracking->Move(10, 25+40);
    fStopping->Move(10, 25+60);
    fStopped ->Move(10, 25+80);
    fAvailMac1->Move(10, 25+120);
    fAvailMac2->Move(10, 25+140);
    fAvailMac3->Move(10, 25+160);
    fAvailSe1->Move(10, 25+180);
    fAvailSe2->Move(10, 25+200);
    fAvailSe3->Move(10, 25+220);

    fError   ->Resize(60, 20);
    fMoving  ->Resize(60, 20);
    fTracking->Resize(60, 20);
    fStopping->Resize(60, 20);
    fStopped ->Resize(60, 20);
    fAvailMac1->Resize(60, 20);
    fAvailMac2->Resize(60, 20);
    fAvailMac3->Resize(60, 20);
    fAvailSe1->Resize(60, 20);
    fAvailSe2->Resize(60, 20);
    fAvailSe3->Resize(60, 20);

    fList->Add(fError);
    fList->Add(fMoving);
    fList->Add(fTracking);
    fList->Add(fStopping);
    fList->Add(fStopped);
    fList->Add(fAvailMac1);
    fList->Add(fAvailMac2);
    fList->Add(fAvailMac3);
    fList->Add(fAvailSe1);
    fList->Add(fAvailSe2);
    fList->Add(fAvailSe3);
}

void MGCosy::CreatePredefinedPos(TGCompositeFrame *tf1)
{
    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.txt");
    if (!fin)
    {
        cout << "ERROR: Predifined posiion in 'prepos.txt' not found." << endl;
        return;
    }

    int i=0;
    while (!fin.eof())
    {
        TString str;
        Double_t zd, az;
        fin >> str >> zd >> az;
        box->AddEntry(str, i++);

        fStarList.Add(zd, az);
    }
}

void MGCosy::CreateTabs(TGTab *fTab)
{
    TGCompositeFrame *tf1 = fTab->AddTab("Position");
    TGCompositeFrame *tf2 = fTab->AddTab("Track");
    TGCompositeFrame *tf4 = fTab->AddTab("Calib");
    TGCompositeFrame *tf5 = fTab->AddTab("Test SE");
    TGCompositeFrame *tf6 = fTab->AddTab("Gear");
    /*TGCompositeFrame *tf3 =*/ fTab->AddTab("Demo");

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

    CreatePredefinedPos(tf1);

    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, which means that the");
    TGLabel *l4 = new TGLabel(tf1, "coordinates are corrected for the bending of");
    TGLabel *l5 = new TGLabel(tf1, "the telescope."); 
    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. 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(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);

    //
    // 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->Resize(50, 25);
    but->Move(25, 210);
    but->SetToolTipText("Right ascension -= 1'");
    but->Associate(this);
    fList->Add(but);
    but= new TGTextButton(tf2, "RA +",  kPB_RAp);
    but->Resize(50, 25);
    but->Move(90, 210);
    but->SetToolTipText("Right ascension += 1'");
    but->Associate(this);
    fList->Add(but);
    but= new TGTextButton(tf2, "DEC +",  kPB_DECp);
    but->Resize(50, 25);
    but->Move(55, 185);
    but->SetToolTipText("Declination += 1'");
    but->Associate(this);
    fList->Add(but);
    but= new TGTextButton(tf2, "DEC -",  kPB_DECm);
    but->Resize(50, 25);
    but->Move(55, 235);
    but->SetToolTipText("Declination -= 1'");
    but->Associate(this);
    fList->Add(but);

    but = new TGTextButton(tf1, "Home", kPB_HOME);
    but->Resize(80, 25);
    but->Move(160, 197);
    but->SetToolTipText("Initiate Home Drive.");
    but->Associate(this);
    fList->Add(but);

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

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

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

    but = new TGTextButton(tf4, "TPoint", kPB_TPOINT);
    but->Resize(80, 25);
    but->Move(25, 190);
    but->SetToolTipText("Trigger writing a tpoint coordinate pair.");
    but->Associate(this);
    fList->Add(but);

    but = new TGTextButton(tf4, "Calib SE", kPB_CALIBSE);
    but->Resize(80, 25);
    but->Move(160, 197);
    but->SetToolTipText("Set SE to given coordinates.");
    but->Associate(this);
    fList->Add(but);

    but = new TGTextButton(tf4, "Load", kPB_LoadBending);
    but->Resize(80, 25);
    but->Move(25, 220);
    but->SetToolTipText("Load bending corrections from file 'bending.txt'");
    but->Associate(this);
    fList->Add(but);

    but = new TGTextButton(tf4, "Reset", kPB_ResetBending);
    but->Resize(80, 25);
    but->Move(25, 250);
    but->SetToolTipText("Reset bending correction (coefficients=0)");
    but->Associate(this);
    fList->Add(but);

    ULong_t color;

    but = new TGTextButton(this, "Start", kPB_START);
    gClient->GetColorByName("Green", color);
    but->SetBackgroundColor(color);
    but->Move(147, 295);
    but->Resize(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->Move(212, 295);
    but->Resize(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, MsgQueue *q, const TGWindow *p, UInt_t w, UInt_t h)
    : TGMainFrame(p, w, h), fObservatory(key), fQueue(q)
{
    fList = new MGList;

#ifdef DEBUG
    cout << "MGCosy: Creating Menu" << endl;
#endif
    CreateMenu();

#ifdef DEBUG
    cout << "MGCosy: Creating TGCompositeFrame" << endl;
#endif
    TGHorizontalFrame *f1 = new TGHorizontalFrame(this, 0, 0);
    TGHorizontalFrame *f2 = new TGHorizontalFrame(this, 0, 0);
    fList->Add(f1);
    fList->Add(f2);

#ifdef DEBUG
    cout << "MGCosy: Creating TGTab" << endl;
#endif
    fTab = new TGTab(f1, 300, 300);
    fList->Add(fTab);

#ifdef DEBUG
    cout << "MGCosy: Creating MGSkyPosition" << endl;
#endif
    fSkyPosition = new MGSkyPosition(fObservatory, f1, 300);
#ifdef DEBUG
    cout << "MGCosy: Creating MGAccuracy" << endl;
#endif
    fAccuracy    = new MGAccuracy   (f1, 300);
#ifdef DEBUG
    cout << "MGCosy: Creating MGVelocity" << endl;
#endif
    fVelocity    = new MGVelocity   (f2, "Velocity [\"/min]", 300);
//    fOffset      = new MGVelocity   (f, "Offset se-re [']", 300);

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

#ifdef DEBUG
    cout << "MGCosy: Creating TGGroupFrame" << endl;
#endif
    TGGroupFrame *frame = new TGGroupFrame(f2, "Status");
    frame->Resize(300, 300);
    fList->Add(frame);

#ifdef DEBUG
    cout << "MGCosy: Creating TGListBox" << endl;
#endif
    fLog = new TGListBox(f2, -1, kSunkenFrame);  //kSunkenFrame|kDoubleBorder,
    fLog->Resize(300, 300);
    fLog->ChangeBackground(TGFrame::GetBlackPixel());
    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    | kLHintsTop   |kLHintsExpandX|kLHintsExpandY);
    TGLayoutHints *hints2a = new TGLayoutHints(kLHintsCenterY | kLHintsTop   |kLHintsExpandX|kLHintsExpandY, 6, 6);
    TGLayoutHints *hints3a = new TGLayoutHints(kLHintsRight   | kLHintsTop   |kLHintsExpandX|kLHintsExpandY);
    TGLayoutHints *hints1b = new TGLayoutHints(kLHintsLeft    | kLHintsBottom|kLHintsExpandX|kLHintsExpandY);
    TGLayoutHints *hints2b = new TGLayoutHints(kLHintsCenterY | kLHintsBottom|kLHintsExpandX|kLHintsExpandY, 6, 6);
    TGLayoutHints *hints3b = new TGLayoutHints(kLHintsRight   | kLHintsBottom|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(fVelocity,    hints2b);
    f2->AddFrame(fLog,         hints3b);

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

    //
    // FIXME!
    //
#ifdef DEBUG
    cout << "MGCosy: Creating Tabs" << endl;
#endif
    CreateTabs(fTab);
#ifdef DEBUG
    cout << "MGCosy: Creating Label" << endl;
#endif
    CreateLabel(frame);

    //
    //   Map the window, set up the layout, etc.
    //
#ifdef DEBUG
    cout << "MGCosy: Setting Size" << endl;
#endif
    const Int_t w = 940;
    const Int_t h = 660;
    SetWMSizeHints(w, h, 1280, 900, 10, 10);  // 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(w, h); //GetDefaultSize());
    MapWindow();
}

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

MGCosy::~MGCosy()
{
    cout << "MGCosy::~MGCosy called." << endl;

    delete fLayMenuBar;
    delete fLayMenuItem;

    cout << "Deleting MGCosy::fList" << endl;

    delete fList;

    cout << "MGCosy::~MGCosy done." << endl;
}

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];

    rd.Ra(rd.Ra()  * 24/2/TMath::Pi());
    rd.Dec(rd.Dec()*360/2/TMath::Pi());

    radec.Ra(radec.Ra()  * 24/2/TMath::Pi());
    radec.Dec(radec.Dec()*360/2/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));
        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));
        fDecEst->SetText(new TGString(text));
    }

    if (radec.Dec()>90|| radec.Dec()<-90)
    {
        radec.Ra(radec.Ra()+12);
        radec.Dec(180-radec.Dec());
    }
    radec.Ra(fmod((radec.Ra()+48), 24));

    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));
        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));
        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)
{
    soll *= 360/2/TMath::Pi();

    static Int_t zd=~0;
    static Int_t az=~0;

    char text[21];

    ZdAz test = soll*600;

    if (zd!=(int)test.Zd())
    {
        zd = (int)test.Zd();
        sprintf(text, "%c%dd %.1fm", soll.Zd()<0?'-':'+', abs((int)soll.Zd()), 0.1*(abs((int)test.Zd())%600));
        fZdSoll->SetText(new TGString(text));
    }
    if (az!=(int)test.Az())
    {
        az = (int)test.Az();
        sprintf(text, "%c%dd %.1fm", soll.Az()<0?'-':'+' , abs((int)soll.Az()), 0.1*(abs((int)test.Az())%600));
        fAzSoll->SetText(new TGString(text));
    }
}

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

    static char oldtxt[256];

    char text[256];

    strcpy(text, time.GetTimeStr());

    char *dot = strrchr(text, '.');

    if (dot)
        dot[2] = 0;

    if (strcmp(oldtxt, text))
    {
        fUtc->SetText(new TGString(text));
        strcpy(oldtxt, text);
    }

    static long oldmjd;
    double mjd = time.GetMjd();

    if (oldmjd != 1000000*mjd)
    {
        sprintf(text, "%12.6f", mjd);
        fMjd->SetText(new TGString(text));

        oldmjd = (long)(1000000*mjd);
    }

    return mjd;
}

void MGCosy::Update(ZdAz pos, ZdAz acc, ZdAz vel, ZdAz off, RaDec radec,
                    ZdAz soll, UInt_t stat, UInt_t stat2)
{
    double mjd = UpdateTime();
    fSkyPosition->Update(pos, mjd);
    fAccuracy->Update(pos, acc);
    fVelocity->Update(vel);
    UpdateOffset(off);
    UpdateRaDec(pos, mjd, radec);
    UpdateZdAz(soll);

#define kError     0x01
#define kMoving    0x02
#define kTracking  0x04
#define kStopping  0x08
#define kStopped   0x10

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

    SetLabelColor(fAvailMac1, stat2&0x01);
    SetLabelColor(fAvailMac2, stat2&0x02);
    SetLabelColor(fAvailMac3, stat2&0x04);
    SetLabelColor(fAvailSe1,  stat2&0x08);
    SetLabelColor(fAvailSe2,  stat2&0x10);
    SetLabelColor(fAvailSe3,  stat2&0x20);

    stat&kTracking ? fAccuracy->MapWindow() : fAccuracy->UnmapWindow();
    stat&kTracking ? fVelocity->MapWindow() : fVelocity->UnmapWindow();
    //    stat&kTracking ? fOffset->MapWindow()   : fOffset->UnmapWindow();

    if (fLog->TestBit(kHasChanged))
    {
        fLog->MapSubwindows();
        fLog->Layout();
        fLog->ResetBit(kHasChanged);
    }
}
// ======================================================================

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, 0, 0);
    cout << "Closing window - done." << endl;
    // gApplication->Terminate(0);
}

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

    XY xy = fCRaDec->GetCoordinates();
    RaDec dest(xy.X()*15., xy.Y()); // xy.X()  [h]->[ø]

    cout << dest.Ra() << "h " << dest.Dec() << "\xb0" << endl;

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

    cout << "PostMsg (WM_Track) 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 **********************
//
#include <TRandom.h>
class MDemo : public MThread
{
private:
    MsgQueue *fQueue;
    TRandom  fRand;

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

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

    virtual void *Thread()
    {
        while (1)
        {
            Timer tm;
            tm.Now();
            /*
            Float_t h = 2.+tm.H()+(10.7+tm.M())/60.;
            RaDec dest(h*15, 129.7);

            cout << dest.Ra()/15 << "h " << dest.Dec() << "°" << endl;

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

            int i = 0;
            while (!HasStopFlag() && i++<60)  // 2.5min
            usleep(1000000);
            if (HasStopFlag())
                break;
            */

            //fQueue->PostMsg(WM_STOP, 0, 0);

            ZdAz dest1((float)fRand.Integer(120)-60., 0);//fRand.Integer(25)+90);

            cout << "Demo: Zd=" << dest1.Zd() << "° Az=" << dest1.Az() << "°" << endl;

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

            int i = 0;
            while (!HasStopFlag() && i++<5/*5*/)  // 30s
                usleep(1000000);
            if (HasStopFlag())
                break;

            //ZdAz dest2(5, 30);
            //fQueue->PostMsg(WM_POSITION, &dest2, sizeof(dest2));
            /*
            i = 0;
            while (!HasStopFlag() && i++<30)  // 30s
                usleep(1000000);
            */
            //if (HasStopFlag())
            //    break;
        }
        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();
}

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:
            if (mp1==kCB_PredefPos)
            {
                MStar *pos = fStarList[mp2];
                if (!pos)
                    return kTRUE;
                fCZdAz->SetCoordinates(ZdAz(pos->GetX(), pos->GetY()));
            }
            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();
                    return kTRUE;
                case 2:
                    fCRaDec->SetCoordinates(fCCalib->GetCoordinates());
                    return kTRUE;
                case 3:
                    fQueue->Proc(WM_TESTSE, (void*)1);
                    return kTRUE;
                case 4:
                    fQueue->Proc(WM_GEAR, (void*)1);
                    return kTRUE;
                case 5:
                    StartDemo();
                    return kTRUE;
                }
                return kTRUE;

            case kPB_STOP:
                cout << "Sending stop movement msg." << endl;
                StopDemo();
                fQueue->PostMsg(WM_STOP, 0, 0);
                if (fTab->GetCurrent()==3)
                    fQueue->Proc(WM_TESTSE, NULL);
                if (fTab->GetCurrent()==4)
                    fQueue->Proc(WM_GEAR, NULL);
                cout << "PostMsg (WM_Stop) returned." << endl;
                return kTRUE;

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

            case kPB_RAp:
            case kPB_RAm:
            case kPB_DECp:
            case kPB_DECm:
                {
                    XY xy = fCRaDec->GetCoordinates();
                    switch (mp1)
                    {
                    case kPB_RAp:
                        xy.X(xy.X()+1./60);
                        break;
                    case kPB_RAm:
                        xy.X(xy.X()-1./60);
                        break;
                    case kPB_DECp:
                        xy.Y(xy.Y()+1./60);
                        break;
                    case kPB_DECm:
                        xy.Y(xy.Y()-1./60);
                        break;
                    }
                    RaDec dest(xy.X()*15., xy.Y()); // xy.X()  [h]->[ø]
                    fQueue->PostMsg(WM_TRACK, &dest, sizeof(dest));
                    //fQueue->Proc(WM_NEWTRACK, &dest);
                    fCRaDec->SetCoordinates(xy);

                    cout << "New Ra/Dec: " << dest.Ra() << "h " << dest.Dec() << "\xb0" << endl;
                }
                return kTRUE;

            case kPB_TPOINT:
                StartTPoint();
                return kTRUE;
            case kPB_CALIBSE:
                StartCalib();
                return kTRUE;
            case kPB_LoadBending:
                fQueue->Proc(WM_LOADBENDING, NULL);
                return kTRUE;
            case kPB_ResetBending:
                fQueue->Proc(WM_RESETBENDING, NULL);
                return kTRUE;
            case kPB_HOME:
                fQueue->PostMsg(WM_HOME, 0, 0);
                return kTRUE;
            case kPB_DISPLAY1:
            case kPB_DISPLAY2:
                fQueue->PostMsg(WM_DISPLAY, 0, 0);
                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, 0, 0);
                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;
            }
            return kTRUE;
        }
    }

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

}
