/**
 * \file src/CaloLevel1.cpp
 * \author Emiliano Mocchiutti
 *
**/
#include <CaloLevel1.h>
//
ClassImp(CaloStrip);
ClassImp(CaloLevel1);

Float_t CaloStrip::UXal = CTX;
Float_t CaloStrip::UYal = CTY;
Float_t CaloStrip::UZal = CTZ;
Bool_t CaloStrip::paramload  = false;

/**
 * CaloStrip default constructor
**/
CaloStrip::CaloStrip() {
  c1 = 0;
  this->Clear();
};

/**
 * CaloStrip default constructor
**/
CaloStrip::CaloStrip(Bool_t mechalig) {
  c1 = 0;
  this->Clear();
  if ( mechalig ){
    ismech = true; 
    paramload = true;
    UXal = MECHCTX; 
    UYal = MECHCTY; 
    UZal = MECHCTZ;
  } else {
    ismech = false; 
    UseStandardAlig();
  };
};

/**
 * CaloStrip default constructor
**/
CaloStrip::CaloStrip(CaloLevel1 *calo) {
  c1 = calo->GetCaloLevel1();
  this->Clear();
  ismech = false; 
  UseStandardAlig();
};

/**
 * CaloStrip default constructor
**/
CaloStrip::CaloStrip(CaloLevel1 *calo, Bool_t mechalig) {
  c1 = calo->GetCaloLevel1();
  this->Clear();
  if ( mechalig ){
    ismech = true; 
    paramload = true;
    UXal = MECHCTX; 
    UYal = MECHCTY; 
    UZal = MECHCTZ;
  } else {
    ismech = false; 
    UseStandardAlig();
  };
};

/**
 * Clear variables
**/
void CaloStrip::Clear(Option_t *t) {  
  fE = 0.;
  fX = 0.;
  fY = 0.;
  fZ = 0.;
  fView = 0;  
  fPlane = 0;
  fStrip = 0;
};

/**
 * Connect to the DB and retrieve alignement parameters
 **/
void CaloStrip::UseStandardAlig(){ 
  //
  if ( !paramload ){
    //
    paramload = true;
    ismech = false; 
    //
    stringstream aligfile;
    Int_t error = 0;
    FILE *f = 0;
    ifstream badfile;
    GL_PARAM *glparam = new GL_PARAM();
    //
    TString host = "mysql://localhost/pamelaprod";
    TString user = "anonymous";
    TString psw = "";
    const char *pamdbhost=gSystem->Getenv("PAM_DBHOST");
    const char *pamdbuser=gSystem->Getenv("PAM_DBUSER");
    const char *pamdbpsw=gSystem->Getenv("PAM_DBPSW");
    if ( !pamdbhost ) pamdbhost = "";
    if ( !pamdbuser ) pamdbuser = "";
    if ( !pamdbpsw ) pamdbpsw = "";
    if ( strcmp(pamdbhost,"") ) host = pamdbhost;
    if ( strcmp(pamdbuser,"") ) user = pamdbuser;
    if ( strcmp(pamdbpsw,"") ) psw = pamdbpsw;
    TSQLServer *dbc = TSQLServer::Connect(host.Data(),user.Data(),psw.Data());
    //
    UXal = 0.;
    UYal = 0.;
    UZal = 0.;
    //
    if ( dbc ){
      //
      // determine where I can find calorimeter ADC to MIP conversion file  
      //
      printf(" Querying DB for calorimeter parameters files...\n");
      //
      //
      //
      error = 0;
      error = glparam->Query_GL_PARAM(1000,102,dbc);
      if ( error >= 0 ){
	//
	aligfile.str("");
	aligfile << glparam->PATH.Data() << "/";
	aligfile << glparam->NAME.Data();
	//
	printf("\n Using parameter file: \n %s \n",aligfile.str().c_str());
	f = fopen(aligfile.str().c_str(),"rb");
	if ( f ){
	  //
	  fread(&UXal,sizeof(UXal),1,f);
	  fread(&UYal,sizeof(UYal),1,f);
	  fread(&UZal,sizeof(UZal),1,f);
	  //
	  fclose(f);
	};
	//
      };
      dbc->Close();
    };
    if ( !UXal ){
      //
      printf(" No able to query DB for calorimeter parameters files\n Using hard-coded parameters \n");
      UXal = CTX; 
      UYal = CTY; 
      UZal = CTZ;
    };
    //  
  };
  //
};

