Index: trunk/FACT++/src/EventBuilderWrapper.h
===================================================================
--- trunk/FACT++/src/EventBuilderWrapper.h	(revision 10947)
+++ trunk/FACT++/src/EventBuilderWrapper.h	(revision 10949)
@@ -15,4 +15,6 @@
 #include <boost/date_time/posix_time/posix_time_types.hpp>
 
+#include <CCfits/CCfits>
+
 #include "EventBuilder.h"
 
@@ -21,4 +23,425 @@
     extern int CloseRunFile(uint32_t runId, uint32_t closeTime);
 }
+
+class DataFileImp
+{
+    uint32_t fRunId;
+
+public:
+    DataFileImp(uint32_t id) : fRunId(id) { }
+
+    virtual bool OpenFile(uint32_t runid, RUN_HEAD* h) = 0;
+    virtual bool Write(EVENT *) = 0;
+    virtual bool Close(RUN_TAIL * = 0) = 0;
+
+    uint32_t GetRunId() const { return fRunId; }
+};
+
+
+#include "FAD.h"
+
+class DataFileRaw : public DataFileImp
+{
+public:
+    DataFileRaw(uint32_t id) : DataFileImp(id)  { }
+    ~DataFileRaw() { Close(); }
+
+    virtual bool OpenFile(uint32_t, RUN_HEAD* ) { return true; }
+    virtual bool Write(EVENT *) { return true; }
+    virtual bool Close(RUN_TAIL * = 0) { return true; }
+};
+
+
+class DataFileFits : public DataFileImp
+{
+    CCfits::FITS*  fFile;        /// The pointer to the CCfits FITS file
+    CCfits::Table* fTable;       /// The pointer to the CCfits binary table
+
+    uint64_t fNumRows;                ///the number of rows that have been written already to the FITS file.
+    uint32_t fRoi;                ///the number of rows that have been written already to the FITS file.
+
+public:
+    DataFileFits(uint32_t runid) : DataFileImp(runid), fFile(0)
+    {
+    }
+
+    // --------------------------------------------------------------------------
+    //
+    //! Default destructor
+    //! The Fits file SHOULD have been closed already, otherwise the informations
+    //! related to the RUN_TAIL will NOT be written to the file.
+    //
+    ~DataFileFits() { Close(); }
+
+    // --------------------------------------------------------------------------
+    //
+    //! Add a new column to the vectors storing the column data.
+    //! @param names the vector of string storing the columns names
+    //! @param types the vector of string storing the FITS data format
+    //! @param numElems the number of elements in this column
+    //! @param type the char describing the FITS data format
+    //! @param name the name of the particular column to be added.
+    //
+    inline void AddColumnEntry(vector<string>& names, vector<string>& types, int numElems, char type, string name)
+    {
+        names.push_back(name);
+
+        ostringstream str;
+        if (numElems != 1)
+            str << numElems;
+        str << type;
+        types.push_back(str.str());
+    }
+
+    // --------------------------------------------------------------------------
+    //
+    //! Writes a single header key entry
+    //! @param name the name of the key
+    //! @param value its value
+    //! @param comment the comment associated to that key
+    //
+    //FIXME this function is a duplicate from the class Fits. should we try to merge it ?
+    template <typename T>
+    void WriteKey(const string &name, const T &value, const string &comment)
+    {
+        try
+        {
+            fTable->addKey(name, value, comment);
+        }
+        catch (CCfits::FitsException e)
+        {
+            ostringstream str;
+            str << "Could not add header key ";
+            //TODO pipe the error message somewhere
+        }
+    }
+
+    template <typename T>
+    void WriteKey(const string &name, const int idx, const T &value, const string &comment)
+    {
+        ostringstream str;
+        str << name << idx;
+
+        WriteKey(str.str(), value, comment);
+    }
+
+    // --------------------------------------------------------------------------
+    //
+    //! This creates an appropriate file name for a particular run number and type
+    //! @param runNumber the run number for which a filename is to be created
+    //! @param runType an int describing the kind of run. 0=Data, 1=Pedestal, 2=Calibration, 3=Calibrated data
+    //! @param extension a string containing the extension to be appened to the file name
+    //
+    string FormFileName(uint32_t runNumber, uint32_t runType, string extension)
+    {
+        //TODO where am I supposed to get the base directory from ?
+        //TODO also, for creating subsequent directories, should I use the functions from the dataLogger ?
+        string baseDirectory = "./Run";
+        ostringstream result;
+        result << baseDirectory;
+        result << Time::fmt("/%Y/%m/%d/") << (Time() - boost::posix_time::time_duration(12,0,0));
+        result << setfill('0') << setw(8) << runNumber;
+        result << "_001_";
+        switch (runType)
+        {
+        case 0:
+            result << 'D';
+            break;
+        case 1:
+            result << 'P';
+            break;
+        case 2:
+            result << 'C';
+            break;
+        case 3:
+            result << 'Y';
+            break;
+        default:
+            //TODO pipe this error message to the appropriate error stream
+            cout << "Error unexpected event" << endl;
+        };
+        result << "_data." << extension;
+
+        return result.str();
+    }
+
+    // --------------------------------------------------------------------------
+    //
+    //! DataFileFits constructor. This is the one that should be used, not the default one (parameter-less)
+    //! @param runid This parameter should probably be removed. I first thought it was the run number, but apparently it is not
+    //! @param h a pointer to the RUN_HEAD structure that contains the informations relative to this run
+    //
+    bool OpenFile(uint32_t /*runid*/, RUN_HEAD* h)
+    {
+        //Form filename, based on runid and run-type
+        string fileName = FormFileName(h->FADhead[0].runnumber, h->RunType, "fits");
+
+        //create the FITS object
+        try
+        {
+            fFile = new CCfits::FITS(fileName, CCfits::RWmode::Write);
+        }
+        catch (CCfits::FitsException e)
+        {
+            ostringstream str;
+            str << "Could not open FITS file " << fileName << " reason: " << e.message();
+            //TODO display the message somewhere
+
+            return false;
+        }
+
+        //create columns according to header
+        ostringstream arrayTypes;
+
+        // uint32_t EventNum ;       // EventNumber as from FTM
+        // uint16_t TriggerType ;    // Trigger Type from FTM
+        // uint32_t SoftTrig ;       // SoftTrigger Info (TBD)
+        // uint32_t PCTime ;         // when did event start to arrive at PC
+        // uint32_t BoardTime[NBOARDS];//
+        // int16_t  StartPix[NPIX];  // First Channel per Pixel (Pixels sorted according Software ID)  ; -1 if not filled
+        // int16_t  StartTM[NTMARK]; // First Channel for TimeMark (sorted Hardware ID) ; -1 if not filled
+        // uint16_t Adc_Data[];      // final length defined by malloc ....
+
+        vector<string> colNames;
+        vector<string> dataTypes;
+        AddColumnEntry(colNames, dataTypes, 1,              'V', "EventNum");
+        AddColumnEntry(colNames, dataTypes, 1,              'U', "TriggerType");
+        AddColumnEntry(colNames, dataTypes, 1,              'V', "SoftTrig");
+        AddColumnEntry(colNames, dataTypes, 1,              'V', "PCTime");
+        AddColumnEntry(colNames, dataTypes, NBOARDS,        'V', "BoardTime");
+        AddColumnEntry(colNames, dataTypes, NPIX,           'I', "StartPix");
+        AddColumnEntry(colNames, dataTypes, NTMARK,         'I', "StartTM");
+        AddColumnEntry(colNames, dataTypes, NPIX*h->Nroi,   'U', "Data");
+
+        fRoi = h->Nroi;
+
+        //actually create the table
+        try
+        {
+            fTable = fFile->addTable("Events", 0, colNames, dataTypes);
+            if (fTable->rows() != 0)
+            {
+                ostringstream str;
+                str << "Error: table created on the fly looks non-empty.";
+                //TODO giev the error text to some error handler
+                //FIXME I guess that this error checking is useless. remove it for performances.
+            }
+        }
+        catch(CCfits::FitsException e)
+        {
+            ostringstream str;
+            str << "Could not create FITS table " << "Events" << " in file " << fileName << " reason: " << e.message();
+            //TODO give the error text to some error handler
+
+            Close();
+            return false;
+        }
+
+        //write header data
+        //first the "standard" keys
+        string stringValue;
+        WriteKey("EXTREL",   1.0f,   "Release Number");
+        WriteKey("TELESCOP", "FACT", "Telescope that acquired this data");
+        WriteKey("ORIGIN",   "ISDC", "Institution that wrote the file");
+        WriteKey("CREATOR",  "FACT++ Event Builder", "Program that wrote this file");
+
+        stringValue = Time().GetAsStr();
+        stringValue[10]= 'T';
+        WriteKey("DATE",     stringValue, "File creation data");
+        WriteKey("TIMESYS",  "TT",        "Time frame system");
+        WriteKey("TIMEUNIT", "d",         "Time unit");
+        WriteKey("TIMEREF",  "UTC",       "Time reference frame");
+        //FIXME should we also put the start and stop time of the received data ?
+        //now the events header related variables
+        WriteKey("VERSION",  h->Version,  "Builder version");
+        WriteKey("RUNTYPE",  h->RunType,  "Type of run");
+        WriteKey("NBOARD",   h->NBoard,   "Number of acquisition boards");
+        WriteKey("NPIX",     h->NPix,     "Number of pixels");
+        WriteKey("NTM",      h->NTm,      "Number of Time marks");
+        WriteKey("NROI",     h->Nroi,     "Number of slices per pixels");
+
+        //now the boards related keywords
+        for (int i=0; i<h->NBoard; i++)
+        {
+            const PEVNT_HEADER &hh = h->FADhead[i];
+
+            WriteKey("STPKGFG", i, hh.start_package_flag,
+                     "Start package flag");
+
+            WriteKey("PKGLEN",  i, hh.package_length,
+                     "Package length");
+
+            WriteKey("VERNO",   i, hh.version_no,
+                     "Version number");
+
+            WriteKey("PLLLCK",  i, hh.PLLLCK,
+                     "");
+/*
+            WriteKey("TRIGCRC", i, hh.trigger_crc,
+                     "Trigger CRC");
+
+            WriteKey("TRIGTYP", i, hh.trigger_type,
+                     "Trigger type");
+
+            WriteKey("TRIGID",  i, hh.trigger_id,
+                     "Trigger ID");
+
+            WriteKey("EVTCNTR", i, hh.fad_evt_counter,
+                     "FAD Event Counter");
+*/
+            WriteKey("REFCLK",  i, hh.REFCLK_frequency,
+                     "Reference Clock Frequency");
+
+            WriteKey("BOARDID", i, hh.board_id,
+                     "Board ID");
+
+            WriteKey("PHASESH", i, hh.adc_clock_phase_shift,
+                     "ADC clock phase shift");
+
+            //WriteKey("TRGGEN",  i, hh.number_of_triggers_to_generate,
+            //         "Number of triggers to generate");
+
+            WriteKey("PRESC",   i, hh.trigger_generator_prescaler,
+                    "Trigger generator prescaler");
+
+            WriteKey("DNA",     i, hh.DNA,       "DNA");
+            WriteKey("TIME",    i, hh.time,      "Time");
+            WriteKey("RUNNB",   i, hh.runnumber, "Run number");
+/*
+            for (int j=0;j<NTemp;j++)
+            {
+                str.str(""); str2.str("");
+                str << "DRS_T" << i << j;
+                str2 << "DRS temperature #" << i << " " << j;
+                WriteKey(str.str(), h->FADhead[i].drs_temperature[j], str2.str());
+            }
+*/
+            for (int j=0;j<NDAC;j++)
+                WriteKey("DAC", i*NDAC+j, hh.dac[j], "DAC");
+
+            //Last but not least, add header keys that will be updated when closing the file
+            WriteFooter(NULL);
+        }
+
+        return true;
+    }
+
+
+    int WriteColumns(size_t &start, size_t size, void *e)
+    {
+        int status = 0;
+        fits_write_tblbytes(fFile->fitsPointer(), fNumRows, start, size, reinterpret_cast<unsigned char*>(e), &status);
+        start += size;
+        return status;
+    }
+
+    // --------------------------------------------------------------------------
+    //
+    //! This writes one event to the file
+    //! @param e the pointer to the EVENT
+    //
+    virtual bool Write(EVENT *e)
+    {
+        //FIXME As discussed earlier, we do not swap the bytes yet.
+        fTable->makeThisCurrent();
+
+        //insert a new row
+        int status(0);
+        if (fits_insert_rows(fTable->fitsPointer(), fNumRows, 1, &status))
+        {
+            ostringstream str;
+            str << "Could not insert a row in fits file";
+            //TODO pipe this error message to the appropriate error stream
+        }
+        fNumRows++;
+
+        const int sh = sizeof(PEVNT_HEADER)-1;
+
+        // column size pointer
+        size_t col = 1;
+        if (!WriteColumns(col, sh + NPIX*fRoi*2, e))
+            return true;
+
+        //TODO output an error
+        return false;
+
+        /*
+        //write the data, chunk by chunk
+        //FIXME hard-coded size corresponds to current variables of the event, in bytes.
+        //FIXME no padding was taken into account. Because smallest member is 2 bytes, I don't think that this should be a problem.
+        const long sizeInBytesOfEventBeforePointers = 16;
+
+        long col = 1;
+        if (FitsWriteTblBytes(col, sizeInBytesOfEventBeforePointers, e))
+        {
+            //TODO output an error
+            return false;
+        }
+        if (FitsWriteTblBytes(col, NBOARDS*2, e->BoardTime))
+        {
+            //TODO output an error
+            return false;
+        }
+        if (FitsWriteTblBytes(col, NPIX*2, e->StartPix))
+        {
+            //TODO output an error
+            return false;
+        }
+        if (FitsWriteTblBytes(col, NTMARK*2, e->StartTM))
+        {
+            //TODO output an error
+            return false;
+        }
+        if (FitsWriteTblBytes(col, NPIX*fRoi*2, e->Adc_Data))
+        {
+            //TODO output an error
+            return false;
+        }
+        return true;*/
+    }
+
+    void WriteFooter(RUN_TAIL *rt)
+    {
+        //write final header keys
+        fTable->makeThisCurrent();
+
+        WriteKey("NBEVTOK",  rt ? rt->nEventsOk  : uint32_t(0),
+                 "How many events were written");
+
+        WriteKey("NBEVTREJ", rt ? rt->nEventsRej : uint32_t(0),
+                 "How many events were rejected by SW-trig");
+
+        WriteKey("NBEVTBAD", rt ? rt->nEventsBad : uint32_t(0),
+                 "How many events were rejected by Error");
+
+        //FIXME shouldn't we convert start and stop time to MjD first ?
+        //FIXME shouldn't we also add an MjD reference ?
+
+        WriteKey("TSTART",   rt ? rt->PCtime0    : uint32_t(0),
+                 "Time when first event received");
+
+        WriteKey("TSTOP",    rt ? rt->PCtimeX    : uint32_t(0),
+                 "Time when last event received");
+    }
+
+    // --------------------------------------------------------------------------
+    //
+    //! Closes the file, and before this it write the TAIL data
+    //! @param rt the pointer to the RUN_TAIL data structure
+    //
+    virtual bool Close(RUN_TAIL *rt = 0)
+    {
+        if (!fFile)
+            return false;
+
+        WriteFooter(rt);
+
+        delete fFile;
+        fFile = NULL;
+
+        return true;
+    }
+
+};
 
 class EventBuilderWrapper
