/* ======================================================================== *\
!
! *
! * This file is part of MARS, the MAGIC Analysis and Reconstruction
! * Software. It is distributed to you in the hope that it can be a useful
! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
! * It is distributed WITHOUT ANY WARRANTY.
! *
! * Permission to use, copy, modify and distribute this software and its
! * documentation for any purpose is hereby granted without fee,
! * provided that the above copyright notice appear in all copies and
! * that both that copyright notice and this permission notice appear
! * in supporting documentation. It is provided "as is" without express
! * or implied warranty.
! *
!
!
!   Author(s): Thomas Bretz  10/2001 (tbretz@uni-sw.gwdg.de)
!
!   Copyright: MAGIC Software Development, 2000-2001
!
!
\* ======================================================================== */

#include "MGEvtDisplay.h"

#include <stdlib.h>               // atoi

#include <TGTab.h>                // TGTab
#include <TGLabel.h>              // TGLabel
#include <TGButton.h>             // TGPictureButton
#include <TGMsgBox.h>             // TGMsgBox
#include <TGTextEntry.h>          // TGTextEntry
#include <TRootEmbeddedCanvas.h>  // TRootEmbeddedCanvas

#include <TG3DLine.h>             // TGHorizontal3DLine
                                  // use TGSplitter instead for root<3.00

#include "MParList.h"
#include "MTaskList.h"
#include "MEvtLoop.h"
#include "MReadTree.h"

ClassImp(MGEvtDisplay);

enum MGCamDisplayCommand
{
    M_PREVEVT,
    M_NEXTEVT,
    M_EVTNUMBER,

    M_PRINT,
    M_CLOSE
}; 

// --------------------------------------------------------------------------
//
//  Return a pointer to the parameter list.
//
MParList *MGEvtDisplay::GetParList() const
{
    return fEvtLoop->GetParList();
}

// --------------------------------------------------------------------------
//
//  Return a pointer to the task list.
//
MTaskList *MGEvtDisplay::GetTaskList() const
{
    return (MTaskList*)GetParList()->FindObject("MTaskList");
}

// --------------------------------------------------------------------------
//
//  Return a pointer to the reader task (MReadTree)
//
MReadTree *MGEvtDisplay::GetReader() const
{
    return (MReadTree*)GetTaskList()->FindObject("MReadTree");
}

// --------------------------------------------------------------------------
//
//  Add the top part of the frame: This is filename and treename display
//
void MGEvtDisplay::AddTopFramePart1(TGVerticalFrame *frame,
                                    const char *filename,
                                    const char *treename)
{
    //
    //  --- the top1 part of the window ---
    //
    TGHorizontalFrame *top1 = new TGHorizontalFrame(frame, 300, 100);
    fList->Add(top1);

    //
    // create gui elements
    //
    TGLabel *lfile = new TGLabel(top1, new TGString("File:"));
    TGLabel *file  = new TGLabel(top1, new TGString(filename));
    TGLabel *ltree = new TGLabel(top1, new TGString("Tree:"));
    TGLabel *tree  = new TGLabel(top1, new TGString(treename));

    fList->Add(lfile);
    fList->Add(file);
    fList->Add(ltree);
    fList->Add(tree);

    //
    // layout and add gui elements in/to frame
    //
    TGLayoutHints *laystd = new TGLayoutHints(kLHintsLeft, 10, 10, 10, 10);
    fList->Add(laystd);

    top1->AddFrame(lfile, laystd);
    top1->AddFrame(file,  laystd);
    top1->AddFrame(ltree, laystd);
    top1->AddFrame(tree,  laystd);

    //
    // layout and add frame
    //
    TGLayoutHints *laytop1 = new TGLayoutHints(kLHintsTop);
    fList->Add(laytop1);

    frame->AddFrame(top1, laytop1);
}

// --------------------------------------------------------------------------
//
//  Show the correct number of events
//
void MGEvtDisplay::UpdateNumOfEvts()
{
    char txt[100];
    sprintf(txt, "out of %d Events", GetReader()->GetEntries());

    fNumOfEvts->SetText(new TGString(txt));
}