/**
 * Given a strip returns its position in the PAMELA reference system
**/
void CaloStrip::Set(Int_t view, Int_t plane, Int_t strip) {
  //
  this->Clear();
  //
  if ( view < 0 || view > 1 ){
    printf(" ERROR: 0 =< view =< 1 \n");
    return;
  };
  if ( plane < 0 || plane > 21 ){
    printf(" ERROR: 0 =< plane =< 21 \n");
    return;
  };
  if ( strip < 0 || strip > 95 ){
    printf(" ERROR: 0 =< strip =< 95 \n");
    return;
  };
  //
  Float_t lShift = 0.;
  Float_t lPos = 0.;
  extern struct shift shift_;
  //
  // Find MIPs for that strip
  //
  if ( c1 ) fE = c1->GetEstrip(view, plane, strip);
  //
  fView = view + 1;
  fPlane = plane + 1;
  fStrip = strip + 1;
  //
  if ( fPlane%2 ){
    lShift = +0.5;
  } else {
    lShift = -0.5;
  };
  //
  shift_.shift = lShift;
  //
  Float_t zplane[22];
  zplane[0] = 0.;
  Int_t ii = 0;
  for ( Int_t i = 2; i < 23; i++){
    ii = i-1;
    if ( i%2 ){
      zplane[ii] = zplane[ii-1] - 10.09;
    } else {
      zplane[ii] = zplane[ii-1] - 8.09;
    };
  };
  //
  millim_(&fStrip,&lPos);
  //
  if ( fView == 1 ){
    //
    // X view
    //
    fX = (lPos - UXal)/10.;
    fY = 0.;
    fZ = (zplane[fPlane-1] - 5.81 + UZal)/10.;    
    //
   } else {
    //
    // Y view
    //
    fX = 0;
    fY = (lPos - UYal)/10.;
    fZ = (zplane[fPlane-1] + UZal)/10.;
  };
  //  
};

/**
 * Given a point in the space (PAMELA ref system) returns the closest strip
**/
void CaloStrip::Set(Float_t X, Float_t Y, Float_t Z) {
  //
  fX = X;
  fY = Y;
  fZ = Z;
  //
  Float_t zplane[22];
  zplane[0] = 0.;
  Int_t ii = 0;
  for ( Int_t i = 2; i < 23; i++){
    ii = i-1;
    if ( i%2 ){
      zplane[ii] = zplane[ii-1] - 10.09;
    } else {
      zplane[ii] = zplane[ii-1] - 8.09;
    };
  };
  //
  Float_t dzx[22];
  Float_t dzy[22];
  for ( Int_t i=0; i < 22; i++){
    dzx[i] = fabs(fZ*10. - (zplane[i] - 5.81 + UZal));
    dzy[i] = fabs(fZ*10. - (zplane[i] + UZal));    
  };
  //
  Float_t minx = TMath::MinElement(22,dzx);
  Float_t miny = TMath::MinElement(22,dzy);
  //
  // find view
  //
  if ( minx < miny ){
    fView = 1;
  } else {
    fView = 2;
  };
  //
  // find plane
  //
  Float_t pos = 0.;
  //
  for ( Int_t i=0; i < 22; i++){
    if ( fView == 1 ){
      if ( dzx[i] == minx ){
	fPlane = i+1;
	pos = fX*10. + UXal;
      };
    } else {
      if ( dzy[i] == miny ){
	fPlane = i+1;
	pos = fY*10. + UYal;
    };
    };
  };
  //
  // find strip
  //
  Float_t dxy[96];
  Float_t stpos = 0.;
  //
  CaloStrip *ca = new CaloStrip();
  //
  for ( Int_t i=0; i < 96; i++){
    ca->Set(fView-1,fPlane-1,i);
    if ( fView == 1 ){
      stpos = ca->GetX()*10. + UXal;
    } else {
      stpos = ca->GetY()*10. + UYal;
    };
    dxy[i] = fabs(pos - stpos);
  };
  //
  delete ca;
  //  
  Float_t mins =  TMath::MinElement(96,dxy);
  //
  for ( Int_t i=0; i < 96; i++){
    if ( dxy[i] == mins ) fStrip = i+1; 
  };
};

