/* ======================================================================== *\
!
! *
! * 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 "MGCamDisplay.h"

#include <TList.h>               // TList::Add
#include <TStyle.h>              // gStyle->SetOptStat
#include <TCanvas.h>             // TCanvas::cd
#include <TGButton.h>            // TGPictureButton
#include <TGButtonGroup.h>       // TGVButtonGroup

#include "MGTask.h"              // MGTask::CreateGui
#include "MClone.h"              // MClone
#include "MHillas.h"             // MHillas
#include "MParList.h"            // MParList::AddToList
#include "MEvtLoop.h"            // MEvtLoop::GetParList
#include "MTaskList.h"           // MTaskList::AddToList
#include "MHCamera.h"            // MHCamera
#include "MHillasCalc.h"         // MHillasCalc
#include "MHillasSrcCalc.h"      // MHillasSrcCalc
#include "MPedestalPix.h"        // MPedestalPix
#include "MPedestalCam.h"        // MPedestalCam
#include "MCerPhotEvt.h"         // MCerPhotEvt::GetPixById
#include "MCerPhotCalc.h"        // MCerPhotCalc
#include "MMcPedestalCopy.h"     // MMcPedestalCopy
#include "MMcPedestalNSBAdd.h"   // MMcPedestalNSBAdd
#include "MBlindPixelCalc.h"     // MBlindPixelCalc
#include "MImgCleanStd.h"        // MImgCleanStd
#include "MRawEvtData.h"         // MRawEvtData

ClassImp(MGCamDisplay);

enum
{
    kRButRawEvt,
    kRButCleanedEvt,
    kCButHillas
};

// --------------------------------------------------------------------------
//
//  Add Setup elements to GUI.
//
void MGCamDisplay::AddSetupElements()
{
    //
    // Create gui elements for vertical frame
    //
    TGGroupFrame *grp = new TGGroupFrame(fTab1, "Display");
    TGVButtonGroup *group = new TGVButtonGroup(grp);
    fList->Add(group);

    TGRadioButton *but1 = new TGRadioButton(group, "Raw Events",      kRButRawEvt);
    TGRadioButton *but2 = new TGRadioButton(group, "Cleaned Events",  kRButCleanedEvt);
    TGCheckButton *but3 = new TGCheckButton(grp,   "Display Ellipse", kCButHillas);

    but2->SetState(kButtonDown);
    but3->SetState(kButtonDown);

    fDisplayRaw    = kFALSE;
    fDisplayHillas = kTRUE;

    /*
     WARNING:
     Bacause of some strage and hidden dependencies the
     GetMainFrame call in the destructor of TGButton may fail if some
     of the other gui elements are deleted first.
     AddFirst adds the buttons at the beginning of the deletion list,
     this seems to work.
     */
    fList->AddFirst(but1);
    fList->AddFirst(but2);

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

    TGLayoutHints *laybut = new TGLayoutHints(kLHintsNormal, 15, 0);
    fList->Add(laybut);

    grp->AddFrame(group);
    grp->AddFrame(but3, laybut);

    TGLayoutHints *laygrp1 = new TGLayoutHints(kLHintsNormal|kLHintsExpandX, 10, 10, 5, 0);
    TGLayoutHints *laygrp2 = new TGLayoutHints(kLHintsNormal|kLHintsExpandX, 10, 10, 5, 5);

    fTab1->AddFrame(grp, laygrp1);
    ((MGTask*)GetTaskList()->FindObject("MImgCleanStd"))->CreateGui(fTab1, laygrp2);
}

// --------------------------------------------------------------------------
//
//  Setup Task and parameter list for hillas calculation,
//  preprocess tasks and read in first event (process)
//
void MGCamDisplay::SetupTaskList()
{
    MTaskList *tlist = GetTaskList();
    MParList  *plist = GetParList();

    MMcPedestalCopy   *pcopy = new MMcPedestalCopy;
    MMcPedestalNSBAdd *pdnsb = new MMcPedestalNSBAdd;
    MCerPhotCalc      *ncalc = new MCerPhotCalc;
    MClone            *clone = new MClone("MCerPhotEvt");
    MImgCleanStd      *clean = new MImgCleanStd;
    MBlindPixelCalc   *blind = new MBlindPixelCalc;
    MHillasCalc       *hcalc = new MHillasCalc;
    MHillasSrcCalc    *scalc = new MHillasSrcCalc;

    tlist->AddToList(pcopy);
    tlist->AddToList(pdnsb);
    tlist->AddToList(ncalc);
    tlist->AddToList(clone);
    tlist->AddToList(clean);
    tlist->AddToList(blind);
    tlist->AddToList(hcalc);
    tlist->AddToList(scalc);

    MPedestalCam  *pedest = new MPedestalCam;
    MRawEvtData   *event  = new MRawEvtData;

    // plist->AddToList(geom);
    plist->AddToList(pedest);
    plist->AddToList(event);

    // return NULL;
}

// --------------------------------------------------------------------------
//
//  Constructor.
//
MGCamDisplay::MGCamDisplay(const char *filename, const TGWindow *p,
                           UInt_t w, UInt_t h)