// --------------------------------------------------------------------------
//
//  Add the second part of the top frame: This are the event number controls
//
void MGEvtDisplay::AddTopFramePart2(TGVerticalFrame *frame)
{
    //
    // --- the top2 part of the window ---
    //
    TGHorizontalFrame *top2 = new TGHorizontalFrame(frame, 300, 100);
    fList->Add(top2);

    //
    // Create the gui elements
    //
    TGTextButton *prevevt = new TGTextButton(top2, "<< Previous Event", M_PREVEVT);
    prevevt->Associate(this);

    TGLabel *evtnr = new TGLabel(top2, new TGString("Event: "));

    fTxtEvtNr = new TGTextEntry(top2, new TGTextBuffer(100), M_EVTNUMBER);
    fTxtEvtNr->Resize(60, fTxtEvtNr->GetDefaultHeight());
    fTxtEvtNr->Associate(this);

    fNumOfEvts = new TGLabel(top2, "out of           Events.");

    TGTextButton *nextevt = new TGTextButton (top2, "Next Event >>", M_NEXTEVT);
    nextevt->Associate(this);

    //
    // Add gui elements to 'atotodel'
    //
    fList->Add(prevevt);
    fList->Add(evtnr);
    fList->Add(fTxtEvtNr);
    fList->Add(fNumOfEvts);
    fList->Add(nextevt);

    //
    // add the gui elements to the frame
    //
    TGLayoutHints *laystd    = new TGLayoutHints(kLHintsLeft|kLHintsCenterY, 10, 10, 10, 10);

    fList->Add(laystd);

    top2->AddFrame(prevevt,    laystd);
    top2->AddFrame(evtnr,      laystd);
    top2->AddFrame(fTxtEvtNr,  laystd);
    top2->AddFrame(fNumOfEvts, laystd);
    top2->AddFrame(nextevt,    laystd);

    frame->AddFrame(top2, new TGLayoutHints(kLHintsCenterX));
}

// --------------------------------------------------------------------------
//
//  Add the mid frame: This are the two tabs with the canvas in the right one
//
void MGEvtDisplay::AddMidFrame(TGHorizontalFrame *frame)
{
    //
    // create tab control
    //
    TGTab *tabs = new TGTab(frame, 300, 300);

    //
    // Create Tab1
    //
    fTab1 = tabs->AddTab("Setup");

    //
    // Crete second gui elemet for tab1 (TGVertical Frame)
    //
    TGLayoutHints *laytabs = new TGLayoutHints(kLHintsNormal|kLHintsExpandY, 10, 10, 10, 10);
    frame->AddFrame(tabs, laytabs);

    //
    // Create second part of frame
    // 
    TGTab *tabdisp = new TGTab(frame, 300, 300);

    TGCompositeFrame *tab2 = tabdisp->AddTab("Event Display");

    TRootEmbeddedCanvas *canvas = new TRootEmbeddedCanvas("EventDisplay", tab2, 400, 400);

    TGLayoutHints *laycanvas = new TGLayoutHints(kLHintsCenterX|kLHintsCenterY|kLHintsExpandX|kLHintsExpandY);
    tab2->AddFrame(canvas, laycanvas);

    fCanvas = canvas->GetCanvas();

    //
    // Add second part to frame
    //
    TGLayoutHints *laydisp = new TGLayoutHints(kLHintsNormal|kLHintsExpandY|kLHintsExpandX, 10, 10, 10, 10);
    frame->AddFrame(tabdisp, laydisp);

    //
    // Now add all gui elements to 'autodel'-list
    //
    fList->Add(tabdisp);
    fList->Add(canvas);
    fList->Add(laycanvas);
    fList->Add(laydisp);
    fList->Add(laytabs);
}

// --------------------------------------------------------------------------
//
//  Add the low frame: These are the buttons Print and Close
//
void MGEvtDisplay::AddLowFrame(TGHorizontalFrame *frame)
{
    TGTextButton *but1 = new TGTextButton(frame, "Print", M_PRINT);
    TGTextButton *but2 = new TGTextButton(frame, "Close", M_CLOSE);

    but1->Associate(this);
    but2->Associate(this);

    fList->Add(but1);
    fList->Add(but2);

    TGLayoutHints *laybut = new TGLayoutHints(kLHintsLeft, 10, 10, 10, 10);
    fList->Add(laybut);

    frame->AddFrame(but1, laybut);
    frame->AddFrame(but2, laybut);
}

// --------------------------------------------------------------------------
//
//  Create and setup all the three frames and build the window interieur
//
void MGEvtDisplay::AddFrames(const char *filename, const char *treename)
{
    //
    // Create the frame elements and add gui elements to it
    //
    TGVerticalFrame *frametop = new TGVerticalFrame(this, 300, 100);
    fList->Add(frametop);

    AddTopFramePart1(frametop, filename, treename);
    AddTopFramePart2(frametop);

    TGLayoutHints *laytop  = new TGLayoutHints(kLHintsTop|kLHintsCenterX);
    fList->Add(laytop);

    // -----

    TGHorizontalFrame *framemid = new TGHorizontalFrame(this, 300, 100);
    fList->Add(framemid);

    AddMidFrame(framemid);

    TGLayoutHints *laymid  = new TGLayoutHints(kLHintsExpandY|kLHintsExpandX);
    fList->Add(laymid);

    //
    // add frame elements to 'autodel'
    //
    TGHorizontal3DLine *line1 = new TGHorizontal3DLine(this);
    TGHorizontal3DLine *line2   = new TGHorizontal3DLine(this);
    fList->Add(line1);
    fList->Add(line2);

    TGLayoutHints *layline = new TGLayoutHints(kLHintsTop|kLHintsExpandX);
    fList->Add(layline);

    // -----
    TGHorizontalFrame *framelow = new TGHorizontalFrame(this, 300, 100);
    fList->Add(framelow);

    AddLowFrame(framelow);

    TGLayoutHints *laylow  = new TGLayoutHints(kLHintsTop);
    fList->Add(laylow);

    //
    // Layout frame elements and add elements to frame
    //
    AddFrame(frametop, laytop);
    AddFrame(line1,    layline);
    AddFrame(framemid, laymid);
    AddFrame(line2,    layline);
    AddFrame(framelow, laylow);
}