/**
 * Given a point in the space or a strip it returns the Silicon sensor number. Numbering goes like this:
 *
 * y ^
 *   |
 *   |    6 7 8 
 *   |    3 4 5   
 *   |    0 1 2 
 *   | -----------> x 
 *
**/
Int_t CaloStrip::GetSiSensor() {
  //
  // fX fY fZ // fView fPlane
  //
  Float_t deadsi = 0.096;
  Float_t dead = 0.05;
  Float_t sidim = 8.00;
  //  Float_t stripdim = 0.244;
  Float_t sensitarea = 7.808;
  //
  Float_t xoffset = 0.;
  Float_t yoffset = 0.;
  //
  if ( (fView-1) == 0 && !((fPlane-1)%2) ){
    xoffset = +0.05;
    yoffset = 0.0;
  };
  if ( (fView-1) == 0 && ((fPlane-1)%2) ){
    xoffset = -0.05;
    yoffset = -0.20;
  };
  if ( (fView-1) == 1 && !((fPlane-1)%2) ){
    xoffset = +0.10;
    yoffset = -0.05;
  };
  if ( (fView-1) == 1 && ((fPlane-1)%2) ){
    xoffset = -0.10;
    yoffset = -0.15;
  };
  //
  Int_t iind = -1;
  Int_t jind = -1;
  //
  for (Int_t i = 0; i < 3; i++){
    if ( (fX+xoffset+12.1) >= (deadsi+(sidim+dead)*i) && (fX+xoffset+12.1) <= (sensitarea+deadsi+(sidim+dead)*i) ){
      iind = i;
      break;
    };
  };
  //
  for (Int_t j = 0; j < 3; j++){
    if ( (fY+yoffset+12.1) >= (deadsi+(sidim+dead)*j) && (fY+yoffset+12.1) <= (sensitarea+deadsi+(sidim+dead)*j) ){
      jind = j;
      break;
    };
  };
  //
  Int_t sensor = -1;
  if ( iind != -1 &&  jind != -1 ){
    sensor = iind + jind * 3;
  };
  //
  //  printf(" View %i Plane %i x %f y %f z %f xoffset %f yoffset %f iind %i jind %i \n",fView,fPlane,fX,fY,fZ,xoffset,yoffset,iind,jind);
  //
  return(sensor);
  //
};

/**
 * CaloLevel1 constructor
**/
CaloLevel1::CaloLevel1() {    
  //
  estrip = TArrayI(0,NULL);
  //
  this->Clear();
  //
};

/**
 * Clear the CaloLevel1 object
 **/
void CaloLevel1::Clear(Option_t *t) {    
  //
  istrip = 0;
  estrip.Reset();
  //
};

/**
 * Returns the detected energy for the given strip once loaded the event
**/
Float_t CaloLevel1::GetEstrip(Int_t sview, Int_t splane, Int_t sstrip){
  Int_t view = -1;
  Int_t plane = -1;
  Int_t strip = -1;
  Float_t mip = 0.;
  //
  if ( istrip == 0 ) return(0.);
  //
  for (Int_t i = 0; i<istrip; i++ ){
    //
    mip = DecodeEstrip(i,view,plane,strip);
    //
    if ( view == sview && splane == plane && sstrip == strip ) return(mip);
    //
    // entry are ordered by strip, plane and view number. Go out if you pass the input strip
    //
    if ( view == sview && plane == splane && strip > sstrip ) return(0.);
    if ( view == sview && plane > splane ) return(0.);
    if ( view > sview ) return(0.);
    //
  };
  return(0.);
};

/**
 * Given estrip entry returns energy plus view, plane and strip numbers
**/
Float_t CaloLevel1::DecodeEstrip(Int_t entry, Int_t &view, Int_t &plane, Int_t &strip){
  Bool_t sat = false;
  Float_t mip=this->DecodeEstrip(entry,view,plane,strip,sat);
  return(mip);
};

