///////////////////////////////////////////////////////////////////////
// zdab read functions
//
// Author: Phil Harvey
//
// Revisions: 06/26/98 - PH Created
///////////////////////////////////////////////////////////////////////
#ifndef __PZdabFile_h__
#define __PZdabFile_h__

#include <RAT/Record_Info.hh>

#include <stdio.h>

/*
  __BIG_ENDIAN__ and __LITTLE_ENDIAN__ are defined in some gcc versions
  only, probably depending on the architecture. Try to use endian.h if
  the gcc way fails - endian.h also doesn't seem to be available on all
  platforms.
*/
#ifdef __BIG_ENDIAN__
#define WORDS_BIGENDIAN 1
#else /* __BIG_ENDIAN__ */
#ifdef __LITTLE_ENDIAN__
#undef WORDS_BIGENDIAN
#else
#ifdef BSD
#include <sys/endian.h>
#else
#include <endian.h>
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
#define WORDS_BIGENDIAN 1
#elif __BYTE_ORDER == __LITTLE_ENDIAN
#undef WORDS_BIGENDIAN
#else
#error "unable to determine endianess!"
#endif /* __BYTE_ORDER */
#endif /* __LITTLE_ENDIAN__ */
#endif /* __BIG_ENDIAN__ */

#ifndef WORDS_BIGENDIAN
#define SWAP_INT32(a,b) swap_bytes((char *)(a),(b), sizeof(int32_t))
#define SWAP_INT16(a,b) swap_bytes((char *)(a),(b), sizeof(int16_t))
#define SWAP_FLOAT(a,b) swap_bytes((char *)(a),(b), sizeof(float))
#define SWAP_DOUBLE(a,b) swap_bytes((char *)(a),(b), sizeof(double))
#define SWAP_PMT_RECORD(a) swap_bytes((char *)(a), sizeof(PmtEventRecord)/sizeof(int32_t), sizeof(int32_t))
#else
#warning Untested on big endian systems
#define SWAP_INT32(a,b)
#define SWAP_INT16(a,b)
#define SWAP_FLOAT(a,b)
#define SWAP_DOUBLE(a,b)
#define SWAP_PMT_RECORD(a)
#endif

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
/* definitions require for ZDAB/ZEBRA */

#define ZEBRA_BLOCKSIZE         3840        // maximum size of zebra record (32-bit words)

/* flag bits for steering block MPR[4] - PH 07/03/98 */
#define ZEBRA_EMERGENCY_STOP    0x80000000UL
#define ZEBRA_END_OF_RUN        0x40000000UL
#define ZEBRA_START_OF_RUN      0x20000000UL
#define ZEBRA_BLOCK_SIZE_MASK   0x00ffffffUL

#define ZEBRA_SIG0              0x0123cdefUL
#define ZEBRA_SIG1              0x80708070UL
#define ZEBRA_SIG2              0x4321abcdUL
#define ZEBRA_SIG3              0x80618061UL


typedef struct nZDAB{
    uint32_t next_bank;      // next bank
    uint32_t supp_bank;      // supp bank
    uint32_t supp_link;      // supp link
    uint32_t bank_number;    // numerical bank-id
    uint32_t bank_name;      // hollerith name bank-id
    uint32_t total_links;    // total number of links
    uint32_t struct_links;   // number of structural links
    uint32_t data_words;     // number of datawords
    uint32_t status;         // status

} nZDAB, *nZDABPtr;

#define NZDAB_WORD_SIZE     (sizeof(nZDAB) / sizeof(uint32_t))

typedef struct ZEBRA_ST {   // Steering clock
    uint32_t MPR[8];         //  Physical record control words
    // PH 07/03/98
    // 0 - 0x0123cdef
    // 1 - 0x80708070
    // 2 - 0x4321abcd
    // 3 - 0x80618061
    // 4 - zebra bank size in 32-bit words (usually 0xf00 = 3840)
    // 5 - bank number in file
    // 6 - offset in 32-bit words to the start of the control record in this bank
    // 7 - number of fast blocks to follow (don't contain steering blocks)
} ZEBRA_ST;

