/* ======================================================================== *\
!
! *
! * 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 <mailto:tbretz@astro.uni-wuerzburg.de>
!
!   Copyright: MAGIC Software Development, 2000-2003
!
!
\* ======================================================================== */

#include "MGEvtDisplay.h"

#include <stdlib.h>               // atoi

#include <TGTab.h>                // TGTab
#include <TGMenu.h>               // TGPopupMenu
#include <TCanvas.h>              // TCanvas::Print
#include <TGLabel.h>              // TGLabel
#include <TGButton.h>             // TGPictureButton
#include <TGMsgBox.h>             // TGMsgBox
#include <TGTextEntry.h>          // TGTextEntry
#include <TGFileDialog.h>         // TGFileDialog
#include <TRootEmbeddedCanvas.h>  // TRootEmbeddedCanvas

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

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

#include "MHCamera.h"
#include "MParList.h"
#include "MTaskList.h"
#include "MEvtLoop.h"
#include "MHCamera.h"
#include "MGeomApply.h"
#include "MRawEvtData.h"
#include "MRawEvtHeader.h"
#include "MReadMarsFile.h"

#include "MMcEvt.hxx"

ClassImp(MGEvtDisplay);

enum MGCamDisplayCommand
{
    kEvtPrev,
    kEvtNext,
    kEvtNumber,

    kFileSaveAs,
    kFileSaveAsRoot,
    kFileSaveAsC,
    kFileSaveAsPS,
    kFileSaveAsEPS,
    kFileSaveAsGIF,
    kFilePrint,
    kClose
}; 

// --------------------------------------------------------------------------
//
//  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("MRead");
}

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

    //
    //  --- the top1 part of the window ---
    //
    TGHorizontalFrame *top2 = new TGHorizontalFrame(frame, 300, 100);
    fList->Add(top2);

    fEvtInfo = new TGLabel(top2, new TGString(""));
    fList->Add(fEvtInfo);
    top2->AddFrame(fEvtInfo, laystd);

    //
    // layout and add frames
    //
    TGLayoutHints *laytop1 = new TGLayoutHints(kLHintsTop);
    fList->Add(laytop1);
    frame->AddFrame(top1, laytop1);
    frame->AddFrame(top2, laytop1);
}

// --------------------------------------------------------------------------
//
//  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", kEvtPrev);
    prevevt->Associate(this);

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

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

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

    TGTextButton *nextevt = new TGTextButton (top2, "Next Event >>", kEvtNext);
    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 a tab with an embedded canvas for an camera display and return the
//  pointer to the canvas
//
TCanvas *MGEvtDisplay::AddTab(TString name)
{
    TGLayoutHints *laycanvas = new TGLayoutHints(kLHintsCenterX|kLHintsCenterY|kLHintsExpandX|kLHintsExpandY);
    fList->Add(laycanvas);

    // Add new tab
    TGCompositeFrame *f = fEvtDisplay->AddTab(name);

    // create root embedded canvas and add it to the tab
    TRootEmbeddedCanvas *ec = new TRootEmbeddedCanvas(name+"Display", f, f->GetWidth(), f->GetHeight());
    f->AddFrame(ec, laycanvas);
    fList->Add(ec);

    // set background and border mode of the canvas
    TCanvas &c = *ec->GetCanvas();
    c.SetBorderMode(0);

    // layout and map new tab
#if ROOT_VERSION_CODE < ROOT_VERSION(3,03,00)
    MapSubwindows();
    Layout();
#else
    Layout();
    MapSubwindows();
#endif

    // display new tab in the main frame
    gClient->ProcessEventsFor(fEvtDisplay);

    // return pointer to new canvas
    return &c;
}