@@ -212,38 +635,4 @@
     // -------------- Mapped event builder callbacks ------------------
 
-    class DataFileImp
-    {
-        uint32_t fRunId;
-
-    public:
-        DataFileImp(uint32_t id) : fRunId(id) { }
-
-        virtual bool Write() = 0;
-        virtual bool Close() = 0;
-
-        uint32_t GetRunId() const { return fRunId; }
-    };
-
-
-    class DataFileRaw : public DataFileImp
-    {
-    public:
-        DataFileRaw(uint32_t id) : DataFileImp(id)  { }
-        ~DataFileRaw() { Close(); }
-
-        virtual bool Write() { return true; }
-        virtual bool Close() { return true; }
-    };
-
-    class DataFileFits : public DataFileImp
-    {
-    public:
-        DataFileFits(uint32_t id) :DataFileImp(id) { }
-        ~DataFileFits() { Close(); }
-
-        virtual bool Write() { return true; }
-        virtual bool Close() { return true; }
-    };
-
     vector<DataFileImp*> fFiles;
 
@@ -259,10 +648,12 @@
     {
         // Check if file already exists...
-        DataFileImp *file = NULL;
+        DataFileImp *file = fFitsFormat ?
+            static_cast<DataFileImp*>(new DataFileFits(runid)) :
+            static_cast<DataFileImp*>(new DataFileRaw(runid));
+
         try
         {
-            file = fFitsFormat ?
-                static_cast<DataFileImp*>(new DataFileFits(runid)) :
-                static_cast<DataFileImp*>(new DataFileRaw(runid));
+            if (!file->OpenFile(runid, h))
+                return 0;
         }
         catch (const exception &e)
@@ -311,5 +702,5 @@
         cout << " tim=" << e->PCTime << endl;
 
-        if (!file->Write())
+        if (!file->Write(e))
             return -1;
 
@@ -333,5 +724,5 @@
     }
 
-    int runClose(FileHandle_t handler, RUN_TAIL *, size_t)
+    int runClose(FileHandle_t handler, RUN_TAIL *tail, size_t)
     {
         DataFileImp *file = reinterpret_cast<DataFileImp*>(handler);
@@ -357,5 +748,5 @@
         //fDimFiles.update();
 
-        const bool rc = file->Close();
+        const bool rc = file->Close(tail);
         if (!rc)
         {
@@ -395,6 +786,4 @@
     void factOut(int severity, int err, char *message)
     {
-        //replace(message, message+strlen(message), '\n', ' ');
-
         // FIXME: Make the output to the console stream thread-safe
         ostringstream str;
