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

/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// MWriteAsciiFile                                                         //
//                                                                         //
// If you want to store a single container into an Ascii file you have     //
// to use this class. You must know the name of the file you wanne write   //
// (you should know it) and the name of the container you want to write.   //
// This can be the name of the class or a given name, which identifies     //
// the container in a parameter container list (MParList).                 //
// The container is written to the ascii file if its ReadyToSave flag is   //
// set (MParContainer)                                                     //
//                                                                         //
// You can write more than one container in one line of the file, see      //
// AddContainer.                                                           //
//                                                                         //
// You can also write single data members of a container (like fWidth      //
// of MHillas). For more details see AddContainer. Make sure, that a       //
// getter method for the data member exist. The name of the method         //
// must be the same than the data member itself, but the f must be         //
// replaced by a Get.                                                      //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////

#include "MWriteAsciiFile.h"

#include <fstream.h>

#include <TClass.h>      // IsA
#include <TMethodCall.h> // TMethodCall, AsciiWrite
#include <TDataMember.h> // TDataMember, AsciiWrite

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

#include "MParList.h"

ClassImp(MWriteAsciiFile);

// --------------------------------------------------------------------------
//
// Init. Replaces the same code used in all constructors.
//
void MWriteAsciiFile::Init(const char *filename, const char *name, const char *title)
{
    fName  = name  ? name  : "MWriteAsciiFile";
    fTitle = title ? title : "Task to write one container to an ascii file";

    fNameFile = filename;

    fOut = new ofstream(fNameFile);
}

// --------------------------------------------------------------------------
//
// Specify the name of the ascii output file 'filename' and the name
// of the container you want to write. (Normally this is the name
// of the class (eg. MHillas) but it can also be a different name which
// identifies the container in the parameter list.
// Because you cannot write more than one container there is no Add-function
// like in MWriteRootFile.
//
//  For Example: MWriteAsciiFile("file.txt", "MHillas");
//
MWriteAsciiFile::MWriteAsciiFile(const char *filename, const char *contname,
                                 const char *name, const char *title)
{
    Init(filename, name, title);

    if (contname)
        AddContainer(contname);
}

// --------------------------------------------------------------------------
//
// Specify a the name of the ascii output file 'filename' and a pointer to
// the container you want to write. 
// Because you cannot write more than one container there is no Add-function
// like in MWriteRootFile.
//
//  For Example: MHillas hillas;
//               MWriteAsciiFile("file.txt", &hillas);
//
//
MWriteAsciiFile::MWriteAsciiFile(const char *filename, MParContainer *cont,
                                 const char *name, const char *title)
{
    Init(filename, name, title);

    if (cont)
        AddContainer(cont);
}

// --------------------------------------------------------------------------
//
// Destructor. Delete the output file if necessary (it is closed
// automatically by its destructor.
//
MWriteAsciiFile::~MWriteAsciiFile()
{
    fContNames.SetOwner();
    fMembers.SetOwner();

    delete fOut;
}

// --------------------------------------------------------------------------
//
// Check if the containers are ready for writing. If so write them.
// The containers are written in one line, one after each other.
// If not all containers are written (because of the IsReadyToSave-flag)
// a warning message is print.
//
void MWriteAsciiFile::CheckAndWrite() const
{
    Bool_t written = kFALSE;

    MParContainer *cont = NULL;

    Int_t num = fContainer.GetEntries();

    TIter NextCont(&fContainer);
    TIter NextMemb(&fMembers);

    while ((cont=(MParContainer*)NextCont()))
    {
        const MScale *memb = (MScale*)NextMemb();

        if (!cont->IsReadyToSave())
            continue;

        if (memb->GetTitle()[0]=='\0')
        {
            if (!cont->AsciiWrite(*fOut))
                continue;
        }
        else
        {
            if (!cont->WriteDataMember(*fOut, memb->GetTitle(), memb->GetScale()))
                continue;
        }

        written = kTRUE;

        num--;
    }

    if (!written)
        return;

    *fOut << endl;

    if (num!=0)
        *fLog << warn << "Warning - given number of containers doesn't fit number of written containers." << endl;
}

// --------------------------------------------------------------------------
//
// Return open state of the file
//
Bool_t MWriteAsciiFile::IsFileOpen() const
{
    return (bool)(*fOut);
}

// --------------------------------------------------------------------------
//
// Tries to get all containers from the ParList which were given by name
// adds them to the list of pointers to the container which should be
// written to the ascii file.
//
Bool_t MWriteAsciiFile::GetContainer(MParList *pList)
{
    MScale *obj = NULL;

    TIter Next(&fContNames);

    while ((obj=(MScale*)Next()))
    {
        const char *name = obj->GetName();

        MParContainer *cont = (MParContainer*)pList->FindObject(name, "MParContainer");
        if (!cont)
        {
            *fLog << err << dbginf << "Cannot find parameter container '" << name << "'." << endl;
            return kFALSE;
        }

        AddContainer(cont, obj->GetTitle(), obj->GetScale());
    }

    return kTRUE;
}

// --------------------------------------------------------------------------
//
// Add another container (by name) to be written to the ascii file.
// The container will be output one after each other in one line.
// If you want to write only one data member of the container
// specify the name of the data member (eg. fAlpha) Make sure,
// that a "GetteMethod" for this data type exists (strip the f and
// replace it by Get)
// If you specify a single data member you can add a scale-factor which
// is (in case of the data member being a floating point value) multiplied
// with the data member value. This is usefull if you are want to
// change the scale (unit) of a data member for writing (eg.
// writing degrees for the hillas parameters instead of the internally
// used millimeters)
//
void MWriteAsciiFile::AddContainer(const char *cname, const char *member, Double_t scale)
{
    MScale *name = new MScale(cname, member, scale);
    fContNames.AddLast(name);
}

// --------------------------------------------------------------------------
//
// Add another container (by pointer) to be written to the ascii file.
// The container will be output one after each other in one line.
// If you want to write only one data member of the container
// specify the name of the data member (eg. fAlpha) Make sure,
// that a "GetteMethod" for this data type exists (strip the f and
// replace it by Get)
// If you specify a single data member you can add a scale-factor which
// is (in case of the data member being a floating point value) multiplied
// with the data member value. This is usefull if you are want to
// change the scale (unit) of a data member for writing (eg.
// writing degrees for the hillas parameters instead of the internally
// used millimeters)
//
void MWriteAsciiFile::AddContainer(MParContainer *cont, const char *member, Double_t scale)
{
    fContainer.AddLast(cont);

    MScale *name = new MScale("", member, scale);
    fMembers.AddLast(name);
}

