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

//////////////////////////////////////////////////////////////////////////////
//
//  MReportFileRead                                                            //
//
//////////////////////////////////////////////////////////////////////////////
#include "MReportFileRead.h"

#include <fstream>

#include <TRegexp.h>
#include <THashTable.h>

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

#include "MReport.h"
#include "MParList.h"

ClassImp(MReportFileRead);

using namespace std;

const TString MReportFileRead::gsReportHeader ="[CC Report File]";
const TString MReportFileRead::gsVersionPrefix="Arehucas Version Number";

class MReportHelp : public TObject
{
private:
    MReport *fReport;
    ULong_t  fNumReports;

public:
    MReportHelp(MReport *rep) : fReport(rep), fNumReports(0) { }
    const char *GetName() const { return fReport->GetIdentifier(); }
    ULong_t GetNumReports() const { return fNumReports; }
    ULong_t Hash() const { return fReport->GetIdentifier().Hash(); }
    MReport *GetReport() { return fReport; }
    //void SetTime(MTime *t) { fReport->SetTime(t); }
    Bool_t Interprete(TString &str)
    {
        if (!fReport->Interprete(str))
            return kFALSE;

        fNumReports++;
        return kTRUE;
    }
    Bool_t SetupReading(MParList &plist) { return fReport->SetupReading(plist); }
    void AddToList(MParList &plist) { plist.AddToList(fReport); }
};

// --------------------------------------------------------------------------
//
// Default constructor. It tries to open the given file.
//
MReportFileRead::MReportFileRead(const char *fname, const char *name, const char *title)
    : fFileName(fname), fIn(NULL)
{
    fName  = name  ? name  : "MReportFileRead";
    fTitle = title ? title : "Read task to read Central Control report files";

    fIn = new ifstream;
    fList = new THashTable(1,1);
    fList->SetOwner();
}

// --------------------------------------------------------------------------
//
// Destructor. Delete input stream.
//
MReportFileRead::~MReportFileRead()
{
    delete fIn;
    delete fList;
}

MReportHelp *MReportFileRead::GetReportHelp(const TString &str) const
{
    return (MReportHelp*)fList->FindObject(str);
}

MReport *MReportFileRead::GetReport(MReportHelp *help) const
{
    return help ? help->GetReport() : 0;
}

MReport *MReportFileRead::GetReport(const TString &str) const
{
    return GetReport(GetReportHelp(str));
}

Bool_t MReportFileRead::AddToList(MReport *rep) const
{
    if (GetReport(rep->GetIdentifier()))
    {
        *fLog << warn << "WARNING - Report with Identifier '";
        *fLog << rep->GetIdentifier() << "' already added to the list... ";
        *fLog << "ignored." << endl;
        return kFALSE;
    }

    fList->Add(new MReportHelp(rep));
    return kTRUE;
}

Bool_t MReportFileRead::CheckFileHeader() const
{
    TString str;
    str.ReadLine(*fIn);   // Read to EOF or newline
    if (str != gsReportHeader)
    {
        *fLog << err << "ERROR - First line doesn't match '" << gsReportHeader <<"' ";
        *fLog << "in file '" << fFileName << "'"<<endl;
        return kFALSE;
    }

    str.ReadLine(*fIn);   // Read to EOF or newline
    if (!str.BeginsWith(gsVersionPrefix))
    {
        *fLog << err << "ERROR - Version prefix '" << gsVersionPrefix <<"' ";
        *fLog << "not found in second line of file '" << fFileName << "'"<<endl;
        return kFALSE;
    }

    str.Remove(0, gsVersionPrefix.Length());
    str = str.Strip(TString::kBoth);

    TString ver = str(TRegexp("^[0-9][0-9][0-9][0-9][0-9][0-9]-[0-9]$"));
    if (ver.IsNull())
    {
        *fLog << err << "ERROR - Version string '" << str <<"' doesn't ";
        *fLog << "match regular expression." << endl;
        return kFALSE;
    }

    *fLog << dbg << "Report File version: <" << ver << ">" << endl;

    return kTRUE;
}

// --------------------------------------------------------------------------
//
// The PreProcess of this task checks for the following containers in the
// list:
//   MRawRunHeader <output>   if not found it is created
//   MRawEvtHeader <output>   if not found it is created
//   MRawEvtData <output>     if not found it is created
//   MRawCrateArray <output>  if not found it is created
//   MRawEvtTime <output>     if not found it is created (MTime)
//
// If all containers are found or created the run header is read from the
// binary file and printed.  If the Magic-Number (file identification)
// doesn't match we stop the eventloop.
//
// Now the EvtHeader and EvtData containers are initialized.
//
Int_t MReportFileRead::PreProcess(MParList *pList)
{
    //MTime *time = (MTime*)pList->FindCreateObj("MTime");
    //if (!time)
    //    return kFALSE;

    TIter Next(fList);
    MReportHelp *help=0;
    while ((help=(MReportHelp*)Next()))
        if (!help->SetupReading(*pList))
            return kFALSE;

    fList->ForEach(MReportHelp, AddToList)(*pList);

    //
    // open the input stream
    // first of all check if opening the file in the constructor was
    // successfull
    //
    fIn->open(fFileName);
    if (!(*fIn))
    {
        *fLog << err << "Error: Cannot open file '" << fFileName << "'" << endl;
        return kFALSE;
    }

    return CheckFileHeader();
}

// --------------------------------------------------------------------------
//
// The Process reads one event from the binary file:
//  - The event header is read
//  - the run header is read
//  - all crate information is read
//  - the raw data information of one event is read
//
Int_t MReportFileRead::Process()
{
    TString str;

    MReportHelp *rep=NULL;
    while (!GetReport(rep))
    {
        str.ReadLine(*fIn);
        if (!*fIn)
        {
            *fLog << dbg << "EOF detected." << endl;
            return kFALSE;
        }

        const Int_t pos = str.First(' ');
        if (pos<=0)
            continue;

        rep = GetReportHelp(str(0,pos));
        if (GetReport(rep))
            str.Remove(0, pos);
    }

    if (!rep->Interprete(str))
    {
        *fLog << err << "ERROR - Interpretation of '" << rep->GetName() << "' failed." << endl;
        return kFALSE;
    }

    return kTRUE;
}

// --------------------------------------------------------------------------
//
//  Close the file. Check whether the number of read events differs from
//  the number the file should containe (MRawRunHeader). Prints a warning
//  if it doesn't match.
//
Int_t MReportFileRead::PostProcess()
{
    fIn->close();

    if (!GetNumExecutions())
        return kTRUE;

    *fLog << inf << endl;
    *fLog << GetDescriptor() << " statistics:" << endl;
    *fLog << dec << setfill(' ');

    TIter Next(fList);
    MReportHelp *rep=0;

    while ((rep=(MReportHelp*)Next()))
    {
        *fLog << " " << setw(7) << rep->GetNumReports() << " (";
        *fLog << setw(3) << (int)(100.*rep->GetNumReports()/GetNumExecutions());
        *fLog << "%): " << rep->GetName() << endl;
    }

    return kTRUE;
}
