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

/////////////////////////////////////////////////////////////////////////////
//
// MReadRflFile
//
// Reads a output file of the reflector program
//
/////////////////////////////////////////////////////////////////////////////
#include "MReadRflFile.h"

#include <errno.h>
#include <fstream>

#include <TSystem.h>

#include "structures_rfl.h"

#include "MParList.h"
#include "MRflEvtData.h"
#include "MRflEvtHeader.h"
#include "MRflRunHeader.h"
#include "MRflSinglePhoton.h"

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

ClassImp(MReadRflFile);

using namespace std;

// ------------------------------------------------
const char PROGNAME[] = "reflector";
#define SIZE_OF_FLAGS           13
#define SIZE_OF_SIGNATURE       13
#define FLAG_START_OF_RUN       "\nSTART---RUN\n"
#define FLAG_START_OF_EVENT     "\nSTART-EVENT\n"
#define FLAG_END_OF_EVENT       "\nEND---EVENT\n"
#define FLAG_END_OF_RUN         "\nEND-----RUN\n"
#define FLAG_END_OF_FILE        "\nEND----FILE\n"
#define FLAG_END_OF_STDIN       "\nEND---STDIN\n"
// ------------------------------------------------

Bool_t MReadRflFile::FlagIsA(const  char *s1, const char *flag)
{
    return strncmp(s1, flag, SIZE_OF_FLAGS)==0;
}

Bool_t MReadRflFile::ReadEvtHeader()
{
    if (fCurrentVersion <= 0.5)
    {
        RflEventHeader_old revth;
        fIn->read((char*)&revth, sizeof(RflEventHeader_old));
        fEvtHeader->SetEvtNumber((int)revth.EvtNumber);
//        *fLog << "Event Number: " << revth.EvtNumber;
//        *fLog << "  Primary ID: " << revth.PrimaryID;
//        *fLog << "  Run Number: " << revth.RunNumber << endl;
        return (bool)*fIn;
    }
    else
    {
        RflEventHeader revth;
        fIn->read((char*)&revth, sizeof(RflEventHeader));
        fEvtHeader->SetEvtNumber((int)revth.EvtNumber);
//        *fLog << "Event Number: " << revth.EvtNumber;
//        *fLog << "  Primary ID: " << revth.PrimaryID;
//        *fLog << "  Run Number: " << revth.RunNumber << endl;
        return (bool)*fIn;
    }
}

enum {
    kError,
    kEndOfFile,
    kStartOfRun,
    kEndOfRun,
    kStartOfEvtData,
    kEndOfEvtData,
    kUndefined
};


int MReadRflFile::ReadFlag()
{
    char flag[SIZE_OF_FLAGS];
    fIn->read(flag, SIZE_OF_FLAGS);

    if (!fIn)
        return kError;

    //*fLog << "<" << TString(&flag[1], 11)  << ">" <<endl;

    if (FlagIsA(flag, FLAG_END_OF_FILE))
        return kEndOfFile;
    if (FlagIsA(flag, FLAG_END_OF_RUN))
        return kEndOfRun;
    if (FlagIsA(flag, FLAG_START_OF_RUN))
        return kStartOfRun;
    if (FlagIsA(flag, FLAG_END_OF_EVENT))
        return kEndOfEvtData;
    if (FlagIsA(flag, FLAG_START_OF_EVENT))
        return kStartOfEvtData;

    return kUndefined;
}

Bool_t MReadRflFile::ReadEvtData()
{
    Bool_t rc = kFALSE;
    while (1)
    {
        cphoton data; // FIRST READ "START OF EVENT"
        fIn->read((char*)&data, SIZE_OF_FLAGS);
        if (!*fIn)
            break;

        if (FlagIsA((char*)&data, FLAG_END_OF_EVENT))
        {
            rc = kTRUE;
            break;
        }

        fIn->read((char*)&data+SIZE_OF_FLAGS, sizeof(cphoton)-SIZE_OF_FLAGS);
        if (!*fIn)
            break;

        MRflSinglePhoton &ph = fEvtData->GetNewPhoton();
        ph.SetXY(data.x*10, data.y*10);
        ph.SetCosUV(data.u, data.v);
        ph.SetTime(data.t);
        ph.SetHeight(data.h);
        ph.SetInclinationAngle(data.phi);
    }

    fEvtData->FixSize();
    return rc;
}

Int_t MReadRflFile::EvalFlag()
{
    const Int_t flag = ReadFlag();

    switch (flag)
    {
    case kEndOfFile:
        fCurrentVersion = ReadVersion();
        if (fCurrentVersion<0)
        {
            *fLog << inf << "Found end of file...Everything OK." << endl;
            break;
        }

        *fLog << warn << "Found flag of end of file, but file goes on..." << endl;
        if (ReadFlag()<0)
            return kError;
        /* FALLTHROU */
    case kStartOfRun:
        if (fCurrentVersion>0.5)
        {
            RflRunHeader rrunh;
            fIn->read((char*)&rrunh, sizeof(RflRunHeader));
            if (*fIn)
            {
                *fLog << inf << "FIXME: Call ReInit" << endl;

                fRunHeader->SetRunNumber((int)rrunh.RunNumber);
                *fLog << underline << "RunHeader:" << endl;
                *fLog << " Run Number:   " << rrunh.RunNumber << endl;
                *fLog << " Date:         " << rrunh.date << endl;
                *fLog << " Corsika Ver.: " << rrunh.Corsika_version << endl;

                break;
            }

            *fLog << err << "Error! found end of file... But no EOF flag. Exiting." << endl;
            return kError;
        }
        return kUndefined;

    case kStartOfEvtData:
    case kEndOfRun:
        break;

    case kError:
        *fLog << err << "ERROR - Flag 'error'" << endl;
        return kError;

    case kUndefined:
        *fLog << err << "ERROR - Flag 'undefined'" << endl;
        return kError;

    default:
        *fLog << err << "ERROR - Unhandled flag" << endl;
        return kError;

    }
    return flag;
}

