#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 <TG3DLine.h>      // TGHorizontal3DLine (TGSplitter)
#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"

ClassImp(MGCosy);

#define IDM_EXIT 1
#define IDM_TEXT 2

#define kPB_POSITION  0x1001
#define kPB_TRACK     0x1002
#define kPB_STOP      0x1003
#define kPB_CALCALTAZ 0x1004
#define kPB_POLARIS   0x1005
#define kPB_START     0x1006

#define kEF_A         0x1010
#define kEF_B         0x1011

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

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

    l = new TGLabel(f, "SE-Zd2:");
    l->Move(x-60, y+40);
    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);
    fLabel1[1]->Move(x+43, y);
    fLabel1[2]->Move(x+70, y);
    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+20);
    fLabel2[1]->Move(x+43, y+20);
    fLabel2[2]->Move(x+70, y+20);
    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+40);
    fLabel3[1]->Move(x+43, y+40);
    fLabel3[2]->Move(x+70, y+40);
    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+80);
    fList->Add(l);

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

    fOffsetZd = new TGLabel(f, "0000000");
    fOffsetAz = new TGLabel(f, "0000000");
    fOffsetZd->SetTextJustify(kTextRight);
    fOffsetAz->SetTextJustify(kTextRight);
    fOffsetZd->Move(x, y+80);
    fOffsetAz->Move(x, y+100);
    fList->Add(fOffsetZd);
    fList->Add(fOffsetAz);


    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");


    ULong_t color;

    gClient->GetColorByName("Red", color);
    fError->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);

    fError   ->Resize(60, 20);
    fMoving  ->Resize(60, 20);
    fTracking->Resize(60, 20);
    fStopping->Resize(60, 20);
    fStopped ->Resize(60, 20);

    fList->Add(fError);
    fList->Add(fMoving);
    fList->Add(fTracking);
    fList->Add(fStopping);
    fList->Add(fStopped);
}

void MGCosy::CreateTabs(TGTab *fTab)
{
    TGCompositeFrame *tf1 = fTab->AddTab("Position Zd/Az");
    TGCompositeFrame *tf2 = fTab->AddTab("Track Ra/Dec");
    TGCompositeFrame *tf3 = fTab->AddTab("Demo Mode");

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

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


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

    //
    // 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(this, "Calc Zd/Az",  kPB_CALCALTAZ);
    but->Resize(80, 25);
    but->Move(37, 257);
    but->SetToolTipText("Calculate Zd/Az corresponding to Ra/Dec.");
    fList->Add(but);

    but = new TGTextButton(this, "Set Polaris", kPB_POLARIS);
    but->Resize(80, 25);
    but->Move(37, 288);
    but->SetToolTipText("Set the actual position as the position of Polaris.");
    fList->Add(but);

    ULong_t color;

    but = new TGTextButton(this, "Start", kPB_START);
    gClient->GetColorByName("Green", color);
    but->SetBackgroundColor(color);
    but->Move(147, 275);
    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, 275);
    but->Resize(62, 25);
    but->SetToolTipText("Stop any movement of telescope.");
    fList->Add(but);

}

MGCosy::MGCosy(MsgQueue *q, const TGWindow *p, UInt_t w, UInt_t h)
    : TGMainFrame(p, w, h), fQueue(q)
{
    fList = new MGList;

    cout << "MGCosy: Creating Menu" << endl;
    CreateMenu();

    cout << "MGCosy: Creating TGCompositeFrame" << endl;
    TGCompositeFrame *f = new TGCompositeFrame(this, 0, 0);
    f->SetLayoutManager(new TGMatrixLayout(f, 3, 2, 10));
    fList->Add(f);

    cout << "MGCosy: Creating TGTab" << endl;
    fTab = new TGTab(f, 300, 300);
    fList->Add(fTab);

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

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

    cout << "MGCosy: Creating TGGroupFrame" << endl;
    TGGroupFrame *frame = new TGGroupFrame(f, "Status");
    frame->Resize(300, 300);
    fList->Add(frame);

    cout << "MGCosy: Creating TGListBox" << endl;
    fLog = new TGListBox(f, -1, kSunkenFrame);  //kSunkenFrame|kDoubleBorder,
    fLog->Resize(300, 300);
    fLog->ChangeBackground(TGFrame::GetBlackPixel());
    fList->Add(fLog);

    //
    // Layout the window, tile by tile...
    //
    f->AddFrame(fTab);
    f->AddFrame(fSkyPosition);
    f->AddFrame(fAccuracy);
    f->AddFrame(frame);
    f->AddFrame(fVelocity);
    f->AddFrame(fLog);

    AddFrame(f, new TGLayoutHints(kLHintsNormal | kLHintsExpandX, 0, 0, 1, 0));

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

    //
    //   Map the window, set up the layout, etc.
    //
    cout << "MGCosy: Setting Size" << endl;
    const Int_t w = 940;
    const Int_t h = 660;
    SetWMSizeHints(w, h, w, h, 10, 10);  // set the smallest and biggest size of the Main frame

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

    cout << "MGCosy: Map Window" << endl;
    MapSubwindows();
    Resize(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::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::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, "%d", zd);
        fOffsetZd->SetText(new TGString(text));
    }
    if (az!=(Int_t)off.Az())
    {
        az = (Int_t)off.Az();
        sprintf(text, "%d", az);
        fOffsetAz->SetText(new TGString(text));
    }
}

void MGCosy::Update(ZdAz pos, ZdAz acc, ZdAz vel, ZdAz off, UInt_t stat)
{
    fSkyPosition->Update(pos);
    fAccuracy->Update(pos, acc);
    fVelocity->Update(vel);
    UpdateOffset(off);

#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);

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

    if (!fLog->TestBit(kHasChanged))
        return;

    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()+(8.+tm.M())/60.;
            RaDec dest(h*15, 130);

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

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

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

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

            ZdAz dest1(fRand.Integer(56)+5, fRand.Integer(360));

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

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

            i = 0;
            while (!HasStopFlag() && i++<30)  // 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();
}
//
// ******************************************************************
//

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_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:
                    StartDemo();
                    return kTRUE;
                }
                return kTRUE;

            case kPB_STOP:
                cout << "Sending stop movement msg." << endl;
                StopDemo();
                fQueue->PostMsg(WM_STOP, 0, 0);

                cout << "PostMsg (WM_Stop) returned." << endl;
                return kTRUE;

            case kPB_CALCALTAZ:
                {
                    SlaStars sla;
                    sla.SetMjd2Now();

                    XY xy = fCRaDec->GetCoordinates();
                    RaDec rd(xy.X()*15., xy.Y());

                    cout << "Ra/Dec: " << rd.Ra() << kDEG << " " << rd.Dec() << kDEG << endl;
                    ZdAz aa=sla.CalcZdAz(rd*kDeg2Rad)*kRad2Deg;
                    cout << "Zd/Az: " << aa.Zd() << kDEG << " " << aa.Az() << kDEG << endl;
                }
                return kTRUE;

            case kPB_POLARIS:
                fQueue->PostMsg(WM_POLARIS, 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;
            }
            return kTRUE;
        }
    }

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

}
