#include "StHbtMaker/CorrFctn/QinvCorrFctnOpposHemisphereKt.h"
#include <sstream>
#include <cstdio>
#include <TRandom3.h>

#ifdef __ROOT__
ClassImp(QinvCorrFctnOpposHemisphereKt)
#endif

//_________________
QinvCorrFctnOpposHemisphereKt::QinvCorrFctnOpposHemisphereKt(char* title,
							     const int& nbins,
							     const float& QinvLo, 
							     const float& QinvHi,
							     const int& nCFs,
							     const float& KtLo,
							     const float& KtHi,
                                                             float mass,
                                                             StHbtPairCut *paircut) {
  mNumberCFs = nCFs;
  mKtMin = KtLo;
  mKtMax = KtHi;
  mDeltaKt = (mKtMax-mKtMin)/mNumberCFs;

  // set up numerators
  mNumerator = new StHbt1DHisto[mNumberCFs];

  // set up denominators
  mDenominator = new StHbt1DHisto[mNumberCFs];

  stringstream TitNum,TitDen,TitCurrent;
  TitNum.str("");TitDen.str("");
  TitNum<<title<<"_Num";
  TitDen<<title<<"_Den";

  for(int i=0; i<mNumberCFs; i++) {
    TitCurrent.str("");
    TitCurrent<<TitNum.str()<<"_bin_"<<i;
    mNumerator[i].SetName(TitCurrent.str().c_str());
    mNumerator[i].SetTitle(TitCurrent.str().c_str());
    mNumerator[i].SetBins(nbins,QinvLo,QinvHi);
    mNumerator[i].SetDirectory(0);
    mNumerator[i].Sumw2();

    TitCurrent.str("");
    TitCurrent<<TitDen.str()<<"_bin_"<<i;
    mDenominator[i].SetName(TitCurrent.str().c_str());
    mDenominator[i].SetTitle(TitCurrent.str().c_str());
    mDenominator[i].SetBins(nbins,QinvLo,QinvHi);
    mDenominator[i].SetDirectory(0);
    mDenominator[i].Sumw2();
  }

  mRandy = new TRandom3((unsigned int)time(0)); // TODO: modify to c-stdlib rand function
                                                //       rand_r?

  mMass = mass;
  mPairCut = paircut;
  mPairCutFlag = true;
}

//_________________
QinvCorrFctnOpposHemisphereKt::~QinvCorrFctnOpposHemisphereKt() {
  delete [] mNumerator;
  delete [] mDenominator;
}

//_________________
void QinvCorrFctnOpposHemisphereKt::WriteOutHistos() {

  for(int i=0; i<mNumberCFs; i++) {
    mNumerator[i].Write();
    mDenominator[i].Write();
  }
}

//_________________
void QinvCorrFctnOpposHemisphereKt::Finish() {
  /* Ratios might be here */
}

//_________________
void QinvCorrFctnOpposHemisphereKt::AddMixedPair(const StHbtPair* pair) {
  /*Empty*/
}

//_________________
void QinvCorrFctnOpposHemisphereKt::AddRealPair(const StHbtPair* pair) {
  double weight = 1.0;
  int idx;

  //
  // Fill real pair Qinv histogram
  //
  if (mPairCut->Pass(pair)) {
    idx = (int)(((fabs(pair->kT())-mKtMin)/mDeltaKt));
    if ((idx>=0)&&(idx<mNumberCFs)) {
        mNumerator[idx].Fill(fabs(pair->qInv()),1.0);
    }
  }
  //
  // Calculate new pair
  //
  double rnd = mRandy->Rndm();
  StHbtTrack newTrack(rnd > 0.5 ?                // if rnd > 0.5 then
                      *pair->track1()->Track() : // track1 otherwise
                      *pair->track2()->Track()); // track2
  StHbtThreeVector trackP = newTrack.P();
  trackP *= -1.0; // invert momentum of the track
  newTrack.SetP(trackP);
  StHbtThreeVector trackPGlobal = newTrack.PGlobal();
  trackPGlobal *= -1.0; // invert momentum of the track
  newTrack.SetPGlobal(trackPGlobal);
  //
  // Re-calculate helix
  //
  // primary helix
  const StPhysicalHelixD &helix = newTrack.Helix();
  StPhysicalHelixD newHelix(trackP, helix.origin(),
                            mMagField*kilogauss, newTrack.Charge());
  newTrack.SetHelix(newHelix);
  // global helix
  const StPhysicalHelixD &helixGlobal = newTrack.HelixGlobal();
  StPhysicalHelixD newHelixGlobal(trackPGlobal, helixGlobal.origin(),
                                  mMagField*kilogauss, newTrack.Charge());
  newTrack.SetHelixGlobal(newHelixGlobal);

  //
  // Set new particle and pair
  //
  StHbtParticle newParticle(&newTrack, mMass);
  StHbtPair newPair(rnd > 0.5 ? pair->track2() : pair->track1(),
                    &newParticle);
  //
  // Check cut for the new pair and fill denominator
  //
  if (mPairCut->Pass(&newPair)) {
    double Qinv = fabs(newPair.qInv());
    //
    // After the rotation was done
    //   we have to recalculate transverse pair momentum
    //   and out qInv to the different kT bin (if it is different)
    //
    idx = (int)( (newPair.kT()/2-mKtMin)/mDeltaKt );
    if((idx>=0)&&(idx<mNumberCFs)) {
        mDenominator[idx].Fill(fabs(newPair.qInv()), weight);
    }
  }
}

//____________________________
StHbtString QinvCorrFctnOpposHemisphereKt::Report() {

  int mNumeratorEntries=0;
  int mDenominatorEntries=0;

  for (int i=0; i<mNumberCFs; i++){
    mNumeratorEntries += (int)mNumerator[i].GetEntries();
    mDenominatorEntries += (int)mDenominator[i].GetEntries();
  }
  string stemp = "Qinv Correlation Function of Opposite Hemi-sphere Report:\n";
  char ctemp[100];
  sprintf(ctemp,"Number of entries in all numerators:\t%i\n",mNumeratorEntries);
  stemp += ctemp;
  sprintf(ctemp,"Number of entries in all denominators:\t%i\n",mDenominatorEntries);
  stemp += ctemp;
  StHbtString returnThis = stemp;
  return returnThis;
}