Int_t MReadRflFile::Process()
{
    for (;;)
    {
        switch (EvalFlag())
        {
        case kError:
            return kFALSE;

        case kEndOfFile:
            if (!OpenNextFile())
                return kFALSE;
            /* FALLTHROU */
        case kStartOfRun:
        case kEndOfRun:
            continue;

        case kStartOfEvtData:
            break;
        }
        break;
    }

    if (!ReadEvtHeader())
        return kFALSE;

    return ReadEvtData();
}

Int_t MReadRflFile::PreProcess(MParList *plist)
{
    fEvtData=(MRflEvtData*)plist->FindCreateObj("MRflEvtData");
    if (!fEvtData)
        return kFALSE;

    fEvtHeader=(MRflEvtHeader*)plist->FindCreateObj("MRflEvtHeader");
    if (!fEvtHeader)
        return kFALSE;

    fRunHeader=(MRflRunHeader*)plist->FindCreateObj("MRflRunHeader");
    if (!fRunHeader)
        return kFALSE;

    Rewind();

    return OpenNextFile();
}

// --------------------------------------------------------------------------
//
// This opens the next file in the list and deletes its name from the list.
//
Bool_t MReadRflFile::OpenNextFile()
{
    //
    // open the input stream and check if it is really open (file exists?)
    //
    if (fIn)
        delete fIn;
    fIn = NULL;

    //
    // Check for the existence of a next file to read
    //
    if (fNumFile >= (UInt_t)fFileNames->GetSize())
    {
        *fLog << inf << GetDescriptor() << ": No unread files anymore..." << endl;
        return kFALSE;
    }

    TNamed *file = (TNamed*)fFileNames->At(fNumFile);

    //TNamed *file = (TNamed*)fFileNames->GetFirst();
    //if (!file)
    //    return kFALSE;

    //
    // open the file which is the first one in the chain
    //
    fFileName = file->GetName();
    const TString expname = fFileName;
    gSystem->ExpandPathName(expname);

    //
    // Remove this file from the list of pending files
    //
    //fFileNames->Remove(file);

    *fLog << inf << "Open file: '" << fFileName << "'" << endl;

    fIn = new ifstream(expname);
    if (!*fIn)
    {
        *fLog << err << "Cannot open file " << expname << ": ";
        *fLog << strerror(errno) << endl;
        return kFALSE;
    }

    *fLog << inf;
    fLog->Separator(fFileName);

    fCurrentVersion = ReadVersion();
    if (fCurrentVersion<0)
    {
        cout << "ERROR reading signature." << endl;
        return kFALSE;
    }
    cout << "Version " << fCurrentVersion << endl << endl;

    fNumFile++;
    return kTRUE;
}

/****************************************************/

float MReadRflFile::ReadVersion()
{
    char sign[20];
    fIn->read(sign, SIZE_OF_SIGNATURE);
    if (!*fIn)
        return -1;

    if (strncmp(sign, PROGNAME, strlen(PROGNAME)) != 0)
    {
        /* For the ascii tail of the file! : */
        if (strncmp(sign, "\n############", SIZE_OF_SIGNATURE))
            cout << "ERROR: Signature of .rfl file is not correct: " << sign << endl;

        return -1;
    }

    float version;
    sscanf(sign, "%*s %f", &version);

    //If the version is < 0.6 the signature had one more byte
    if (version < 0.6)
        *fIn >> sign[0];

    return version;
}

// --------------------------------------------------------------------------
//
// Default constructor. Creates an array which stores the file names of
// the files which should be read. If a filename is given it is added
// to the list.
//
MReadRflFile::MReadRflFile(const char *fname, const char *name,
                           const char *title) : fIn(NULL), fEntries(0)
{
    fName  = name  ? name  : "MRead";
    fTitle = title ? title : "Reads a Reflector output file";

    //
    // remember file name for opening the file in the preprocessor
    //
    fFileNames = new TList;
    fFileNames->SetOwner();

    if (fname)
        AddFile(fname);
}

// --------------------------------------------------------------------------
//
// Delete the filename list and the input stream if one exists.
//
MReadRflFile::~MReadRflFile()
{
    delete fFileNames;
    if (fIn)
        delete fIn;
}

// --------------------------------------------------------------------------
//
// Add this file as the last entry in the chain
//
Int_t MReadRflFile::AddFile(const char *txt, int)
{
    const char *name = gSystem->ExpandPathName(txt);

    TString fname(name);
    delete [] name;
/*
    if (!CheckHeader(fname))
    {
        *fLog << warn << "WARNING - Problem reading header... ignored." << endl;
        return;
    }

    const Int_t n = GetNumEvents(fname);
    if (n==0)
    {
        *fLog << warn << "WARNING - File contains no data... ignored." << endl;
        return;
    }

    fEntries += n;

    *fLog << inf << "File " << txt << " contains " << n << " events (Total=" << fEntries << ")" << endl;
*/
    fFileNames->AddLast(new TNamed(txt, ""));
    return 1;
}


Bool_t MReadRflFile::SearchFor(Int_t runno, Int_t eventno)
{
    if (!fEvtHeader)
        return kFALSE;

    fNumFile = 0;
    if (!OpenNextFile())
        return kFALSE;

    while (1)
    {
        fEvtData->Reset();
        if (!Process())
            return kFALSE;

        if (fEvtHeader->GetEvtNumber()==eventno &&
            fRunHeader->GetRunNumber()==runno)
            return kTRUE;
    }
}