// --------------------------------------------------------------------------
//
//  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
    //
    fEvtDisplay = new TGTab(frame, 300, 300);

    fCanvas=AddTab("Photons");

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

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

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

    but->Associate(this);

    fList->Add(but);

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

    frame->AddFrame(but, 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
//
void MGEvtDisplay::AddMenuBar()
{
    //
    // Add all GUI elements and update the event counter
    //
    TGLayoutHints *laymenubar  = new TGLayoutHints(kLHintsTop|kLHintsLeft|kLHintsExpandX, 2, 2, 2, 2);
    TGLayoutHints *laymenuitem = new TGLayoutHints(kLHintsTop|kLHintsLeft, 0, 4, 0, 0);
    TGLayoutHints *laylinesep  = new TGLayoutHints(kLHintsTop|kLHintsExpandX);

    fList->Add(laymenubar);
    fList->Add(laymenuitem);
    fList->Add(laylinesep);

    TGPopupMenu *filemenu = new TGPopupMenu(gClient->GetRoot());
    filemenu->AddEntry("Save &As...",           kFileSaveAs);
    filemenu->AddEntry("Save As display.p&s",   kFileSaveAsPS);
    filemenu->AddEntry("Save As display.&eps",  kFileSaveAsEPS);
    filemenu->AddEntry("Save As display.&gif",  kFileSaveAsGIF);
    filemenu->AddEntry("Save As display.&C",    kFileSaveAsC);
    filemenu->AddEntry("Save As display.&root", kFileSaveAsRoot);
    filemenu->AddSeparator();
    filemenu->AddEntry("&Print...", kFilePrint);
    filemenu->AddSeparator();
    filemenu->AddEntry("E&xit", kClose);
    filemenu->Associate(this);

    TGMenuBar *menubar = new TGMenuBar(this, 1, 1, kHorizontalFrame);
    menubar->AddPopup("&File", filemenu, laymenuitem);
    AddFrame(menubar, laymenubar);

    TGHorizontal3DLine *linesep = new TGHorizontal3DLine(this);
    AddFrame(linesep, laylinesep);

    fList->Add(filemenu);
    fList->Add(menubar);
    fList->Add(linesep);
}

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), fInitOk(kFALSE)
: TGMainFrame(p, w, h), fInitOk(kFALSE), fGeom(NULL)
{
    //
    //  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();

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

    MGeomApply *apl = new MGeomApply;
    tlist->AddToList(apl);

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

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

    AddMenuBar();
    AddFrames(fname, tname);

    SetWMSizeHints(450, 400, 1000, 1000, 10, 10);
    Move(rand()%100+50, rand()%100+50);
}

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

    delete fList;
}

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

void MGEvtDisplay::UpdateMcLabel()
{
    MMcEvt *evt=(MMcEvt*)GetParList()->FindObject("MMcEvt");
    if (!evt)
        return;

    TString txt = " ";

    switch (evt->GetPartId())
    {
    case kGAMMA:
        txt += "Gamma";
        break;
    case kPROTON:
        txt += "Proton";
        break;
    case kHELIUM:
        txt += "Helium";
        break;
    default:
        txt += "Unknown Particle Id#";
        txt += evt->GetPartId();
    }

    txt += ":  E=";
    txt += (int)(evt->GetEnergy()+.5);
    txt += "GeV  r=";
    txt += (int)(evt->GetImpact()/100+.5);
    txt += "m  ZA=";
    txt += (int)(evt->GetTheta()*180/TMath::Pi()+.5);
    txt += "°  ";
    txt += evt->GetPhotElfromShower();
    txt += "PhEl";

    const MRawEvtHeader *hed = (MRawEvtHeader*)GetParList()->FindObject("MRawEvtHeader");
    if (hed)
    {
        txt += "  DAQEvt #";
        txt += hed->GetDAQEvtNumber();
    }

    fEvtInfo->SetText(txt);

    gLog << all;
    gLog.Separator(txt);

    //
    // Seems to be necessary to newly layout the upper part to display
    // the whole line of text
    //
    TGFrame &f = *(TGFrame*)fEvtInfo->GetParent()->GetParent();
    f.Layout();
    f.MapSubwindows();
}

// --------------------------------------------------------------------------
//
//  Adds the geometry tab 
//
void MGEvtDisplay::AddGeometryTab()
{
    MGeomCam *geom = (MGeomCam*)GetParList()->FindObject("MGeomCam");
    if (!geom)
        return;

    fGeom=AddTab("Geometry");

    MHCamera *cam = new MHCamera(*geom);
    cam->SetBit(TH1::kNoStats);
    cam->Draw();
    cam->DrawPixelIndices();
    fList->Add(cam);

    fGeom->Modified();
    fGeom->Update();
}