// --------------------------------------------------------------------------
//
//  Constructor
//
MGEvtDisplay::MGEvtDisplay(const char *fname, const char *tname,
                           const TGWindow *p, const TGWindow *main,
                           UInt_t w, UInt_t h)
    : TGTransientFrame(p, main, w, h)
{
    //
    //  create an autodelete-list for the gui elements
    //
    fList = new TList;
    fList->SetOwner();

    //
    // Setup an empty job, with a reader task only.
    // All tasks and parameter containers are deleted automatically
    // (via SetOwner())
    //

    MTaskList *tlist = new MTaskList;
    tlist->SetOwner();

    MReadTree *read = new MReadTree(tname, fname);
    tlist->AddToList(read);

    MParList *plist = new MParList();
    plist->SetOwner();
    plist->AddToList(tlist);

    fEvtLoop = new MEvtLoop;
    fEvtLoop->SetOwner();
    fEvtLoop->SetParList(plist);

    //
    // Add all GUI elements and update the event counter
    // 
    AddFrames(fname, tname);
    UpdateEventCounter();
}


// --------------------------------------------------------------------------
//
//  Destructs the graphical members and the eventloop members
//
MGEvtDisplay::~MGEvtDisplay()
{
    delete fList;

    fEvtLoop->PostProcess();
    delete fEvtLoop;
}

// --------------------------------------------------------------------------
//
// The close message is generated by the window manager when its close
// window menu item is selected.
//
void MGEvtDisplay::CloseWindow()
{
    delete this;
}

// --------------------------------------------------------------------------
//
//  Checks if the event number is valid, and if so reads the new event
//  and updates the display
//
void MGEvtDisplay::ReadinEvent(UInt_t iEvt)
{
    Int_t buttons = 4;
    Int_t retval  = 0;

    //  first check if the new event is in the range of possible events
  
    if (iEvt >= GetReader()->GetEntries())
    {
        new TGMsgBox(gClient->GetRoot(), this,
                     "WARNING!",
                     "The event number is out of range!!!",
                     kMBIconExclamation, buttons, &retval);
    }
    else
    {
        GetReader()->SetEventNum(iEvt);

        if (GetTaskList()->Process())
            UpdateDisplay();
    }

    UpdateEventCounter();
}

// --------------------------------------------------------------------------
//
//  Update the event counter
//
void MGEvtDisplay::UpdateEventCounter()
{
    char txt[256];

    sprintf(txt, "%d", GetReader()->GetEventNum());

    fTxtEvtNr->SetText(txt);
}

// --------------------------------------------------------------------------
//
//    ProcessMessage(Long_t msg, Long_t parm1, Long_t parm2)
//
// Processes information from all GUI items.
// Selecting an item usually generates an event with 4 parameters.
// The first two are packed into msg (first and second bytes).
// The other two are parm1 and parm2.
//
Bool_t MGEvtDisplay::ProcessMessage(Long_t msg, Long_t parm1, Long_t parm2)
{
    switch(GET_MSG(msg))
    {
    case kC_COMMAND:
        switch(GET_SUBMSG(msg))
        {
        case kCM_BUTTON:
            switch (parm1)
            {
            case M_PREVEVT:
                //
                // '-2' is because MReadTree::Process increases the
                // Event number by one - this is the natural behaviour
                // after reading one event
                //
                ReadinEvent(GetReader()->GetEventNum()-2);
                return kTRUE;

            case M_NEXTEVT:
                //
                // '+0' is because MReadTree::Process increases the
                // Event number by one - this is the natural behaviour
                // after reading one event
                //
                ReadinEvent(GetReader()->GetEventNum());
                return kTRUE;

            case M_CLOSE:
                CloseWindow();
                return kTRUE;
            }
            return kTRUE;
        }
        return kTRUE;

    case kC_TEXTENTRY:
        if (GET_SUBMSG(msg) == kTE_ENTER)
            ReadinEvent(atoi(fTxtEvtNr->GetText())-1);
        return kTRUE;
    }

    return kTRUE;
}
