#include "AdvEvCountCheck.h"
#include <TString.h>

struct spdWord_t {
  Short_t w[1];                            // Data word itself
  Short_t GetIdentifier(Int_t i){
    return ( (w[i] & 0xc000)>>14 );
  };
  Short_t GetHalfStaveNumber(Int_t i){
    return ( (w[i] & 0x3800)>>11);
  }
  Short_t GetEventCounter(Int_t i){
    return ( (w[i] & 0x07f0)>>4 );
  };
  Short_t GetChipNumber(Int_t i){
    return ( w[i] & 0x000f );
  }
  Short_t GetRow(Int_t i){
    return ( (w[i] & 0x1fe0)>>5 );
  }
  Short_t GetColumn(Int_t i){
    return ( w[i] & 0x001f );
  }
  Short_t GetChipHitCounter(Int_t i){
    return ( w[i] & 0x1fff );
  }
  Short_t GetBusyViolationFlag(Int_t i){
    return ( (w[i] & 0x1000)>>12 );
  }
  Short_t GetIdleViolationFlag(Int_t i){
    return ( (w[i] & 0x0800)>>11 );
  }
};

AdvEvCountCheck::AdvEvCountCheck() {
  Reset();
  fDumpAll=kFALSE;
}
//________________________________________________________________________________
AdvEvCountCheck::~AdvEvCountCheck() {}
//________________________________________________________________________________
void AdvEvCountCheck::Reset() {
  nrProcessedEvents=0;
  problemEventZero=kFALSE;
  for (UInt_t ieq=0; ieq<20; ieq++) {
    fEqPresent[ieq] = kFALSE;
    for (UInt_t ihs=0; ihs<6; ihs++) {
      fHSPresent[ieq][ihs] = kFALSE;
      for (UInt_t ichip=0; ichip<10; ichip++) {
	evCounter[ieq][ihs][ichip] = 9999;
	evCounterPrevious[ieq][ihs][ichip] = 9999;
      }
    }
  }
}
//________________________________________________________________________________
void AdvEvCountCheck::NewChipHeader(UInt_t eq, UInt_t hs, UInt_t chip, Short_t evCo, UInt_t eventNum) {
  evCounter[eq][hs][chip] = evCo;
  evNum[eq][hs][chip] = eventNum;
  fEqPresent[eq] = kTRUE;
  fHSPresent[eq][hs] = kTRUE;
}
//________________________________________________________________________________
void AdvEvCountCheck::ProcessPayload(Char_t *tempChar, UInt_t eq, UInt_t eventNum) {
  if (eq>20) {
    printf("Router nr (%d) out of bounds.\n",eq);
    return;
  }
  Char_t *temp = tempChar;

  if (temp==NULL) return;

  fEqPresent[eq] = kTRUE;

  Int_t chip=0; //, eventCnt, bv, iv; // header contents
  Int_t oldchip=-1;
  Int_t hs=0;
  Int_t colAddr, rowAddr;           // pixel hit contents
  Int_t chipHitCount;               // trailer content
  Int_t i=0;  // 16bits-word pointer
  Int_t j=0;
  Int_t dataWordCounter=0;
  Int_t numberOfHeadersFound=0;
  Int_t numberOfTrailersFound=0;
  Int_t previousMode = -1;

  // Read Eq+CDH Headers... **************************************************************
  //  temp-=60;
  Int_t *intTemp = (Int_t *)temp;
  payloadSize=*intTemp;


  temp+=40; // temp points to the start of the block attributes word
  payloadSize-=40;
  Int_t *word = (Int_t*)temp;
  Int_t howManyHalfStaves=0;
  Bool_t halfStavePresent[6];
  Bool_t calHeadPresent = kFALSE;
  for (Int_t i=0 ; i<6 ; i++) {
    if ( 1 == ((((*word)>>(24+i)))&0x00000001) ) {
      halfStavePresent[i] = kFALSE;
      fHSPresent[eq][i] = kFALSE;
    }
    else {
      halfStavePresent[i] = kTRUE;
      fHSPresent[eq][i] = kTRUE;
      howManyHalfStaves++;
    }
  }
  if ( 1 == ((((*word)>>(30)))&0x00000001) ) {
    calHeadPresent = kTRUE;
  }

  // Read Calib Header *******************************************************************
  temp+=20; // temp points the first word of the payload
  payloadSize-=20;
  if (calHeadPresent) {
    Int_t *intTemp = (Int_t *)temp;
    Int_t headerLen=*intTemp; // Header length
    headerLen*=4; // temporary fix!!!
    if (headerLen>=0 && headerLen<payloadSize-4) {
      temp+=headerLen; // step past the calibration header
      payloadSize-=headerLen;
      temp+=4;          // one step extra for the header lenght word itself
      payloadSize-=4;
    }
    else {
      if (headerLen==payloadSize-4) {
	return;
      }
      else {
	return;
      }
    }
  }

  // Read Payload  ***********************************************************************

  //  Int_t *firstword = (Int_t*)temp;
  //  if (eventNum<10)   printf("firstword = %x \n",*firstword);

  struct spdWord_t *raw;
  raw = (struct spdWord_t*)temp;

  if (payloadSize==0) {
    TString errMess = Form("Event %d : Eq %d without payload.",eventNum,eq);
    printf("%s\n",errMess.Data());
  }

  while (j<(payloadSize)/2) {  // move on the physical memory

    // Swap the pairs of 16-bit words .../Henrik T
    if (j%2==0) i=j+1;
    else i=j-1;

    Int_t id = raw->GetIdentifier(i);

    switch (id) {

    case 0:   // trailer word
      numberOfTrailersFound++;
      chipHitCount = raw->GetChipHitCounter(i);
      previousMode=0;
      break;
	
    case 1:   // header word
      numberOfHeadersFound++;
      hs = raw->GetHalfStaveNumber(i);
      dataWordCounter = 0;

      if (numberOfHeadersFound>1) {
	oldchip = chip;
      }
      chip = raw->GetChipNumber(i);

      NewChipHeader(eq,hs,chip,raw->GetEventCounter(i),eventNum);

      previousMode=1;
      break;
	
    case 2:   // data word
      dataWordCounter++;
      rowAddr = raw->GetRow(i);
      colAddr = raw->GetColumn(i);
      previousMode=2;
      break;

    case 3:   // fill word
      previousMode=3;
      break;

    }

    j++;
  }


}
//________________________________________________________________________________
void AdvEvCountCheck::PrepareNewEvent() {
  for (UInt_t ieq=0; ieq<20; ieq++) {
    for (UInt_t ihs=0; ihs<6; ihs++) {
      for (UInt_t ichip=0; ichip<10; ichip++) {
	evNumPrevious[ieq][ihs][ichip] = evNum[ieq][ihs][ichip];
	evCounterPrevious[ieq][ihs][ichip] = evCounter[ieq][ihs][ichip];
      }
    }
  }
}
//________________________________________________________________________________
void AdvEvCountCheck::CompareEvCount(UInt_t eventNum) {
  nrProcessedEvents++;
  if (nrProcessedEvents==1) {
    return CompareEvCountZero(eventNum);
  }
  // check synchronization of event counters
  Bool_t problemSynch = kFALSE;
  Bool_t problemJump = kFALSE;
  Int_t diff = 9999;

  for (UInt_t ieq=0; ieq<20; ieq++) {
      for (UInt_t ihs=0; ihs<6; ihs++) {
	  for (UInt_t ichip=0; ichip<10; ichip++) {
	    problemEvent[ieq][ihs][ichip] = kFALSE;
	    problemJumpEvent[ieq][ihs][ichip] = kFALSE;
	  }
      }
  }

  for (UInt_t ieq=0; ieq<20; ieq++) {
    if (fEqPresent[ieq]) {
      for (UInt_t ihs=0; ihs<6; ihs++) {
	if (fHSPresent[ieq][ihs]) {
	  for (UInt_t ichip=0; ichip<10; ichip++) {
	    if (evCounterPrevious[ieq][ihs][ichip] != 9999) {
	      Int_t thisDiff = evCounter[ieq][ihs][ichip] - evCounterPrevious[ieq][ihs][ichip] - evNum[ieq][ihs][ichip] + evNumPrevious[ieq][ihs][ichip];
	      while (thisDiff<0) thisDiff+=128;
	      if (thisDiff!=0) {
		problemJump = kTRUE;
		problemEvent[ieq][ihs][ichip] = kTRUE;
	      }
	      if (thisDiff!=diff && diff!=9999) {
		problemSynch = kTRUE;
	      }
	      diff = thisDiff;
	    }
	  }
	}
      }
    }
  }
  if (problemSynch) {
    TString errMess = Form("Event %d : Event counter problem.",eventNum);
    printf("%s\n",errMess.Data());
    if (!fDumpAll) DumpEvCount(kTRUE);
  }
  else if (problemJump) {
    if (diff>64) diff-=128;
    TString errMess = Form("Event %d : Event counter jump (%d steps).",eventNum,diff+1);
    //    printf("%s\n",errMess.Data());
    // don't flag this as bad event, but as jump event:
    for (UInt_t ieq=0; ieq<20; ieq++) {
      for (UInt_t ihs=0; ihs<6; ihs++) {
	for (UInt_t ichip=0; ichip<10; ichip++) {
	  problemEvent[ieq][ihs][ichip] = kFALSE;
	  problemJumpEvent[ieq][ihs][ichip] = kTRUE;
	}
      }
    }
  }

  if (fDumpAll) {
    printf("Event %d DUMP:\n",eventNum);
    DumpEvCount(kTRUE);
  }

  PrepareNewEvent();
}
//________________________________________________________________________________
void AdvEvCountCheck::CompareEvCountZero(UInt_t eventNum) {
  // check synchronization of event counters
  Bool_t problem = kFALSE;
  UShort_t compareCount = 9999;
  for (UInt_t ieq=0; ieq<20; ieq++) {
    if (fEqPresent[ieq]) {
      for (UInt_t ihs=0; ihs<6; ihs++) {
	if (fHSPresent[ieq][ihs]) {
	  for (UInt_t ichip=0; ichip<10; ichip++) {
	    if (evCounter[ieq][ihs][ichip]!=9999 && evCounter[ieq][ihs][ichip]!=compareCount && compareCount!=9999) {
	      problem = kTRUE;
	      problemEventZero = kTRUE;
	    }
	    compareCount = evCounter[ieq][ihs][ichip];
	  }
	}
      }
    }
  }
  if (problem) {
    TString errMess = Form("Event %d : Event counters not synchronized.",eventNum);
    printf("%s\n",errMess.Data());
    if (!fDumpAll) DumpEvCount();
  }

  if (fDumpAll) {
    printf("Event %d DUMP:\n",eventNum);
    DumpEvCount();
  }

  PrepareNewEvent();
}
//________________________________________________________________________________
void AdvEvCountCheck::DumpEvCount(Bool_t dispExp) {
  for (UInt_t ieq=0; ieq<20; ieq++) {
    if (fEqPresent[ieq]) {
      printf("Eq %2d",ieq);
      for (UInt_t ihs=0; ihs<6; ihs++) {
	printf(" [%d] ",ihs);
	UShort_t evC = 9999;
	Bool_t diffChip=kFALSE;
	for (UInt_t ichip=0; ichip<10; ichip++) {
	  if (evC!=evCounter[ieq][ihs][ichip] && evC!=9999) {
	    diffChip=kTRUE;
	  }
	  evC = evCounter[ieq][ihs][ichip];
	}
	if (fHSPresent[ieq][ihs]) {
	  if (dispExp) {
	    if (diffChip) printf(" in (   )");
	    else {
	      Int_t parenthesis = evCounterPrevious[ieq][ihs][0]+evNum[ieq][ihs][0]-evNumPrevious[ieq][ihs][0];
	      while (parenthesis>=128) parenthesis-=128;
	      while (parenthesis<0) parenthesis+=128;
	      if (evC!=parenthesis) {
		printf("%3d (%3d)",evC,parenthesis);
	      }
	      else {
		printf("%3d      ",evC);
	      }
	    }
	  }
	  else {
	    if (diffChip) printf(" in ");
	    else printf("%3d ",evC);
	  }
	}
	else {
	  if (dispExp) {
	    printf("         ");
	  }
	  else {
	    printf("    ");
	  }
	}
      }
      printf("\n");
    }
  }
}
//________________________________________________________________________________
Bool_t AdvEvCountCheck::IsErrorEventZero() {
  if (nrProcessedEvents==1) {
    return problemEventZero;
  }
  return kFALSE;
}
//________________________________________________________________________________
Bool_t AdvEvCountCheck::IsErrorEvent(UInt_t eq, UInt_t hs, UInt_t chip) {
  if (nrProcessedEvents>1) {
    if (eq<20 && hs<6 && chip<10) {
      if (fEqPresent[eq] && fHSPresent[eq][hs]) {
	return problemEvent[eq][hs][chip];
      }
    }
  }
  return kFALSE;
}
//________________________________________________________________________________
Bool_t AdvEvCountCheck::IsJumpErrorEvent(UInt_t eq, UInt_t hs, UInt_t chip) {
  if (nrProcessedEvents>1) {
    if (eq<20 && hs<6 && chip<10) {
      if (fEqPresent[eq] && fHSPresent[eq][hs]) {
	return problemJumpEvent[eq][hs][chip];
      }
    }
  }
  return kFALSE;
}
//________________________________________________________________________________
void AdvEvCountCheck::SetDumpAll(Bool_t set) {
  fDumpAll = set;
}
//________________________________________________________________________________
void AdvEvCountCheck::SetEqPresent(UInt_t eq, Bool_t set) {
  if (eq<20) {
    fEqPresent[eq] = set;
  }
}
//________________________________________________________________________________
void AdvEvCountCheck::SetHSPresent(UInt_t eq, UInt_t hs, Bool_t set) {
  if (eq<20 && hs<6) {
    fHSPresent[eq][hs] = set;
  }
}
