/*
 * factfits.h
 *
 *  Created on: May 26, 2013
 *      Author: lyard
 */

#ifndef FACTFITS_H_
#define FACTFITS_H_

#include "zfits.h"


#ifndef __MARS__
namespace std
{
#else
using namespace std;
#endif

class factFits : public zfits
{
public:
    /*
     *  Default constructor
     */
    factFits(const string& fname,
             const string& tableName="",
             bool          force=false,
             bool          mightFail=false) : zfits(fname, tableName, force, mightFail),
                                              fOffsetCalibration(0),
                                              fStartCellOffset(0)
    {
        readDrsCalib(fname);
        GetStartCellOffset();
    }
    /*
     *  Alternative constructor
     */
    factFits(const string& fname,
             const string& fout,
             const string& tableName="",
             bool          force=false,
             bool          mightFail=false): zfits(fname, fout, tableName, force, mightFail),
                                             fOffsetCalibration(0),
                                             fStartCellOffset(0)
    {
        readDrsCalib(fname);
        GetStartCellOffset();
    }
    /*
     *  Default destrctor
     */
    ~factFits()
    {}
    /*
     *  Overload of zfits.h
     */
#if !defined(__MARS__) && !defined(__CINT__)
    virtual bool GetRow(size_t row, bool check=true)
#else
    virtual bool GetRowNum(size_t row, bool check=true)
#endif
    {
#if !defined(__MARS__) && !defined(__CINT__)
        const bool isGood = zfits::GetRow(row, check);
#else
        const bool isGood = zfits::GetRowNum(row, check);
#endif
        //row is loaded in internal memory. Should we un-apply the integer drs ?
        if (!isGood)                        return false;
        if (!fTable.isCompressed)           return true;
        if (fOffsetCalibration.size() == 0) return true;

        //now Drs un-Calibrate, if required
        const Pointers::iterator dtaIt = fPointers.find("Data");
        if (dtaIt == fPointers.end()) return true;

        //re-get the pointer to the data to access the offsets
        const uint8_t offset = (row*fTable.bytes_per_row)%4;
        const char* ptr = fBufferRow.data() + offset;

        //get column details for Data
        const Table::Columns::const_iterator dataColIt = fTable.cols.find("Data");
        if (dataColIt == fTable.cols.end()) return false;
        const Table::Column& dataColumn = dataColIt->second;
        const uint32_t numSlices = dataColumn.num/1440;

        //Drs un-calibrate !
        for (uint32_t i=0;i<1440;i++)
        {
            const int32_t thisStartCell = reinterpret_cast<const int16_t*>(ptr+fStartCellOffset)[i];
            for (uint32_t j=0;j<numSlices;j++)
                reinterpret_cast<int16_t*>(dtaIt->second)[numSlices*i+j] += fOffsetCalibration[1024*i + (thisStartCell+j)%1024];
        }
        return true;
    }

private:

    /*
     *  Read the Drs calibration data
     */
    void readDrsCalib(const string& fileName)
    {
        zfits calib(fileName, "DrsCalib", false, true);

        const bool isDrsCalibTable = (bool)calib;
        if (!isDrsCalibTable) return;
        // Check correct size and format of table
        if (calib.GetNumRows() != 1)
        {
#ifdef __EXCEPTIONS
            throw runtime_error("ERROR: DrsCalib table found, but not with one row as expected");
#else
            gLog << ___err___ << "ERROR: DrsCalib table found, but not with one row as expected" << endl;
            return;
#endif
        }
        if (calib.GetStr("TTYPE1") != "OffsetCalibration")
        {
#ifdef __EXCEPTIONS
            throw runtime_error("ERROR: DrsCalib table found, but first column is not the one expected");
#else
            gLog << ___err___ << "ERROR: DrsCalib table found, but first column is not the one expected" << endl;
            return;
#endif
        }
        if (calib.GetStr("TFORM1") != "1474560I")
        {
#ifdef __EXCEPTIONS
            throw runtime_error("ERROR: DrsCalib table found, but has wrong column format");
#else
            gLog << ___err___ << "ERROR: DrsCalib table found, but has wrong column format" << endl;
            return;
#endif
        }

        fOffsetCalibration.resize(1024*1440);
        calib.SetPtrAddress("OffsetCalibration",fOffsetCalibration.data());

        calib.GetNextRow();
    }

    /*
     *  Get the offset of the StartCell column
     */
    void GetStartCellOffset()
    {
        for (Table::Columns::const_iterator jt=fTable.cols.begin(); jt != fTable.cols.end(); jt++)
        {
            if (jt->first == "StartCellData")
            {
                fStartCellOffset = jt->second.offset;
                break;
            }
        }
    }

    vector<int16_t> fOffsetCalibration; ///< integer values of the drs calibration used for compression
    size_t          fStartCellOffset;   ///< offset of the StartCellData column in the uncompressed data

}; //class factfits

#ifndef __MARS__
}; //namespace std
#endif

#endif /* FACTFITS_H_ */
