#include "HenrikClusterFinderSPD.h"
#include "AliITSRawStreamSPD.h"
#include <string.h>

HenrikClusterFinderSPD::HenrikClusterFinderSPD() : 
  fNhitsLeft(0),fOldModule(-1),
  fClusterTypeFlag(kTRUE),fFindClusterType(kFALSE),fClusterTypeOrigCol(0),fClusterTypeOrigRow(0),
  fColSum(0),fRowSum(0)
{}
//___________________________________________________________________________________
HenrikClusterFinderSPD::~HenrikClusterFinderSPD() {}
//___________________________________________________________________________________
void HenrikClusterFinderSPD::StartEvent() {
  NewEvent();
}
//___________________________________________________________________________________
Int_t HenrikClusterFinderSPD::ProcessHitOnline(UInt_t eq, UInt_t hs, UInt_t chip, UInt_t col, UInt_t row) {
  UInt_t mod,colM,rowM;
  if (AliITSRawStreamSPD::OnlineToOffline(eq,hs,chip,col,row,mod,colM,rowM)) {
    return ProcessHit(mod,colM,rowM);
  }
  else {
    printf("ERROR: HenrikClusterFinderSPD::ProcessHitOnline: Out of bounds: eq,hs,chip,col,row = %d,%d,%d,%d,%d\n",eq,hs,chip,col,row);
    return 1;
  }
}
//___________________________________________________________________________________
Int_t HenrikClusterFinderSPD::ProcessHit(UInt_t mod, UInt_t colM, UInt_t rowM) {
  if (mod>=240 || colM>=160 || rowM>=256) {
    printf("ERROR: HenrikClusterFinderSPD::ProcessHit: Out of bounds: mod,colM,rowM = %d,%d,%d\n",mod,colM,rowM);
    return 1;
  }

  // do we need to perform clustering on previous module?
  if (fOldModule!=-1 && (Int_t)mod!=fOldModule) {
    DoModuleClustering(fOldModule);
    NewModule();
  }
  fOldModule=mod;

  // add hit
  fHitCol[fNhitsLeft]=colM;
  fHitRow[fNhitsLeft]=rowM;
  fHits[colM][rowM]=kTRUE;
  fNhitsLeft++;

  return 0;
}
//___________________________________________________________________________________
void HenrikClusterFinderSPD::FinishEvent() {
  if (fNhitsLeft>0) {
    DoModuleClustering(fOldModule);
  }
}
//___________________________________________________________________________________
UInt_t HenrikClusterFinderSPD::GetClusterCount(UInt_t mod) {
  if (mod>=240) {
    printf("ERROR: HenrikClusterFinderSPD::GetClusterCount: Module out of bounds: mod = %d\n",mod);
    return 0;
  }
  return fClusterList[mod].GetNrEntries();
}
//___________________________________________________________________________________
Float_t HenrikClusterFinderSPD::GetClusterMeanCol(UInt_t mod, UInt_t index) {
  if (mod>=240) {
    printf("ERROR: HenrikClusterFinderSPD::GetClusterMeanCol: Module out of bounds: mod = %d\n",mod);
    return 0;
  }
  return fClusterList[mod].GetColIndex(index);
}
//___________________________________________________________________________________
Float_t HenrikClusterFinderSPD::GetClusterMeanRow(UInt_t mod, UInt_t index) {
  if (mod>=240) {
    printf("ERROR: HenrikClusterFinderSPD::GetClusterMeanRow: Module out of bounds: mod = %d\n",mod);
    return 0;
  }
  return fClusterList[mod].GetRowIndex(index);
}
//___________________________________________________________________________________
UInt_t HenrikClusterFinderSPD::GetClusterSize(UInt_t mod, UInt_t index) {
  if (mod>=240) {
    printf("ERROR: HenrikClusterFinderSPD::GetClusterSize: Module out of bounds: mod = %d\n",mod);
    return 0;
  }
  return fClusterList[mod].GetSizeIndex(index);
}
//___________________________________________________________________________________
UInt_t HenrikClusterFinderSPD::GetClusterWidthZ(UInt_t mod, UInt_t index) {
  if (mod>=240) {
    printf("ERROR: HenrikClusterFinderSPD::GetClusterWidthZ: Module out of bounds: mod = %d\n",mod);
    return 0;
  }
  return fClusterList[mod].GetWidthZIndex(index);
}
//___________________________________________________________________________________
UInt_t HenrikClusterFinderSPD::GetClusterWidthPhi(UInt_t mod, UInt_t index) {
  if (mod>=240) {
    printf("ERROR: HenrikClusterFinderSPD::GetClusterWidthPhi: Module out of bounds: mod = %d\n",mod);
    return 0;
  }
  return fClusterList[mod].GetWidthPhiIndex(index);
}
//___________________________________________________________________________________
UInt_t HenrikClusterFinderSPD::GetClusterType(UInt_t mod, UInt_t index) {
  if (mod>=240) {
    printf("ERROR: HenrikClusterFinderSPD::GetClusterType: Module out of bounds: mod = %d\n",mod);
    return 0;
  }
  return fClusterList[mod].GetTypeIndex(index);
}
//___________________________________________________________________________________
void HenrikClusterFinderSPD::PrintAllClusters() {
  for (UInt_t mod=0; mod<240; mod++) {
    PrintClusters(mod);
  }
}
//___________________________________________________________________________________
void HenrikClusterFinderSPD::PrintClusters(UInt_t mod) {
  if (mod>=240) {
    printf("ERROR: HenrikClusterFinderSPD::PrintClusters: Out of bounds: mod = %d\n",mod);
    return;
  }
  for (UInt_t c=0; c<fClusterList[mod].GetNrEntries(); c++) {
    printf("mod %d , z,y=%f,%f , size=%d , type=%d\n",mod,fClusterList[mod].GetColIndex(c),fClusterList[mod].GetRowIndex(c),fClusterList[mod].GetSizeIndex(c),fClusterList[mod].GetTypeIndex(c));
  }  
}
//___________________________________________________________________________________
void HenrikClusterFinderSPD::NewEvent() {
  for (UInt_t i=0; i<240; i++) {
    fClusterList[i].Clear();
  }
  NewModule();
  fOldModule = -1;
}
//___________________________________________________________________________________
void HenrikClusterFinderSPD::NewModule() {
  fNhitsLeft=0;
  memset(fHits,0,160*256*sizeof(Bool_t));
}
//___________________________________________________________________________________
Int_t HenrikClusterFinderSPD::DoModuleClustering(Int_t module) {
  UInt_t maxHits=fNhitsLeft;
  for (UInt_t hit=0; hit<maxHits; hit++) {
    if (fClusterTypeFlag) fFindClusterType = kTRUE;

    fClusterWidthMinCol = fHitCol[hit];
    fClusterWidthMinRow = fHitRow[hit];
    fClusterWidthMaxCol = fHitCol[hit];
    fClusterWidthMaxRow = fHitRow[hit];

    fClusterTypeOrigCol = fHitCol[hit];
    fClusterTypeOrigRow = fHitRow[hit];
    memset(fClusterTypeArea,0,kMAXCLUSTERTYPESIDEZ*kMAXCLUSTERTYPESIDEY*sizeof(Bool_t));
    fColSum=0;
    fRowSum=0;
    UInt_t size = FindClusterRecu(fClusterTypeOrigCol,fClusterTypeOrigRow);
    if (size>0) {
      fClusterList[module].Insert((Float_t)fColSum/size, (Float_t)fRowSum/size, size, GetClusterWidthZ(), GetClusterWidthPhi(), GetClusterType(size));
    }
    if (fNhitsLeft==0) break;
  }
  return 0;
}
//___________________________________________________________________________________
UInt_t HenrikClusterFinderSPD::FindClusterRecu(Int_t colM, Int_t rowM) {
  if (colM<0 || colM>=160 || rowM<0 || rowM>=256 || !fHits[colM][rowM]) {return 0;}
  fHits[colM][rowM]=kFALSE;
  fColSum+=colM;
  fRowSum+=rowM;
  fNhitsLeft--;
  UInt_t size=1;

  if (colM>fClusterWidthMaxCol) fClusterWidthMaxCol = colM;
  if (colM<fClusterWidthMinCol) fClusterWidthMinCol = colM;
  if (rowM>fClusterWidthMaxRow) fClusterWidthMaxRow = rowM;
  if (rowM<fClusterWidthMaxRow) fClusterWidthMinRow = rowM;

  if (fFindClusterType) {
    Short_t diffz = fClusterTypeOrigCol - colM;
    Short_t diffy = rowM - fClusterTypeOrigRow;
    if (diffz>=kMAXCLUSTERTYPESIDEZ || diffy>=kMAXCLUSTERTYPESIDEY) {
      fFindClusterType=kFALSE;
    }
    else {
      if (diffz==-1) {
	ShiftClusterTypeArea(kSHIFTRIGHT);
	diffz=0;
      }
      if (diffy==-1) {
	ShiftClusterTypeArea(kSHIFTDOWN);
	diffy=0;
      }
      fClusterTypeArea[diffz][diffy] = kTRUE;
    }
  }

  // straight:
  size+=FindClusterRecu(colM  ,rowM-1);
  size+=FindClusterRecu(colM-1,rowM  );
  size+=FindClusterRecu(colM  ,rowM+1);
  size+=FindClusterRecu(colM+1,rowM  );
  // diagonal:
  size+=FindClusterRecu(colM-1,rowM-1);
  size+=FindClusterRecu(colM-1,rowM+1);
  size+=FindClusterRecu(colM+1,rowM-1);
  size+=FindClusterRecu(colM+1,rowM+1);

  return size;
}
//___________________________________________________________________________________
void HenrikClusterFinderSPD::ShiftClusterTypeArea(UInt_t direction) {
  if (direction == kSHIFTRIGHT) {
    fClusterTypeOrigCol++;
    Bool_t tmpClusterTypeArea[kMAXCLUSTERTYPESIDEZ][kMAXCLUSTERTYPESIDEY];
    memset(tmpClusterTypeArea,0,kMAXCLUSTERTYPESIDEZ*kMAXCLUSTERTYPESIDEY*sizeof(Bool_t));
    for (UInt_t z=0; z<kMAXCLUSTERTYPESIDEZ; z++) {
      for (UInt_t y=0; y<kMAXCLUSTERTYPESIDEY; y++) {
	if (fClusterTypeArea[z][y]) {
	  if (z==kMAXCLUSTERTYPESIDEZ-1) {
	    fFindClusterType=kFALSE;
	    return;
	  }
	  else {
	    tmpClusterTypeArea[z+1][y] = kTRUE;
	  }
	}
      }
    }
    memcpy(fClusterTypeArea,tmpClusterTypeArea,kMAXCLUSTERTYPESIDEZ*kMAXCLUSTERTYPESIDEY*sizeof(Bool_t));
  }
  else if (direction == kSHIFTDOWN) {
    fClusterTypeOrigRow--;
    Bool_t tmpClusterTypeArea[kMAXCLUSTERTYPESIDEZ][kMAXCLUSTERTYPESIDEY];
    memset(tmpClusterTypeArea,0,kMAXCLUSTERTYPESIDEZ*kMAXCLUSTERTYPESIDEY*sizeof(Bool_t));
    for (UInt_t z=0; z<kMAXCLUSTERTYPESIDEZ; z++) {
      for (UInt_t y=0; y<kMAXCLUSTERTYPESIDEY; y++) {
	if (fClusterTypeArea[z][y]) {
	  if (y==kMAXCLUSTERTYPESIDEY-1) {
	    fFindClusterType=kFALSE;
	    return;
	  }
	  else {
	    tmpClusterTypeArea[z][y+1] = kTRUE;
	  }
	}
      }
    }
    memcpy(fClusterTypeArea,tmpClusterTypeArea,kMAXCLUSTERTYPESIDEZ*kMAXCLUSTERTYPESIDEY*sizeof(Bool_t));
  }
}
//___________________________________________________________________________________
UInt_t HenrikClusterFinderSPD::GetClusterWidthZ() {
  return fClusterWidthMaxCol-fClusterWidthMinCol+1;
}
//___________________________________________________________________________________
UInt_t HenrikClusterFinderSPD::GetClusterWidthPhi() {
  return fClusterWidthMaxRow-fClusterWidthMinRow+1;
}
//___________________________________________________________________________________
UInt_t HenrikClusterFinderSPD::GetClusterType(UInt_t size) {
  // category 'other':
  if (!fFindClusterType || size>4) 
    return 0;

  // X
  if (size==1) 
    return 1;

  // X
  // X
  if (size==2 && 
      fClusterTypeArea[0][1] && 
      fClusterTypeArea[0][0] ) 
    return 2;

  // XX
  if (size==2 && 
      fClusterTypeArea[1][0] &&
      fClusterTypeArea[0][0] ) 
    return 3;

  // X
  // X
  // X
  if (size==3 && 
      fClusterTypeArea[0][2] && 
      fClusterTypeArea[0][1] && 
      fClusterTypeArea[0][0] ) 
    return 4;
  
  // XX
  // X      and equivalent...
  if (size==3 && 
      (
       (
	fClusterTypeArea[0][0] && 
	fClusterTypeArea[0][1] && 
	(fClusterTypeArea[1][1] || 
	 fClusterTypeArea[1][0])
	)
       ||
       (
	fClusterTypeArea[1][0] && 
	fClusterTypeArea[1][1] && 
	(fClusterTypeArea[0][1] || 
	 fClusterTypeArea[0][0])
	)
       )
      ) 
    return 5;
  
  // XXX
  if (size==3 && 
      fClusterTypeArea[2][0] && 
      fClusterTypeArea[1][0] && 
      fClusterTypeArea[0][0] )
    return 6;
  
  // X
  // X
  // X
  // X
  if (size==4 &&
      fClusterTypeArea[0][3] && 
      fClusterTypeArea[0][2] &&
      fClusterTypeArea[0][1] && 
      fClusterTypeArea[0][0] ) 
    return 7;
  
  // XX
  // XX
  if (size==4 && 
      fClusterTypeArea[1][1] && 
      fClusterTypeArea[1][0] && 
      fClusterTypeArea[0][1] &&
      fClusterTypeArea[0][0] ) 
    return 8;

  // XX
  // X
  // X     and equivalent...
  if (size==4 &&
      (
       (
	fClusterTypeArea[1][0] && 
	fClusterTypeArea[1][1] && 
	fClusterTypeArea[1][2] && 
	fClusterTypeArea[0][0]
	)
       ||
       (
	fClusterTypeArea[1][0] && 
	fClusterTypeArea[1][1] && 
	fClusterTypeArea[1][2] && 
	fClusterTypeArea[0][2]
	)
       ||
       (
	fClusterTypeArea[0][0] && 
	fClusterTypeArea[0][1] && 
	fClusterTypeArea[0][2] && 
	fClusterTypeArea[1][0]
	)
       ||
       (
	fClusterTypeArea[0][0] && 
	fClusterTypeArea[0][1] && 
	fClusterTypeArea[0][2] && 
	fClusterTypeArea[1][2]
	)
       )
      )
    return 9;

  // X
  // XX
  // X     and equivalent...
  if (size==4 &&
      (
       (
	fClusterTypeArea[1][0] && 
	fClusterTypeArea[1][1] && 
	fClusterTypeArea[1][2] && 
	fClusterTypeArea[0][1]
	)
       ||
       (
	fClusterTypeArea[0][0] && 
	fClusterTypeArea[0][1] && 
	fClusterTypeArea[0][2] && 
	fClusterTypeArea[1][1]
	)
       )
      )
    return 10;

  // X
  // XXX    and equivalent...
  if (size==4 &&
      (
       (
	fClusterTypeArea[2][0] && 
	fClusterTypeArea[2][1] && 
	fClusterTypeArea[1][1] && 
	fClusterTypeArea[0][1]
	)
       ||
       (
	fClusterTypeArea[0][0] && 
	fClusterTypeArea[2][1] && 
	fClusterTypeArea[1][1] && 
	fClusterTypeArea[0][1]
	)
       ||
       (
	fClusterTypeArea[2][1] && 
	fClusterTypeArea[2][0] && 
	fClusterTypeArea[1][0] && 
	fClusterTypeArea[0][0]
	)
       ||
       (
	fClusterTypeArea[0][1] && 
	fClusterTypeArea[2][0] && 
	fClusterTypeArea[1][0] && 
	fClusterTypeArea[0][0]
	)
       )
      )
    return 11;

  //  X
  // XXX     and equivalent...
  if (size==4 &&
      (
       (
	fClusterTypeArea[1][0] && 
	fClusterTypeArea[2][1] && 
	fClusterTypeArea[1][1] && 
	fClusterTypeArea[0][1]
	)
       ||
       (
	fClusterTypeArea[1][1] && 
	fClusterTypeArea[2][0] && 
	fClusterTypeArea[1][0] && 
	fClusterTypeArea[0][0]
	)
       )
      )
    return 12;

  //  X
  // X      and equivalent...
  if (size==2 &&
      (
       (
	fClusterTypeArea[0][0] &&
	fClusterTypeArea[1][1] 
	)
       ||
       (
	fClusterTypeArea[1][0] &&
	fClusterTypeArea[0][1] 
	)
       )
      )
    return 13;

  //  X
  //  X
  // X      and equivalent...
  if (size==3 &&
      (
       (
	fClusterTypeArea[0][0] &&
	fClusterTypeArea[0][1] && 
	fClusterTypeArea[1][2] 
	)
       ||
       (
	fClusterTypeArea[1][0] &&
	fClusterTypeArea[1][1] && 
	fClusterTypeArea[0][2] 
	)
       ||
       (
	fClusterTypeArea[1][0] &&
	fClusterTypeArea[0][1] && 
	fClusterTypeArea[0][2] 
	)
       ||
       (
	fClusterTypeArea[0][0] &&
	fClusterTypeArea[1][1] && 
	fClusterTypeArea[1][2] 
	)
       )
      )
    return 14;

  //  XX
  // X      and equivalent...
  if (size==3 &&
      (
       (
	fClusterTypeArea[0][0] &&
	fClusterTypeArea[1][0] && 
	fClusterTypeArea[2][1] 
	)
       ||
       (
	fClusterTypeArea[0][1] &&
	fClusterTypeArea[1][1] && 
	fClusterTypeArea[2][0] 
	)
       ||
       (
	fClusterTypeArea[0][0] &&
	fClusterTypeArea[1][1] && 
	fClusterTypeArea[2][1] 
	)
       ||
       (
	fClusterTypeArea[0][1] &&
	fClusterTypeArea[1][0] && 
	fClusterTypeArea[2][0] 
	)
       )
      )
    return 15;



  return 0;
}