// --------------------------------------------------------------------------
//
//  Checks if the event number is valid, and if so reads the new event
//  and updates the display
//
void MGEvtDisplay::ReadinEvent(Int_t dir)
{
    if (!fGeom)
        AddGeometryTab();

    MRawEvtData *raw = (MRawEvtData*)GetParList()->FindObject("MRawEvtData");
    if (!raw)
        return;

    const Int_t num = GetReader()->GetNumEntry();

    do
    {
        if (dir<0 && !GetReader()->DecEventNum())
        {
            GetReader()->SetEventNum(num);
            return;
        }
        if (dir>0 && !GetReader()->IncEventNum())
        {
            GetReader()->SetEventNum(num);
            return;
        }

        if (!GetTaskList()->Process())
            return;

        GetReader()->DecEventNum();

    } while (raw->GetNumPixels()<1 && dir!=0);

    UpdateMcLabel();
    UpdateDisplay();

    fTxtEvtNr->SetText(Form("%d", GetReader()->GetNumEntry()+1));
}

void MGEvtDisplay::ReadFirstEvent()
{
    fInitOk = fEvtLoop->PreProcess();

    if (fInitOk)
        ReadinEvent();

    TGString *txt = new TGString(Form("out of %d Events", GetReader()->GetEntries()));
    fNumOfEvts->SetText(txt);
}

// --------------------------------------------------------------------------
//
//  Opens a save as dialog, and tries to store the canvas
//  in the given output format
//
void MGEvtDisplay::SaveAsDialog() const
{
    static const char *gSaveAsTypes[] =
    {
        "PostScript",   "*.ps",
        "Encapsulated PostScript", "*.eps",
        "Gif files",    "*.gif",
        "Macro files",  "*.C",
        "ROOT files",   "*.root",
        "All files",    "*",
        NULL,           NULL
    };

    static TString dir(".");

    TGFileInfo fi; // fFileName and fIniDir deleted in ~TGFileInfo

    fi.fFileTypes = (const char**)gSaveAsTypes;
    fi.fIniDir    = StrDup(dir);

    new TGFileDialog(fClient->GetRoot(), this, kFDSave, &fi);

    if (!fi.fFilename)
        return;

    dir = fi.fIniDir;

    if (strstr(fi.fFilename, ".root") ||
        strstr(fi.fFilename, ".ps")   ||
        strstr(fi.fFilename, ".eps")  ||
        strstr(fi.fFilename, ".gif"))
    {
        fCanvas->SaveAs(fi.fFilename);
        return;
    }
    if (strstr(fi.fFilename, ".C"))
    {
        fCanvas->SaveSource(fi.fFilename);
        return;
    }
    Warning("SaveAsDialog", "Unknown Extension: %s", fi.fFilename);
}

// --------------------------------------------------------------------------
//
//  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_TEXTENTRY:
        switch(GET_SUBMSG(msg))
        {
        case kTE_ENTER:
            if (parm1==kClose)
            {
                CloseWindow();
                return kTRUE;
            }

            if (!fInitOk)
                return kTRUE;

            switch(GET_SUBMSG(msg))
            {
            case kTE_ENTER:
                if (GetReader()->SetEventNum(atoi(fTxtEvtNr->GetText())-1))
                    ReadinEvent();
                return kTRUE;
            }
            return kTRUE;
        }
        return kTRUE;

    case kC_COMMAND:
        switch (GET_SUBMSG(msg))
        {
        case kCM_BUTTON:
            if (parm1==kClose)
            {
                CloseWindow();
                return kTRUE;
            }

            if (!fInitOk)
                return kTRUE;

            switch (parm1)
            {
            case kEvtPrev:
                ReadinEvent(-1);
                return kTRUE;

            case kEvtNext:
                ReadinEvent(+1);
                return kTRUE;
            }
            return kTRUE;

        case kCM_MENU:
            switch (parm1)
            {
            case kFileSaveAs:
                SaveAsDialog();
                return kTRUE;
            case kFileSaveAsRoot:
                fCanvas->SaveAs("display.root");
                return kTRUE;
            case kFileSaveAsC:
                // FIXME: The line opening the canvas is wrong.
                fCanvas->SaveSource("display.C");
                return kTRUE;
            case kFileSaveAsPS:
                fCanvas->SaveAs("display.ps");
                return kTRUE;
            case kFileSaveAsEPS:
                fCanvas->SaveAs("display.eps");
                return kTRUE;
            case kFileSaveAsGIF:
                fCanvas->SaveAs("display.gif");
                return kTRUE;
            case kFilePrint:
                fCanvas->Print();
                return kTRUE;
            case kClose:
                CloseWindow();
                return kTRUE;
            }
            return kTRUE;
        }
        return kTRUE;
    }
    return kTRUE;
}

