#include <sstream>
#include <fstream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "Riostream.h"
#include "TFile.h"
#include "TDirectory.h"
#include "TTree.h"
#include "TLeafI.h"
#include "TH1.h"
#include "TH2.h"
#include "TF1.h"
#include "TMath.h"
#include "TRandom.h"
#include "TSQLServer.h"
#include "TSystem.h"
#include "CalibTrk1Event.h"
#include "CalibTrk2Event.h"
//
#include "Digitizer.h"
#include "CRC.h"
//
#include <PamelaRun.h>
#include <physics/calorimeter/CalorimeterEvent.h>
#include <CalibCalPedEvent.h>
#include "GLTables.h"

void Digitizer::DigitizeRunHeader(){
  const Int_t lenRH = fRunHeaderbuffer*2;
  UChar_t buffRH[lenRH];
  UShort_t buffPSCU[8];
  UChar_t *p;
  p=buffRH;

  // header: 16 bytes
  DigitizePSCU(fRunHeaderbuffer*2,0x20,buffPSCU);
  memcpy(p,buffPSCU,16*sizeof(UChar_t));
  p+=16;

  // time stamp (uint32): 0x82569c97
  *(p++) = 0x82;
  *(p++) = 0x56;
  *(p++) = 0x9C;
  *(p++) = 0x97;

  // acq_setting_mode (uint8)
  *(p++) = 2; 

  // obt (uint32)
  ULong64_t obt = fOBT + 30LL;
  while ( obt > 4294967295LL )
    obt -= 4294967295LL;
  fOBT = (UInt_t)obt;
  //
  *(p++) = (UChar_t)(fOBT >> 24);
  *(p++) = (UChar_t)(fOBT >> 16);
  *(p++) = (UChar_t)(fOBT >> 8);
  *(p++) = (UChar_t)fOBT;

  // last time_sync_info (uint32)  (from file 000_001_00110)
  *(p++) = 0x00; 
  *(p++) = 0x08; 
  *(p++) = 0x68; 
  *(p++) = 0xEF; 

  // fav. working schedule (uint8)
  *(p++) = 0; 

  // eff. working schedule (uint8)
  *(p++) = 0; 

  // trigger_mode_A (uint32)
  *(p++) = 0; 
  *(p++) = 0; 
  *(p++) = 0; 
  *(p++) = 0x01; 

  // trigger_mode_B (uint32)
  *(p++) = 0; 
  *(p++) = 0; 
  *(p++) = 0; 
  *(p++) = 0x03; 

  // acq_after_calib (0,1) (uint8)
  *(p++) = 0; 

  // trk_calib_used (uint32)
  *(p++) = 0; 
  *(p++) = 0; 
  *(p++) = 0; 
  *(p++) = 0x68; 

  // acq_build_info (4 zero bits + 28 1's) (uint32)
  *(p++) = 0x3F; 
  *(p++) = 0xFF; 
  *(p++) = 0xFF; 
  *(p++) = 0xFF; 

  // acq_var_info (11 bits) (uint16)
  *(p++) = 0x23;
  *(p++) = 0x7F;

  // cal_dsp_mask (uint8)
  *(p++) = 0;

  // crc (uint16)
  UShort_t crcRH = (UShort_t)CM_Compute_CRC16((UINT16)0, (BYTE*)&buffRH, (UINT32)(fRunHeaderbuffer*2-2));
  *(p++) = (UChar_t)(crcRH << 8);
  *p = (UChar_t)crcRH;

  memcpy(fDataRunHeader,buffRH,fRunHeaderbuffer*sizeof(UShort_t));
};