typedef struct CONTROL {
    uint32_t length;
    uint32_t recordtype;
} CONTROL, *CONTROLPtr;

typedef struct PILOT{
    uint32_t pilot0;
    uint32_t pilot1;
    uint32_t pilot2;
    uint32_t pilot3;
    uint32_t pilot4;
    uint32_t pilot5;
    uint32_t pilot6;
    uint32_t pilot7;
    uint32_t pilot8;
    uint32_t pilot9;
} PILOT, *PILOTPtr;

typedef struct MTC{
    uint32_t mtc0;
    uint32_t mtc1;
    uint32_t mtc2;
    uint32_t mtc3;
    uint32_t mtc4;
    uint32_t mtc5;
} MTC, *MTCPtr;

typedef  struct pilotHeader{
    CONTROL   control; //  2 uint32_t : Record Length Control
    PILOT     pilot;   // 10 uint32_t : Pilot (ZEBRA)
} pilotHeader, *pilotHeaderPtr;

class PackedCharArray {
public:
    PackedCharArray(char *data)      { mData = data; }

#ifndef WORDS_BIGENDIAN
    char    Get(int index)           { return(mData[index ^ 0x03]); }
    void    Set(int index, char val) { mData[index ^ 0x03] = val;   }
#else
    char    Get(int index)           { return(mData[index]); }
    void    Set(int index, char val) { mData[index] = val;   }
#endif

private:
    char    *mData;
};

//----------------------------------------------------------------------


/* class definition */
class PZdabFile {
public:
                    PZdabFile();
    virtual         ~PZdabFile();

    int             Init(FILE *inFile);
    void            Free();

    // return next nZDAB record from file
    nZDAB           * NextRecord();

    // return next specified data type from file
    PmtEventRecord  * NextPmt();
    uint32_t        * NextBank(uint32_t bank_name);

    // extract various records from nZDAB record
    PmtEventRecord  * GetPmtRecord(nZDAB *nzdabPtr);
    static uint32_t * GetBank(nZDAB *nzdabPtr, uint32_t bank_name=0);

    static uint32_t   GetSize(PmtEventRecord *pmtRecord);
    static uint32_t * GetExtendedData(PmtEventRecord *pmtRecord, int subType);
    static uint32_t * GetNcdData(PmtEventRecord *pmtRecord)
                          { return GetExtendedData(pmtRecord, SUB_TYPE_NCD); }

    static void       DumpHex(nZDAB *nzdabPtr, int numToPrint=16);

    static uint32_t   BankName(char *bank_name_string);
    static char     * BankNameString(uint32_t bank_name);

    static void       AddSubField(uint32_t **io_sub_header_pt,
                                  int sub_type, uint32_t numBytes);

protected:
    FILE * mFile;

private:
    uint32_t   mWordOffset;
    uint32_t   mBlockCount, mRecordCount;
    int        mBufferEmpty;
    uint32_t * mRecBuffer;
    uint32_t   mRecBuffsize;       // size of temporary ZDAB buffer
    uint32_t * mBuffPtr32;
    uint32_t   mBytesRead, mWordsTotal, mBytesTotal;
    uint32_t   mLastGTID;
    nZDAB   * mLastRecord;
};


int zdab_get_subrun(char *filename);
int zdab_set_subrun(char *filename, int subrun);
long zdab_get_run(char *filename);
long zdab_set_run(char *filename, long run);
void swap_bytes(char *valPt, int num, int size);
void swap_PmtRecord(aPmtEventRecord *aPmtRecord);

// PmtEventRecord utility functions
double  get50MHzTime(PmtEventRecord *pmtRecord);
double  get50MHzTimeMax();
int     isOrphan(PmtEventRecord *pmtRecord);



#endif // __PZdabFile_h__