import ROOT,os,sys,getopt
import rootUtils as ut
import shipunit as u
import RecoSettings
########################################################################
## For a single event stores HNL, its decay products and several other tracks (if requested) 
#  from ShipMCTracks collection of a MCtrack branch (see $FAIRSHIP/shipdata/ShipMCTrack.h)\n
#
#
class MCTrackInfo(object):
  """ More description here"""
  def __init__(self, tree, debug=0):
    ## root tree to be read.
    self.__tree        = tree
    ## debug level [0,3].
    self.__debug       = debug
    ## z of the last full acceptance plane (to be used in #checkEllipticAcc), must be set later.
    self.__zAcc          = 3380*u.cm
    ## event weight. Read in #readEvent as weight of HNL.
    self.__weight        = 0
    ## HNL decay vertex coordinates (TVector3). Assigned in #readEvent()
    self.__HNLdecayCoord = None
    ## MCTrID (index of the track in MCtrack list) for \b HNL \b daughter products. 
    self.__decayProdTrID = []
    ## Production vertices of several stored products (\b not \b only HNL daughter) as {MCTrID : TVector3}.
    self.__productVertex = {}
    ## Momentum of several stored products (\b not \b only HNL daughter) as {MCTrID : TVector3}. Correspond to $__productVertex.
    self.__productMoment = {}
    ## Charge of several stored products (\b not \b only HNL daughter) as {MCTrID : value}. Correspond to $__productVertex.
    self.__productCharge = {}
    ## Basic information on the particle (pdg, mother pdg,... to be added - z decay!)
    self.__productInfo   = {}
  
  
  
  
  
  ########################################################################
  ##\brief clears all dictionaries and lists. To be called inside #readEvent.
  def __clean(self):
    self.__weight        = 0
    self.__HNLdecayCoord = None
    self.__decayProdTrID = []
    self.__productVertex.clear()
    self.__productMoment.clear()
    self.__productCharge.clear()
    self.__productInfo.clear()

  ########################################################################
  ##\brief set z of the last full acceptance plane (to be used in #checkEllipticAcc), depends on #RecoSettings. trackMinNofStations.
  # \param z - the value [cm].
  def setAccPlaneZ(self, z):
    self.__zAcc = z
    

  ########################################################################
  ##\brief prints HNL decay products
  def PrintHNL(self):
    print type(self).__name__, " : "
    print "\tdecay vertex: ", "  ".join("{:10.4f}".format(self.__HNLdecayCoord(ii)) for ii in range(0,3)),
    print "\n\tdecay products (trID, pdg, charge, momentum):"
    for tid in self.__decayProdTrID :
      print "\t", "{0:6}{1:10}{2:6.0f}".format(tid, self.__productInfo[tid]['pdg'], self.__productCharge[tid]),
      print "\t", " ".join("{:10.4f}".format(self.__productMoment[tid](ii)) for ii in range(0,3))


        
  ########################################################################
  ## \brief returns event weight.
  #  \return value of #__weight
  def getEventWeight(self):
    return self.__weight


  ########################################################################
  ## \brief returns \b HNL \b decay \b vertex coordinates (TVector3 #__HNLdecayCoord).
  #  \return TVector3 #__HNLdecayCoord 
  def getHNLdecayVertex(self):
    return self.__HNLdecayCoord
  
  
  ########################################################################
  ## \brief returns list of MCtrIDs (index in MCTrack collection) fot \b HNL \b decay \b products.
  #  \return list of MCtrIDs (index in MCTrack collection) fot \b HNL \b decay \b products.
  def getHNLdecayTrIDs(self):
    trID = []
    for tid in self.__decayProdTrID:
      trID.append(tid)
    return trID
  
  
  ########################################################################
  ## \brief returns list of all MCtrIDs read by #readEvent and #readTrack - \b not \b only HNL decay products.
  #  \return list of MCtrIDs (index in MCTrack collection) fot all tracks read by #readEvent and #readTrack.
  def getTrIDs(self):
    trID = []
    for tid in self.__productMoment:
      trID.append(tid)
    return trID


  ########################################################################
  ## \brief returns charge of a particle with the given trID (index in MCTrack collection). May return None.
  #  Tries to get charge from #__productCharge; 
  #  if it is not available, tries to add the track calling #readTrack. If fails, returns None.
  #  \param  trID - index of the MCTrack
  #  \return charge of the track or None
    def getCharge(self, trID):
      if ( not trID in self.__productCharge ): self.readTrack(trID)
      return self.__productCharge[trID]
  
  

  ########################################################################
  ## \brief returns momentum (TVector3) for a particle with the given trID (index in MCTrack collection). May return None.
  #  Tries to get momentum from #__productMoment; 
  #  if it is not available, tries to add the track calling #readTrack. If fails, returns None.
  #  \param  trID - index of the MCTrack
  #  \return momentum of the track (TVector3) or None
  def getMomentum(self, trID):
    if ( not trID in self.__productMoment ): self.readTrack(trID)
    return self.__productMoment[trID]
  
  

  
  ########################################################################
  ## \brief checks if HNL decay vertex (and both HNL daughter tracks, if tight cut) are in elliptic acceptance at #__zAcc.
  #  For tight cut each HNL daughter is propagated to #__zAcc (see #getTrackPropagation) 
  #  and checked if it in the acceptance (see #RecoSettings .checkEllipticAcc). Also checks HNL decay vertex.
  #  \param tight - if true, not only HNL vertex but also propagated tracks are checked.
  #  \return True if the vertex (and both tracks, if tight cut) are in the acceptance.
  def checkEllipticAcc(self, tight=True):
    ok = RecoSettings.checkEllipticAcc(self.__HNLdecayCoord)
    if ( not ok ) : return False
    if ( tight ) :
      for tid in self.__decayProdTrID :
	v3 = self.getTrackPropagation(tid, self.__zAcc)
	ok = ( ok and RecoSettings.checkEllipticAcc(v3) )
	if ( not ok ) : break
    return ok
  
  
  
  
  ########################################################################
  ## \brief for Tracker Performance studies. Returns -1, 0, 1 depending on vertex topology.
  #  defines direction in y: 0 if both momY the same direction, -1 if (negative up, positive down).
  #  \return 0 if both momY the same direction, -1 if (negative up, positive down).
  def checkVertexUpDown(self):
    theCase = 0 # undefined
    if ( (not self.__productVertex) or (len(self.__decayProdTrID)<2) ): return theCase
    tid1 = self.__decayProdTrID[0]
    tid2 = self.__decayProdTrID[1]
    if ( self.__productMoment[tid1].Y()*self.__productMoment[tid2].Y() >=0 ): return theCase
    
    theCase = self.__productCharge[tid1]*self.__productMoment[tid1].Y()/abs(self.__productMoment[tid1].Y())
    
    return theCase


  ########################################################################
  ## \brief for Tracker Performance studies. Returns TVector3.
  #  Linear propagation of a given track to a plane (0,0,z) perendicular to Z-axis.
  #  \param trID - track index (MCtrID).
  #  \param    z - coordinate of the plane. 
  #  \return coordinates of the point of track crossing the given plalne (TVector3). 
  def getTrackPropagation(self, trID, z):
    if ( not trID in self.__productMoment ): self.readTrack(trID)
    xy=[0.,0.]
    for ii in [0,1]:
      xy[ii] = self.__productVertex[trID][ii]+self.__productMoment[trID][ii]/self.__productMoment[trID][2]*(z-self.__productVertex[trID][2])
    vec3 = ROOT.TVector3(xy[0],xy[1],z)
    return vec3




  ########################################################################
  ## \brief reads one additional track with trID from MCTrack collection. Returns 1 if success, 0 otherwise.
  #  fills #__productVertex, #__productMoment, #__productCharge dictionaries.
  #  \param trID - track index (MCtrID).
  #  \return 1 if successful, 0 if trID is not found and the dictionaries are not modified.
  def readTrack(self, trID):
    x = None
    try:
      x=self.__tree.MCTrack[trID]
    except:
      pass
    if (not b) : return 0
    
    vec3 = ROOT.TVector3(x.GetPx(),x.GetPy(),x.GetPz())
    self.__productMoment[trID] = vec3
    vec3 = ROOT.TVector3(x.GetStartX(),x.GetStartY(),x.GetStartZ())
    self.__productVertex[trID] = vec3
    self.__productCharge[trID] = RecoSettings.chargePDG(x.GetPdgCode())
    self.__productInfo[trID] ={}
    self.__productInfo[trID]['pdg'] = x.GetPdgCode()
    mid  = x.GetMotherId()
    if(mid>=0) : self.__productInfo[trID]['mpdg'] = self.__tree.MCTrack[mid].GetPdgCode()
    else       : self.__productInfo[trID]['mpdg'] = 0
    if( self.__debug>0 ):
	print "additional MC track for trID", trID
	print "\tpdgid/charge/mother: ", x.GetPdgCode(), self.__productCharge[trID], mid, "(pdf=",self.__productInfo[trID]['mpdg'], ")"
	print "\torigin:   ", self.__productVertex[trID].X(), self.__productVertex[trID].Y(), self.__productVertex[trID].Z()
	print "\tmomentum: ", self.__productMoment[trID].X(), self.__productMoment[trID].Y(), self.__productMoment[trID].Z()
    return 1




    
  ########################################################################
  ## \brief reads \b HNL \b decay \b products from MCTrack collection (\b two at the moment!). Returns number of daughters.
  #  Fills #__HNLdecayCoord and #__productVertex, #__productMoment, #__productCharge dictionaries for \b HNL \b daughters.
  #  \return number of daughters (loop stops at daughter==2 at the moment!).
  def readEvent(self):
    self.__clean()
    if (self.__debug>0 ):
      print "sTree.MCTrack", self.__tree.MCTrack, "size:", len(self.__tree.MCTrack)
    daughter = 0  
    tid      = 0
    #/home/kkuzn/SHIP/ShipSoft/tmp/FairShip/shipdata/ShipMCTrack.h
    for x in self.__tree.MCTrack:
      if daughter>1:
	break
      mid = x.GetMotherId()
      Z   = x.GetStartZ()
      if( Z>5000 ): Z=5000
      if mid==0: # #NB# here I suppose HNL is record #1 always!
	self.__weight = x.GetWeight()
	if self.__debug>0: print "HNL\t\t", tid,"\t", x.GetStartX(), x.GetStartY(), x.GetStartZ(), "\t\t", x.GetPx(), x.GetPy(), x.GetPz() 
      elif( mid==1 ): # HNL decay products
	self.__decayProdTrID.append(tid)
	vec3 = ROOT.TVector3(x.GetPx(),x.GetPy(),x.GetPz())
	self.__productMoment[tid] = vec3
	vec3 = ROOT.TVector3(x.GetStartX(),x.GetStartY(),x.GetStartZ())
	self.__productVertex[tid] = vec3
	self.__productCharge[tid] = RecoSettings.chargePDG(x.GetPdgCode())
	self.__productInfo[tid] ={}
	self.__productInfo[tid]['pdg']  = x.GetPdgCode()
	self.__productInfo[tid]['mpdg'] = self.__tree.MCTrack[mid].GetPdgCode()
	if( self.__debug>0 ): print  x.GetPdgCode(), "\t\t", tid,"\t", x.GetStartX(), x.GetStartY(), x.GetStartZ(), "\t\t", x.GetPx(), x.GetPy(), x.GetPz() 
	if daughter<1: 
	  self.__HNLdecayCoord = ROOT.TVector3(x.GetStartX(), x.GetStartY(), x.GetStartZ())
	else:
	  if self.__HNLdecayCoord.Z()!=x.GetStartZ():
	    self.__HNLdecayCoord.SetXYZ(0,0,-8899)
	daughter+=1
      tid+=1
      
    return daughter

