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

/////////////////////////////////////////////////////////////////////////////
//
// MRawRunHeader
//
// Root storage container for the RUN HEADER information
//
//
//  RAW DATA FORMAT VERSION
//  =======================
//
//  Format Version 5:
//  -----------------
//   - now the sub millisecond information of the time is valid and decoded
//     which enhances the precision from 51.2us to 200ns
//
//  Format Version 4:
//  -----------------
//   - added support for pixels with negative IDs
//
//  Format Version 3:
//  -----------------
//   - ???
//
//  Format Version 2:
//  -----------------
//   - removed mjd from data
//   - added start time
//   - added stop  time
//
//
//  MRawRunHeader CLASS VERSION
//  ===========================
//
//  Class Version 3:
//  ----------------
//   - enhanced SourceName and ProjectName by one character, because
//     without telling us the guranteed trailing \0-character has
//     skipped
//
//  Class Version 2:
//  ----------------
//   - removed fMJD, fYear, fMonth, fDay
//   - added fRunStart
//   - added fRunStop
// 
//  Class Version 1:
//  ----------------
//   - first implementation
//
////////////////////////////////////////////////////////////////////////////

#include "MRawRunHeader.h"

#include <fstream>
#include <iomanip>

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

#include "MArrayS.h"

ClassImp(MRawRunHeader);

using namespace std;

const UShort_t MRawRunHeader::kMagicNumber      = 0xc0c0;
const Byte_t   MRawRunHeader::kMaxFormatVersion =      5;

// --------------------------------------------------------------------------
//
// Default constructor. Creates array which stores the pixel assignment.
//
//
MRawRunHeader::MRawRunHeader(const char *name, const char *title) : fPixAssignment(NULL)
{
    fName  = name  ? name  : "MRawRunHeader";
    fTitle = title ? title : "Raw Run Header Information";

    fPixAssignment = new MArrayS(0);

    fFormatVersion=0;
    fSoftVersion=0;
    fRunType=kRTNone;  // use 0xffff for invalidation, 0 means: Data run
    fRunNumber=0;
    memset(fProjectName, 0, 23);
    memset(fSourceName, 0, 13);
    fSourceEpochChar[0]=0;
    fSourceEpochDate=0;
    fNumCrates=0;
    fNumPixInCrate=0;
    fNumSamplesLoGain=0;
    fNumSamplesHiGain=0;
    fNumEvents=0;
}

// --------------------------------------------------------------------------
//
// Destructor. Deletes the 'pixel-assignment-array'
//
MRawRunHeader::~MRawRunHeader()
{
    delete fPixAssignment;
}

// --------------------------------------------------------------------------
//
// Swap the assignment of the pixels with hardware id id0 and id1
//
Bool_t MRawRunHeader::SwapAssignment(Short_t id0, Short_t id1)
{
    const Int_t n = fPixAssignment->GetSize();

    // Look-up-table
    UShort_t *lut = fPixAssignment->GetArray();

    // Search for one of the hardware indices to get exchanged
    int i;
    for (i=0; i<n; i++)
        if (lut[i]==id0 || lut[i]==id1)
            break;

    // Check if one of the two pixels were found
    if (i==n)
    {
        *fLog << err << "ERROR - Assignment of pixels with hardware ID " << id0 << " and " << id1;
        *fLog << " should be exchanged, but none were found." << endl;
        return kFALSE;
    }

    // Store first index
    const Int_t idx0 = i;

    // Search for the other hardware indices to get exchanged
    for (i++; i<n; i++)
        if (lut[i]==id0 || lut[i]==id1)
            break;

    // Check if the second pixel was found
    if (i==n)
    {
        *fLog << err << "ERROR - Assignment of pixels with hardware ID " << id0 << " and " << id1;
        *fLog << " should be exchanged, but only one was found." << endl;
        return kFALSE;
    }

    const Int_t idx1 = i;

    const Short_t p0 = lut[idx0];
    const Short_t p1 = lut[idx1];

    lut[idx0] = p1;
    lut[idx1] = p0;

    *fLog << inf << "Assignment of pixels with hardware ID " << id0 << " and " << id1 << " exchanged." << endl;

    return kTRUE;
}

// --------------------------------------------------------------------------
//
// This implements a fix of the pixel assignment before 25.9.2005
//
// From magic_online (the pixel numbers are hardware indices):
// --------------------------------------------------------------------------
//   From: Florian Goebel <fgoebel@mppmu.mpg.de>
//   Date: Sun Sep 25 2005 - 05:13:19 CEST
//   
//   [...]
//   - problem 2: pixels were swaped
//   - cause: opical fibers were wrongly connected to receiver board
//      554 <-> 559
//      555 <-> 558
//      556 <-> 557
//   - action: reconnect correctly
//   - result: ok now
//   - comment: I don't know when this error was introduced, so backward
//      correction of the data is difficult.
//      Fortunately the effect is not too large since the affected 6 pixels are
//      on the outermost edge of the camera
//      Since this board has special pixels the optical fibers have been
//      unplugged several times.
//   !!!!! Whenever you unplug and reconnect optical fibers make really !!!!!
//   !!!!! sure you connect them back correctly. !!!!!
//   !!!!! Always contact me before you do so and if you have any doubts !!!!!
//   !!!!! how to reconnect the fibers ask me. !!!!!
//      These swapped pixels have only been found by chance when doing the
//      flatfielding.
//   [...]
//
Bool_t MRawRunHeader::FixAssignment()
{
    if (fRunNumber<53300 || fRunNumber>68754)
        return kTRUE;

    if (!SwapAssignment(554, 559))
        return kFALSE;

    if (!SwapAssignment(555, 558))
        return kFALSE;

    if (!SwapAssignment(556, 557))
        return kFALSE;

    return kTRUE;
}