/**
 * Given estrip entry returns energy plus view, plane, strip numbers and saturation info
**/
Float_t CaloLevel1::DecodeEstrip(Int_t entry, Int_t &view, Int_t &plane, Int_t &strip, Bool_t &saturated){
  //
  if ( entry>istrip ){
    //
    printf(" ERROR: problems decoding entry %i, it seems that number of entries is %i\n",entry,istrip);
    //
    return(0.);
  };
  //
  //  printf(" num lim %f \n",std::numeric_limits<Float_t>::max());
  //  printf(" estrip.At(%i) = %i \n",entry,estrip.At(entry));
  //
  Int_t eval = 0;
  if ( estrip.At(entry) > 0. ){
    view = 0;
    eval = estrip.At(entry);
  } else {
    view = 1;
    eval = -estrip.At(entry);
  };
  //
  Int_t fbi = 0;
  fbi = (Int_t)truncf((Float_t)(eval/1000000000));
  //
  Int_t plom = 0;
  plom = (Int_t)truncf((Float_t)((eval-fbi*1000000000)/10000000));
  //
  Float_t tim = 100000.;
  plane = plom;
  if ( fbi == 1 ) tim = 10000.;
  if ( plom > 21 ){
    plane = plom - 22;
    if ( fbi == 1 ){
      tim = 1000.;
    } else {
      tim = 100.;
    };
  };
  if ( plom > 43 ){
    plane = plom - 44;
    tim = 10.;
  };
  if ( plom > 65 ){
    plane = plom - 66;
    tim = 1.;
  };
  //
  strip = (Int_t)truncf((Float_t)((eval - fbi*1000000000 -plom*10000000)/100000));
  //
  Double_t mip = (Double_t)(((Float_t)(eval - fbi*1000000000 -plom*10000000 -strip*100000))/tim);
  //
  saturated = false;
  if ( mip > 5000. ){
    mip -= 5000.;
    saturated = true;
  };
  if ( mip > 0. && mip < 99999. ) return((Float_t)mip);
  //
  printf(" ERROR: problems decoding value %i at entry %i \n",estrip.At(entry),entry);
  //
  view = -1;
  plane = -1;
  strip = -1;
  return(0.);  
}

/*
 * Returns energy released on plane nplane (where 0<= nplane <= 43, 0 = 1Y, 1 = 1X, 2 = 2Y, 3 = 2X, etc. etc.).
 */
Float_t CaloLevel1::qtotpl(Int_t nplane){
  Bool_t sat = false;
  Float_t mip = this->qtotpl(nplane,sat);
  return(mip);
};

/*
 * Returns energy released on plane nplane (where 0<= nplane <= 43, 0 = 1Y, 1 = 1X, 2 = 2Y, 3 = 2X, etc. etc.).
 */
Float_t CaloLevel1::qtotpl(Int_t nplane, Bool_t &sat){
  //
  sat = false;
  Int_t sview = 1;
  if ( nplane%2 ) sview = 0;
  //
//  Int_t splane = nplane-(sview+1)/2;
  Int_t splane = (nplane+sview-1)/2;
  //
  Float_t totmip = qtotpl(sview,splane,sat);
  //
  return(totmip);
  //
};

/*
 * Returns energy released on view "view" (0 = X, 1 = Y) and plane "plane" ( 0 <= plane <= 21 ).
 */
Float_t CaloLevel1::qtotpl(Int_t sview, Int_t splane){
  Bool_t sat = false;
  Float_t mip = this->qtotpl(sview,splane,sat);
  return(mip);
};

/*
 * Returns energy released on view "view" (0 = X, 1 = Y) and plane "plane" ( 0 <= plane <= 21 ).
 */
Float_t CaloLevel1::qtotpl(Int_t sview, Int_t splane, Bool_t &sat){
  //
  Int_t view = -1;
  Int_t plane = -1;
  Int_t strip = -1;
  Bool_t lsat = false;
  sat = false;
  //
  Float_t mip = 0.;
  Float_t totmip = 0.;
  //
  if ( istrip == 0 ) return(0.);
  //
  for (Int_t i = 0; i<istrip; i++ ){
    //
    mip = DecodeEstrip(i,view,plane,strip,lsat);
    //
    if ( view == sview && splane == plane ){
      if ( lsat ) sat = true;
      totmip += mip;
      //printf(" totmip %f mip %f \n",totmip,mip);
    };
    //
    // entry are ordered by strip, plane and view number. Go out if you pass the input strip
    //
    if ( view == sview && plane > splane ) return(totmip);
    if ( view > sview ) return(totmip);
    //
  };
  //
  return(totmip);
  //
};