: MGEvtDisplay(filename, "Events", p, w, h)
{
    //
    // Setup Task list for hillas calculation
    //
    SetupTaskList();

    //
    // Add missing setup elements to GUI
    //
    AddSetupElements();

    fCanvas2[0] = fCanvas;
    fCanvas2[1] = AddTab("PedRMS");
    fCanvas2[2] = AddTab("Phot/PedRMS");
    fCanvas2[3] = AddTab("Levels");
    fCanvas2[4] = AddTab("Pedestals");

    //
    // Show camera display for the actual geometry
    //
    gStyle->SetOptStat(1100);
    for (int i=0; i<5; i++)
    {
        fCanvas2[i]->cd();
        fDisplay[i] = new MHCamera;//(*geom);
        fDisplay[i]->Draw();
        fDisplay[i]->AddNotify(*(MCamEvent*)GetParList()->FindObject("MRawEvtData"));
        fCanvas2[i]->Update();
        fList->Add(fDisplay[i]);
    }

    fDisplay[0]->SetNameTitle("photons",    "Number of Photons");
    fDisplay[1]->SetNameTitle("pedrms",     "Pedestal RMS");
    fDisplay[2]->SetNameTitle("photpedrms", "Number of Photons / Pedestal RMS");
    fDisplay[3]->SetNameTitle("levels",     "Cleaning Levels");
    fDisplay[4]->SetNameTitle("pedestals",  "Pedestals");

    fDisplay[0]->SetYTitle("S [au]");
    fDisplay[1]->SetYTitle("P_{rms} [au]");
    fDisplay[2]->SetYTitle("S/P_{rms}");
    fDisplay[3]->SetYTitle("L");
    fDisplay[4]->SetYTitle("P [au]");

    ReadFirstEvent();

    //
    //   Map the window, set up the layout, etc.
    //
    MapSubwindows();

    Layout();

    SetWindowName("Image Event Display");
    SetIconName("Image");

    MapWindow();
}

// --------------------------------------------------------------------------
//
//  Update event display:
//  dependent on the setup either the uncleaned or cleand data is shown
//  together with the hillas ellipse or not.
//
void MGCamDisplay::UpdateDisplay()
{
    if (!IsInitOk())
        return;

    const MParList *plist = fEvtLoop->GetParList();

    if (!fDisplay[0]->GetGeometry())
    {
        MGeomCam *cam = (MGeomCam*)plist->FindObject("MGeomCam");
        for (int i=0; i<5; i++)
            fDisplay[i]->SetGeometry(*cam);
    }

    //
    // Show Hillas ellipse
    //
    MHillas *hillas = (MHillas*)plist->FindObject("MHillas");

    hillas->Print();
    plist->FindObject("MHillasExt")->Print();
    plist->FindObject("MHillasSrc")->Print();
    plist->FindObject("MNewImagePar")->Print();

    //
    // Display the requested event. This does a Canvas update, too.
    //
    MCerPhotEvt *evt = NULL;
    if (fDisplayRaw)
    {
        // Get a clone of MCerPhotEvt which is made before the image cleaning
        const MClone *clone = (MClone*)GetTaskList()->FindObject("MClone");
        evt = (MCerPhotEvt*)clone->GetClone();
    }
    else
    {
        // Get MCerPhotEvt which containes the cleaned data
        evt = (MCerPhotEvt*)plist->FindObject("MCerPhotEvt");
    }

    const MImgCleanStd *clean = (MImgCleanStd*)GetTaskList()->FindObject("MImgCleanStd");
    const MPedestalCam *ped   = (MPedestalCam*)plist->FindObject("MPedestalCam");

    const Int_t n = ped->GetSize();

    // FIXME: Make this publicly available
    TArrayD val(n);
    TArrayC use(n);
    for (int i=0; i<n;i++)
    {
        // FIXME: photon event first!
        MCerPhotPix *pix = evt->GetPixById(i);
        if (!pix)
            continue;
        MPedestalPix &ppix = (*ped)[i];
        if (ppix.GetPedestalRms()<=0)
            continue;
        val[i] = pix->GetNumPhotons()/ppix.GetPedestalRms();
        use[i] = 1;
    }

    TArrayF lvl(2);
    lvl[0] = clean->GetCleanLvl2();
    lvl[1] = clean->GetCleanLvl1();

    fDisplay[0]->SetCamContent(*evt, 0);
    fDisplay[1]->SetCamContent(*ped, 1);
    fDisplay[2]->SetCamContent(val, &use);
    fDisplay[3]->SetCamContent(val, &use);
    fDisplay[3]->SetLevels(lvl);
    fDisplay[4]->SetCamContent(*ped, 0);

    if (fDisplayHillas)
        for (int i=0; i<5; i++)
        {
            fCanvas2[i]->cd();
            hillas->Draw();
            fCanvas2[i]->Modified();
            fCanvas2[i]->Update();
         }
    else
    {
        hillas->Clear();
        for (int i=0; i<5; i++)
        {
            fCanvas2[i]->Modified();
            fCanvas2[i]->Update();
        }
    }
}

// --------------------------------------------------------------------------
//
//  Process the messages from the setup GUI elements.
//
Bool_t MGCamDisplay::ProcessMessage(Long_t msg, Long_t parm1, Long_t parm2)
{
    switch(GET_MSG(msg))
    {
    case kC_COMMAND:
        switch(GET_SUBMSG(msg))
        {
        case kCM_CHECKBUTTON:
            switch (parm1)
            {
            case kCButHillas:
                fDisplayHillas = !fDisplayHillas;
                UpdateDisplay();
                return kTRUE;
            }
            break;

        case kCM_RADIOBUTTON:
            switch (parm1)
            {
            case kRButRawEvt:
                fDisplayRaw = kTRUE;
                UpdateDisplay();
                return kTRUE;

            case kRButCleanedEvt:
                fDisplayRaw = kFALSE;
                UpdateDisplay();
                return kTRUE;
            }
            break;
        }
        break;
    }
    return MGEvtDisplay::ProcessMessage(msg, parm1, parm2);
}