// --------------------------------------------------------------------------
//
// Read in one run header from the binary file
//
Bool_t MRawRunHeader::ReadEvt(istream& fin)
{
    //
    // read one RUN HEADER from the input stream
    //
    fMagicNumber = 0;

    fin.read((char*)&fMagicNumber, 2);          // Total=2

    //
    // check whether the the file has the right file type or not
    //
    if (fMagicNumber != kMagicNumber && fMagicNumber != kMagicNumber+1)
    {
        *fLog << err << "ERROR - Wrong Magic Number (0x" << hex << fMagicNumber << "): Not a Magic File!" << endl;
        return kFALSE;
    }

    if (fMagicNumber == kMagicNumber+1)
        *fLog << warn << "WARNING - This file maybe broken (0xc0c1) - DAQ didn't close it correctly!" << endl;

    Byte_t dummy[16];

    fin.read((char*)&fFormatVersion,    2);     // Total=4
    if (fFormatVersion>kMaxFormatVersion)
    {
        *fLog << err << "ERROR - File vormat V" << fFormatVersion << " not implemented!" << endl;
        return kFALSE;
    }

    fin.read((char*)&fSoftVersion,      2);     // Total=6
    fin.read((char*)&fRunType,          2);     // Total=8
    fin.read((char*)&fRunNumber,        4);     // Total=12
    fin.read((char*)&fProjectName,     22);     // Total=34
    fin.read((char*)&fSourceName,      12);     // Total=46
    fin.read((char*)dummy,              4); // was RA  (moved to tracking system)
    fin.read((char*)dummy,              4); // was DEC (moved to tracking system)
    fin.read((char*)&fSourceEpochChar,  2);     // Total=56
    fin.read((char*)&fSourceEpochDate,  2);     // Total=58
    if (fFormatVersion<2)                       // Total += 10
    {
        UShort_t y, m, d;
        fin.read((char*)dummy, 4); // Former fMJD[4],
        fin.read((char*)&y,    2); // Former fDateYear[2]
        fin.read((char*)&m,    2); // Former fDateMonth[2]
        fin.read((char*)&d,    2); // Former fDateDay[2]
        fRunStart.Set(y, m, d, 0, 0, 0, 0);
    }
    fin.read((char*)&fNumCrates,        2);     // Total=60
    fin.read((char*)&fNumPixInCrate,    2);     // Total=62
    fin.read((char*)&fNumSamplesLoGain, 2);     // Total=64
    fin.read((char*)&fNumSamplesHiGain, 2);     // Total=66
    fin.read((char*)&fNumEvents,        4);     // Total=70
    if (fFormatVersion>1)
    {
        fRunStart.ReadBinary(fin);              // Total += 7
        fRunStop.ReadBinary(fin);               // Total += 7
    }

    //
    // calculate size of array, create it and fill it
    //
    Int_t nPixel = fNumCrates*fNumPixInCrate;
    fPixAssignment->Set(nPixel);

    fin.read((char*)fPixAssignment->GetArray(), nPixel*2);
    fin.read((char*)&dummy, 16);

    return FixAssignment();
}

// --------------------------------------------------------------------------
//
// Return the run type as string ("Data", "Pedestal", ...), for example
// to print it as readable text.
//
const char *MRawRunHeader::GetRunTypeStr() const
{
    switch (fRunType)
    {
    case kRTData:
        return "Data";
    case kRTPedestal:
        return "Pedestal";
    case kRTCalibration:
        return "Calibration";
    case kRTPointRun:
        return "Point-Run";
    case kRTMonteCarlo:
        return "Monte Carlo";
    case kRTNone:
        return "<none>";
    default:
        return "<unknown>";
    }
}

