/* ======================================================================== *\
!
! *
! * 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;

// --------------------------------------------------------------------------
//
// Initialize the MStatusArray from an MStatusDisplay. Note, the contents
// still owned by MStatusDisplay and will vanish if the display changes
// or is deleted without further notice.
//
MStatusArray::MStatusArray(const MStatusDisplay &d) : TObjArray()
{
    d.FillArray(*this);
}

// --------------------------------------------------------------------------
//
// 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 = NULL;//pad->FindObject(object);
//    if (o && o->InheritsFrom(cls))
//        return o;

    TIter Next(pad->GetListOfPrimitives());
    while ((o=Next()))
    {
        if (o->GetName()==(TString)object && o->InheritsFrom(cls))
            return o;

        if (o==pad || !o->InheritsFrom(TVirtualPad::Class()))
            continue;

        if ((o = FindObjectInPad((TVirtualPad*)o, object, cls)))
            return o;
//            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 << ") " << (int)o->TestBit(kCanDelete) << endl;
        }

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

// --------------------------------------------------------------------------
//
// 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
{
    gLog << all;

    PrintObjectsInPad(this, TString(option));
}

/*
// --------------------------------------------------------------------------
//
// Make sure that kCanDelete is properly set for all directly contained
// objects. Some kCanDelete bits might not be properly set or get lost when
// the MParContainer is stored.
//
void MStatusArray::SetCanDelete(const TCollection *list) const
{
    TIter Next(list);
    TObject *o=0;
    while ((o=Next()))
    {
        if (o->InheritsFrom(TVirtualPad::Class()))
            SetCanDelete(((TVirtualPad*)o)->GetListOfPrimitives());
        else
            o->SetBit(kCanDelete|kMustCleanup);
    }
}
*/

// --------------------------------------------------------------------------
//
// Set kCanDelete for all objects for which kMyCanDelete is set. This
// is a STUPID workaruond for an ANNOYING root bug which is that
// the streamer function of TH1 resets the KCanDelete bit after reading.
//
void MStatusArray::SetCanDelete(const TCollection *list) const
{
    TIter Next(list);
    TObject *o=0;
    while ((o=Next()))
    {
        if (o->InheritsFrom(TVirtualPad::Class()))
            SetCanDelete(((TVirtualPad*)o)->GetListOfPrimitives());
        else
        {
            if (o->TestBit(kMyCanDelete) && o->InheritsFrom("TH1"))
            {
                o->SetBit(kCanDelete);
                o->ResetBit(kMyCanDelete);
            }
        }
    }
}

void MStatusArray::EnableTH1Workaround(const TCollection *list) const
{
    TIter Next(list?list:this);
    TObject *o=0;
    while ((o=Next()))
    {
        if (o->InheritsFrom(TVirtualPad::Class()))
            EnableTH1Workaround(((TVirtualPad*)o)->GetListOfPrimitives());
        else
            if (o->InheritsFrom("TH1"))
                o->SetBit(kCanDelete);
    }
}

// --------------------------------------------------------------------------
//
// Set kMyCanDelete for all objects for which kCanDelete is set. This
// is a STUPID workaruond for an ANNOYING root bug which is that
// the streamer function of TH1 resets the KCanDelete bit after reading.
//
void MStatusArray::SetMyCanDelete(const TCollection *list) const
{
    TIter Next(list);
    TObject *o=0;
    while ((o=Next()))
    {
        if (o->InheritsFrom(TVirtualPad::Class()))
            SetMyCanDelete(((TVirtualPad*)o)->GetListOfPrimitives());
        else
        {
            if (o->TestBit(kMyCanDelete) && o->InheritsFrom("TH1"))
                gLog << warn << "WARNING - MStatusArray::Write - " << o->GetName() << " [" << o->ClassName() << "] has BIT(30) already set!" << endl;

            if (o->TestBit(kCanDelete) && o->InheritsFrom("TH1"))
                o->SetBit(kMyCanDelete);
        }
    }
}

// --------------------------------------------------------------------------
//
// Reset kMyCanDelete for all objects for which kMyCanDelete is set. This
// is a STUPID workaruond for an ANNOYING root bug which is that
// the streamer function of TH1 resets the KCanDelete bit after reading.
//
void MStatusArray::ResetMyCanDelete(const TCollection *list) const
{
    TIter Next(list);
    TObject *o=0;
    while ((o=Next()))
    {
        if (o->InheritsFrom(TVirtualPad::Class()))
            ResetMyCanDelete(((TVirtualPad*)o)->GetListOfPrimitives());
        else
        {
            if (o->TestBit(kMyCanDelete) && o->InheritsFrom("TH1"))
                o->ResetBit(kMyCanDelete);
        }
    }
}

// --------------------------------------------------------------------------
//
// Switch off adding histograms to current directory before reading.
// Switch back
//
Int_t MStatusArray::Read(const char *name)
{
    // It seems that the contents are not properly deleted by TObjArray::Read
    Delete();

    // Make sure newly read histograms are not added to the current directory
    const Bool_t store = TH1::AddDirectoryStatus();
    TH1::AddDirectory(kFALSE);
    const Int_t rc = TObjArray::Read(name?name:"MStatusDisplay");
    TH1::AddDirectory(store);

    // All objects in the list (TNamed, TCanvas, etc) do not have
    // the kCanDelete bit set. Make sure that it is set to make
    // them deleted by the destructor of this list
    TIter Next(this);
    TObject *o=0;
    while ((o=Next()))
    {
        if (o->InheritsFrom(TVirtualPad::Class()))
        {
            TIter Next2(((TVirtualPad*)o)->GetListOfPrimitives());
            TObject *o2=0;
            while ((o2=Next2()))
                if (o2->InheritsFrom("MParContainer"))
                    o2->SetBit(kCanDelete);
        }
        o->SetBit(kCanDelete);
    }

    // Make sure that all kCanDelete bits are properly set
    SetCanDelete(this);
    SetOwner();

    return rc;
}

// --------------------------------------------------------------------------
//
// Switch off adding histograms to current directory before reading.
// Switch back
//
Int_t MStatusArray::Write(const char *name, Int_t option, Int_t bufsize) const
{
    SetMyCanDelete(this);
    const Int_t rc = TObjArray::Write(name, option, bufsize);
    ResetMyCanDelete(this);

    return rc;
}
