#ifndef PAMVMCRAWMGR_H
#define PAMVMCRAWMGR_H
#include "CRC.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <TObject.h>
#include "TString.h"

using namespace std;

typedef vector<char>     PamVMCBuffer;
typedef vector<UChar_t>  UCBuffer;
typedef vector<UShort_t> USBuffer;

class PamVMCRawMgr: public TObject {

 private:

  static PamVMCRawMgr * frm;

  UInt_t fCounter;
  UInt_t fCounterPhys;
  UInt_t fOBT;

  TString fFilename;
  std::ofstream fFile;


  UInt_t fLen;

  UCBuffer fEvtDataPSCU;   //event
  UCBuffer fEvtDataPadding;
  UInt_t fEvtPadding;

  //++++++++++//
  //  BUFFER  //
  //++++++++++//

  PamVMCBuffer* fbuffer;

 protected:
  PamVMCRawMgr(const char *filename) {
      fFilename=TString(filename)+".pam";
      //fFilename = filename; //to be redefined in options
      fCounter=fCounterPhys=fOBT=0;
      fbuffer = new PamVMCBuffer(0);
      fFile.open(fFilename.Data(),ios::out | ios::binary);
  };

 public:
  
  ~PamVMCRawMgr(){ 

    delete fbuffer;
    //delete fFilename; 
   
  }

  static PamVMCRawMgr * Instance(const char *filename = 0);

  UInt_t GetCounter(){ return fCounter; }

  void AddCounter(){ fCounter++; }

  void SetCounter(UInt_t count){ fCounter = count; }

  void AddCounterPhys(){ fCounterPhys++; }

  UInt_t GetCounterPhys(){ return fCounterPhys; }
  
  void SetCounterPhys(UInt_t phcount){ fCounterPhys = phcount; }

  UInt_t GetOBT(){ return fOBT; }

  void SetOBT(UInt_t obt){ fOBT = obt; }

  UInt_t GetLen(){ return fLen; }

  void SetLen(UInt_t len){ fLen = len; }

  void WritePSCU(const UCBuffer* b){ CopyUCharToBuff(b); }
  
  void WritePadding(const UCBuffer* b){ CopyUCharToBuff(b); }

  void WriteEvent(){
    fEvtPadding = 0;
    //fLen=fbuffer->size();
    //AddPadding(fEvtPadding,&fEvtDataPadding);
    UInt_t length = fbuffer->size()+fEvtPadding; // DATA+Padding size;
    DigitizePSCU(length, 0x10, &fEvtDataPSCU);
    
    //cout<<"PADDING "<<fEvtPadding<<" bytes"<<endl;
    UCBuffer::const_iterator p = fEvtDataPSCU.begin();
    while( p!=fEvtDataPSCU.end() )
      {
	char t = char(*p);
	fFile.write(&t,sizeof(char));
	p++;
      }
    fEvtDataPSCU.clear();

    WriteToFile(); //WRITE BUFFER CONTENT TO FILE
    
    if(fEvtPadding){
      UCBuffer::const_iterator p = fEvtDataPadding.begin();
      while( p!=fEvtDataPadding.end() )
	{
	  char t = char(*p);
	  fFile.write(&t,sizeof(char));
	  p++;	  
	}    
      fEvtDataPadding.clear();
    }
  }

  void WriteToFile(){
    cout<<"BUFFER SIZE: "<<fbuffer->size()<<endl;
    PamVMCBuffer::const_iterator p = fbuffer->begin();
    while( p!=fbuffer->end() )
      {
	fFile.write(&(*p),sizeof(char));
	p++;
      }
    fbuffer->clear(); //cleaning MAIN buffer
  }

  void FinishRun(){
    WriteToFile();
    Int_t end = EOF;
    char t = char(end);
    fFile.write(&t, sizeof(char));
    fFile.close();
  }

  void CopyUCharToBuff (const UCBuffer * b){
    UCBuffer::const_iterator p = b->begin();
    cout<<"size of UChar DATA:"<<b->size()<<endl;
    while( p!=b->end() )
      {
	//cout<<Int_t(*p)<<endl;
	fbuffer->push_back(char(*p));
	p++;
      }       
  }


  void CopyUShortToBuff (const USBuffer * b){
    USBuffer::const_iterator p = b->begin();
    cout<<"size of UShort DATA:"<<b->size()<<endl;
    UShort_t Data;
    UChar_t tmp[2];
    while( p!=b->end() )
      {
	Data=(*p);
	memcpy(tmp,&Data,sizeof(UShort_t));
	//cout<<Int_t(*p)<<" "<<hex<<tmp[1]<<" "<<hex<<tmp[0]<<endl;
	fbuffer->push_back(char(tmp[1]));
	fbuffer->push_back(char(tmp[0]));
	p++;
      }       
  }


void AddPadding(UInt_t & pad, UCBuffer * b){
  
  Float_t pd0 = (fLen+16)/32.;
  Float_t pd1 = pd0 - (Float_t)Int_t(pd0);
  Float_t padfrac = 32. - pd1 * 32.;
  
  UInt_t padbytes = (UInt_t)padfrac;
  b->clear();
  if ( padbytes > 0 && padbytes < 32 ){
    //
    // here the padding length
    //
    pad = padbytes+32;
    //
    // random padding bytes
    //
    UShort_t Data;
    UChar_t tmp[2];
    for (Int_t ur=0; ur<16; ur++){
      Data=(UShort_t)rand();
      memcpy(tmp,&Data,sizeof(UShort_t));
      //cout<<"DATA"<<hex<<Data<<endl;
      //tmp[1] should be first (swapping bytes)
      //b->push_back(tmp[1]);
      //b->push_back(tmp[0]);
    };
  };

}


void  DigitizePSCU(UInt_t length, UChar_t type, UCBuffer * b){
  //
  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);

  cout<<"Digitizer::DigitizePSCU...  OK " <<endl;

  b->clear();

  for (Int_t i=0; i<16; i++)  b->push_back(buff[i]);
}


  void PrintBinaryUns(unsigned char val){
    for(Int_t i =7; i>=0; i--){
      if(val & (1<<i))
	cout<<"1";
      else
	cout<<"0";
    };
    cout<<endl;    
  }

 
};
#endif