// --------------------------------------------------------------------------
//
// print run header information on *fLog. The option 'header' supresses
// the pixel index translation table.
//
void MRawRunHeader::Print(Option_t *t) const
{
    *fLog << all << endl;
    *fLog << "MagicNumber:  0x" << hex << fMagicNumber << " - ";
    switch (fMagicNumber)
    {
    case kMagicNumber:   *fLog << "OK";               break;
    case kMagicNumber+1: *fLog << "File not closed!"; break;
    default:             *fLog << "Wrong!";           break;
    }
    *fLog << endl;
    *fLog << "Version:      " << dec << "Format=" << fFormatVersion << "  ";
    *fLog << "Software=" << fSoftVersion << endl;
    *fLog << "RunNumber:    " << fRunNumber << " (Type=" << GetRunTypeStr() << ")" << endl;
    *fLog << "ProjectName: '" << fProjectName << "'" << endl;
    *fLog << "Source:      '" << fSourceName << "' " << "  ";
    *fLog << fSourceEpochChar << dec << fSourceEpochDate << endl;
    *fLog << "Run Start:    " << fRunStart << endl;
    *fLog << "Run Stop:     " << fRunStop << endl;
    *fLog << "Crates:       " << fNumCrates << " x " << fNumPixInCrate << " Pixel/Crate = " << fNumCrates*fNumPixInCrate << " Pixel/Evt" << endl;
    *fLog << "Num Pixels:   " << GetNumNormalPixels() << " (normal) + " << GetNumSpecialPixels() << " (special) = " << GetNumConnectedPixels() << " (total)" << endl;
    *fLog << "Samples:      " << fNumSamplesHiGain << "/" << fNumSamplesLoGain << " (hi/lo) = " << (fNumSamplesLoGain+fNumSamplesHiGain) * fNumCrates * fNumPixInCrate /1024 << "kiB/Evt" << endl;
    *fLog << "Evt Counter:  " << fNumEvents << endl;

    if (TString(t).Contains("header", TString::kIgnoreCase))
        return;

    *fLog << inf << hex;
    for (int i=0; i<GetNumPixel(); i++)
        *fLog << setfill('0') << setw(3) << (*fPixAssignment)[i] << " ";

    *fLog << dec << endl;
}

// --------------------------------------------------------------------------
//
// Return the assigned pixel number for the given FADC channel
//
Short_t MRawRunHeader::GetPixAssignment(UShort_t i) const
{
    // FIXME: Do we need a range check here?
    return (Short_t)(*fPixAssignment)[i];
}

// --------------------------------------------------------------------------
//
// Return the number of pixel which are markes as connected in the
// pix assignment (!=0)
//
UShort_t MRawRunHeader::GetNumConnectedPixels() const
{
    const Int_t num = fPixAssignment->GetSize();

    UShort_t rc = 0;
    for (int i=0; i<num; i++)
    {
        if (GetPixAssignment(i)!=0)
            rc++;
    }
    return rc;
}

// --------------------------------------------------------------------------
//
// Return the number of pixel which are markes as connected and so-called
// 'normal' pixels in the pix assignment (>0)
//
UShort_t MRawRunHeader::GetNumNormalPixels() const
{
    const Int_t num = fPixAssignment->GetSize();

    UShort_t rc = 0;
    for (int i=0; i<num; i++)
    {
        if (GetPixAssignment(i)>0)
            rc++;
    }
    return rc;
}

// --------------------------------------------------------------------------
//
// Return the number of pixel which are markes as connected and so-called
// 'special' pixels in the pix assignment (<0)
//
UShort_t MRawRunHeader::GetNumSpecialPixels() const
{
    const Int_t num = fPixAssignment->GetSize();

    UShort_t rc = 0;
    for (int i=0; i<num; i++)
    {
        if (GetPixAssignment(i)<0)
            rc++;
    }
    return rc;
}

// --------------------------------------------------------------------------
//
// Return the maximum id which exists in the pix assignment
//
UShort_t MRawRunHeader::GetMaxPixId() const
{
    const Int_t num = fPixAssignment->GetSize();

    Short_t rc = 0;
    for (int i=0; i<num; i++)
        rc = TMath::Max(GetPixAssignment(i), rc);

    return rc;
}

// --------------------------------------------------------------------------
//
// Return minus th minimum id which exists in the pix assignment
//
UShort_t MRawRunHeader::GetMinPixId() const
{
    const Int_t num = fPixAssignment->GetSize();

    Short_t rc = 0;
    for (int i=0; i<num; i++)
        rc = TMath::Min(GetPixAssignment(i), rc);

    return (UShort_t)-rc;
}

// --------------------------------------------------------------------------
//
// Return the number of pixel in this event.
//
// WARNING: This is the number of pixels stored in this file which is
//          a multiple of the number of pixels per crate and in general
//          a number which is larger than the camera size!
//
//          To know the range of the pixel indices please use the geometry
//          container!
//
UShort_t MRawRunHeader::GetNumPixel() const
{
    return fPixAssignment->GetSize();
}

// --------------------------------------------------------------------------
//
// Returns absolute size in bytes of the run header as read from a raw file.
// This must be done _after_ the header is read, because the header doesn't
// have a fixed size (used in MRawSocketRead)
//
Int_t MRawRunHeader::GetNumTotalBytes() const
{
    switch (fFormatVersion)
    {
    case 1:
        return 80+fNumCrates*fNumPixInCrate*2+16;
    case 2:
        return 84+fNumCrates*fNumPixInCrate*2+16;
    }
    return 0;
}