void Digitizer::DigitizeRunTrailer(){
  UChar_t buffRT[fRunTrailerbuffer*2];
  UShort_t buffPSCU[8];
  UChar_t *p;
  p=buffRT;

  // header: 16 bytes
  DigitizePSCU(fRunHeaderbuffer*2,0x21,buffPSCU);
  memcpy(p,buffPSCU,16*sizeof(UChar_t));
  p+=16;

  // pkt_counter (uint32)
  fCounterPhys++;
  fCounter++;
  while ( fCounterPhys > 16777215 )
    fCounterPhys -= 16777215;
  //
  *(p++) = (UChar_t)(fCounterPhys >> 24);
  *(p++) = (UChar_t)(fCounterPhys >> 16);
  *(p++) = (UChar_t)(fCounterPhys >> 8);
  *(p++) = (UChar_t)fCounterPhys;

  // pkt_readyCounter: valid packets in the run (uint32)
  *(p++) = 0;
  *(p++) = 0;
  *(p++) = 0;
  *(p++) = 0;

  // obt (uint32)
  ULong64_t obt = fOBT + 30LL;
  while ( obt > 4294967295LL )
    obt -= 4294967295LL;
  fOBT = (UInt_t)obt;
  //
  *(p++) = (UChar_t)(fOBT >> 24);
  *(p++) = (UChar_t)(fOBT >> 16);
  *(p++) = (UChar_t)(fOBT >> 8);
  *(p++) = (UChar_t)fOBT;

  // last time_sync_info (uint32)
  *(p++) = 0;
  *(p++) = 0;
  *(p++) = 0;
  *(p++) = 0;

  // crc (uint16)
  UShort_t crcRT = (UShort_t)CM_Compute_CRC16((UINT16)0, (BYTE*)(buffRT), (UINT32)(fRunTrailerbuffer*2-2));
  *(p++) = (UChar_t)(crcRT << 8);
  *p = (UChar_t)crcRT;

  memcpy(fDataRunTrailer,buffRT,fRunTrailerbuffer*sizeof(UShort_t));
};

void Digitizer::AddPadding(){
  //
  Float_t pd0 = (fLen+16)/64.;
  Float_t pd1 = pd0 - (Float_t)int(pd0);
  Float_t padfrac = 64. - pd1 * 64.;
  //
  UInt_t padbytes = (UInt_t)padfrac;
  if ( padbytes > 0 && padbytes < 64 ){
    //
    // here the padding length
    //
    fPadding = padbytes+64;
    //
    // random padding bytes
    //
    for (Int_t ur=0; ur<32; ur++){
      fDataPadding[ur] = (UShort_t)rand();
    };
  };
};


void Digitizer::DigitizePSCU(UInt_t length, UChar_t type, UShort_t *pPSCU) {
  //
  UChar_t buff[16];
  //
  // CPU signature
  //  
  buff[0] = 0xFA;
  buff[1] = 0xFE;
  buff[2] = 0xDE;
  //
  // packet type (twice)
  //
  buff[3] = type;
  buff[4] = type;
  //
  // counter
  //
  fCounter++;
  while ( fCounter > 16777215 ){
    fCounter -= 16777215;
  };
  //
  buff[5] = (UChar_t)(fCounter >> 16);
  buff[6] = (UChar_t)(fCounter >> 8);
  buff[7] = (UChar_t)fCounter;
  //
  // on board time
  //
  ULong64_t obt = fOBT + 30LL;
  //
  while ( obt > 4294967295LL ){
    obt -= 4294967295LL;
  };
  fOBT = (UInt_t)obt;
  //
  buff[8] = (UChar_t)(fOBT >> 24);
  buff[9] = (UChar_t)(fOBT >> 16);
  buff[10] = (UChar_t)(fOBT >> 8);
  buff[11] = (UChar_t)fOBT;
  //
  // Packet length
  //
  fLen = length;
  //
  buff[12] = (UChar_t)(fLen >> 16);
  buff[13] = (UChar_t)(fLen >> 8);
  buff[14] = (UChar_t)fLen;
  //
  // CPU header CRC
  //
  buff[15] = (BYTE)CM_Compute_CRC16((UINT16)0, (BYTE*)&buff, (UINT32)15);
  //
  //memcpy(fDataPSCU,buff,16*sizeof(UChar_t));
  memcpy(pPSCU,buff,16*sizeof(UChar_t));
  //
};