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

//////////////////////////////////////////////////////////////////////////////
//
//  MStatusArray
//
//  Helper class for MStatusDisplay
//
// If you want to read a MStatusArray (normally with name MStatusDisplay)
// it is recommended to do it like this:
//     TFile f("myfile.root", "read");
//     MStatusArray arr;
//     arr.Read();
//
// If you want to use TFile::Get or TFile::GetObject you should switch off
// addding Histograms automatically to the current directory first:
//     TFile f("myfile.root", "read");
//     TH1::AddDirectory(kFALSE);
//     f.Get("MStatusDisplay");
//
//////////////////////////////////////////////////////////////////////////////
#include "MStatusArray.h"

#include <TH1.h>      // TH1::AddDirectoryStatus();
#include <TClass.h>
#include <TCanvas.h>

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

#include "MStatusDisplay.h"

ClassImp(MStatusArray);

using namespace std;

// --------------------------------------------------------------------------
//
// If o==NULL a new status display is created, otherwise the one with name o
// is searched in gROOT->GetListOfSpecials().
// In this display the contents of the MStatusArray is displayed.
//
TObject *MStatusArray::DisplayIn(Option_t *o) const
{
    MStatusDisplay *d = 0;
    if (TString(o).IsNull())
        d = new MStatusDisplay;

    if (!d)
        d = (MStatusDisplay*)gROOT->GetListOfSpecials()->FindObject(o);

    if (!d)
        return 0;

    if (d->Display(*this))
        return d;

    delete d;
    return 0;
}

// --------------------------------------------------------------------------
//
// Display the contents of the given tab in the display given as argument.
//
void MStatusArray::DisplayIn(MStatusDisplay &d, const char *tab) const
{
    d.Display(*this, tab);
}

TObject *MStatusArray::FindObjectInPad(TVirtualPad *pad, const char *object, TClass *cls) const
{
    TObject *o = pad->FindObject(object);
    if (o && o->InheritsFrom(cls))
        return o;

    TIter Next(pad->GetListOfPrimitives());
    while ((o=Next()))
    {
        if (o==pad || !o->InheritsFrom(TVirtualPad::Class()))
            continue;

        if ((o = FindObjectInPad((TVirtualPad*)o, object, cls)))
            if (o->InheritsFrom(cls))
                return o;
    }
    return 0;
}

// FIXME: Move to a general class MMARS (TROOT) and unify with MParContainer
TClass *MStatusArray::GetClass(const char *name) const
{
    TClass *cls = gROOT->GetClass(name);
    Int_t rc = 0;
    if (!cls)
        rc =1;
    else
    {
        if (!cls->Property())
            rc = 5;
        if (!cls->Size())
            rc = 4;
        if (!cls->IsLoaded())
            rc = 3;
        if (!cls->HasDefaultConstructor())
            rc = 2;
    }

    if (rc==0)
        return cls;

    gLog << err << dbginf << "Class '" << name << "' not in dictionary: ";
    switch (rc)
    {
    case 1:
        gLog << "gROOT->GetClass() returned NULL." << endl;
        return NULL;
    case 2:
        gLog << "no default constructor." << endl;
        return NULL;
    case 3:
        gLog << "not loaded." << endl;
        return NULL;
    case 4:
        gLog << "zero size." << endl;
        return NULL;
    case 5:
        gLog << "no property." << endl;
        return NULL;
    }

    gLog << "THIS SHOULD NEVER HAPPEN!" << endl;

    return 0;
}

TCanvas *MStatusArray::FindCanvas(const char *name) const
{
    TObject *o = TObjArray::FindObject(name);
    if (!o)
        return 0;

    return o->InheritsFrom(TCanvas::Class()) ? (TCanvas*)o : 0;
}


TObject *MStatusArray::FindObjectInCanvas(const char *object, const char *base, const char *canvas) const
{
    TClass *cls = GetClass(base);
    if (!cls)
        return 0;

    TCanvas *c = canvas ? FindCanvas(canvas) : 0;
    if (canvas)
    {
        if (!c)
        {
            gLog << warn << "Canvas '" << canvas << "' not found..." << endl;
            return 0;
        }

        TObject *o = FindObjectInPad(c, object, cls);
        if (!o)
        {
            gLog << warn << "Object '" << object << "' [" << base << "] not found in canvas '" << canvas << "'..." << endl;
            return 0;
        }

        return o; //o->InheritsFrom(cls) ? o : 0;
    }

    TObject *o=0;
    TIter Next(this);
    while ((o=Next()))
    {
        if (!o->InheritsFrom(TVirtualPad::Class()))
            continue;

        if ((o=FindObjectInPad((TVirtualPad*)c, object, cls)))
            return o;
    }

    gLog << warn << "Object '" << object << "' [" << base << "] not found in canvas '" << canvas << "'..." << endl;
    return NULL;
}

TObject *MStatusArray::FindObjectInCanvas(const char *object, const char *canvas) const
{
    return FindObjectInCanvas(object, object, canvas);
}

TObject *MStatusArray::FindObject(const char *object, const char *base) const
{
    return FindObjectInCanvas(object, base, 0);
}

TObject *MStatusArray::FindObject(const char *object) const
{
    return FindObjectInCanvas(object, object, 0);
}

// --------------------------------------------------------------------------
//
// Print recursively all objects in this and sub-pads
//
void MStatusArray::PrintObjectsInPad(const TCollection *list, const TString &name, Int_t lvl) const
{
    TIter Next(list);
    TObject *o=0;
    while ((o=Next()))
    {
        const Bool_t print = name.IsNull() || name==(TString)o->GetName();
        if (print)
        {
            if (lvl>0)
                gLog << setw(lvl) << ' ';
            gLog << o->ClassName() << ": " << o->GetName() << " <" << Next.GetOption() << "> (" << o << ")" << endl;
        }

        if (o->InheritsFrom(TVirtualPad::Class()))
            PrintObjectsInPad(((TVirtualPad*)o)->GetListOfPrimitives(), print?TString():name, lvl+1);
    }
}

// --------------------------------------------------------------------------
//
// Switch off adding histograms to current directory before reading.
// Switch back
//
Int_t MStatusArray::Read(const char *name)
{
    const Bool_t store = TH1::AddDirectoryStatus();
    TH1::AddDirectory(kFALSE);
    const Int_t rc = TObjArray::Read(name?name:"MStatusDisplay");
    TH1::AddDirectory(store);
    return rc;
}

// --------------------------------------------------------------------------
//
// Print recursively all objects in this and sub-pads. If !option.IsNull()
// only objects in the corresponding pad are printed.
//
void MStatusArray::Print(Option_t *option) const
{
    const TString opt(option);

    PrintObjectsInPad(this, opt);
}
