/* ======================================================================== *\
!
! *
! * 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@uni-sw.gwdg.de>
!
!   Copyright: MAGIC Software Development, 2000-2002
!
!
\* ======================================================================== */

#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 "MParList.h"
#include "MTaskList.h"
#include "MEvtLoop.h"
#include "MCamDisplay.h"
#include "MReadMarsFile.h"
#include "MGeomCamMagic.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("MReadMarsFile");
}

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

    TGLayoutHints *laycanvas = new TGLayoutHints(kLHintsCenterX|kLHintsCenterY|kLHintsExpandX|kLHintsExpandY);

    TGCompositeFrame *tab2 = tabdisp->AddTab("Event Display");
    TRootEmbeddedCanvas *canvas = new TRootEmbeddedCanvas("EventDisplay", tab2, 400, 400);
    tab2->AddFrame(canvas, laycanvas);
    fList->Add(canvas);

    fCanvas = canvas->GetCanvas();

    TGCompositeFrame *tab3 = tabdisp->AddTab("Geometry");
    canvas = new TRootEmbeddedCanvas("CamDisplay", tab3, 400, 400);
    tab3->AddFrame(canvas, laycanvas);
    fList->Add(canvas);

    MGeomCamMagic geom;
    MCamDisplay *display = new MCamDisplay(&geom);
    display->Draw();
    display->DrawPixelNumbers();
    fList->Add(display);

    //
    // 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(laycanvas);
    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)
{
    //
    //  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);

    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 += ":  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 += "°     ";

    fEvtInfo->SetText(txt);
}

// --------------------------------------------------------------------------
//
//  Checks if the event number is valid, and if so reads the new event
//  and updates the display
//
void MGEvtDisplay::ReadinEvent()
{
    if (GetTaskList()->Process())
    {
        GetReader()->DecEventNum();
        UpdateDisplay();
        UpdateMcLabel();
    }

    fTxtEvtNr->SetText(Form("%d", GetReader()->GetEventNum()+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)
{
    if (GET_MSG(msg)!=kC_TEXTENTRY && GET_MSG(msg)!=kC_COMMAND)
        return kTRUE;

    switch(GET_SUBMSG(msg))
    {
    case kTE_ENTER:
    case kCM_BUTTON:
        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;

        case kCM_BUTTON:
            switch (parm1)
            {
            case kEvtPrev:
                if (GetReader()->DecEventNum())
                    ReadinEvent();
                return kTRUE;

            case kEvtNext:
                if (GetReader()->IncEventNum())
                    ReadinEvent();
                return kTRUE;
            }
            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;
}
