// Author: Bertrand Bellenot   22/08/02

/*************************************************************************
 * Copyright (C) 1995-2002, Bertrand Bellenot.                           *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see the LICENSE file.                         *
 *************************************************************************/

#include <stdlib.h>

#include <TROOT.h>
#include <TPolyLine3D.h>
#include <TRandom.h>
#include <TParticle.h>
#include <TDecayChannel.h>
#include "MyParticle.h"
#include "TVector3.h"
#include "MyEvent.h"
#include "RootShower.h"
#include <TGeoTrack.h>

TGeoTrack *fgTrack;
TGeoTrack *fgChild;

//______________________________________________________________________________
//
// MyEvent class implementation
//______________________________________________________________________________

ClassImp(EventHeader)
ClassImp(MyEvent)

TClonesArray *MyEvent::fgParticles = 0;

//______________________________________________________________________________
MyEvent::MyEvent()
{
    // Create an Event object.
    // When the constructor is invoked for the first time, the 
    // class static variables fgParticles and fgTracks is 0 and 
    // the TClonesArray fgParticles is created.
    if (!fgParticles) fgParticles = new TClonesArray("MyParticle", 100000);
    fgParticles->BypassStreamer(kFALSE);
    fParticles = fgParticles;
    fNparticles = 0;
}

//______________________________________________________________________________
MyEvent::~MyEvent()
{
    // Destructor
    Clear();
}

//______________________________________________________________________________
void MyEvent::Init(Int_t id, Int_t first_particle, Double_t E_0, Double_t B_0)
{
    // Initialize event ...
    // creates detector and set initial values

    Char_t  strtmp[80];
    Int_t i;
    fId = id;
    fB = B_0;

    // generate array of energies threshold used
    // to give a track color related to the particle
    // energy
    for(i=0;i<10;i++)
        E_thresh[i] = E_0/(2<<i);

    Clear();
    Reset();

    if (!fgParticles) fgParticles = new TClonesArray("MyParticle", 100000);
    fParticles = fgParticles;
    fNparticles = 0;

    fTotalParticles = 0;
    fLast = 0;
    fAliveParticles = 1;
    fMatter = 0;

    TVector3 location(0.0,fDetector.GetMinY(),0.0);
    TVector3 momentum(0.0,E_0,0.0);

    AddParticle(0,first_particle, location, momentum);
    GetParticle(0)->GenerateTimeOfDecay();

    AddTrack(0, -1);

    gTmpLTI = gEventListTree->AddItem(gBaseLTI,
            GetParticle(0)->GetName());
    sprintf(strtmp,"%1.2e GeV",GetParticle(0)->Energy());
    gEventListTree->SetToolTipItem(gTmpLTI, strtmp);
    gLTI[0] = gTmpLTI;

}

//______________________________________________________________________________
void MyEvent::Clear(Option_t *option)
{
    // Clear tracks and particles arrays
    if(gGeoManager) gGeoManager->ClearTracks();
    fParticles->Clear(option);
    fMatter = 0;
}

//______________________________________________________________________________
void MyEvent::Reset(Option_t *option)
{
    // Static function to reset all static objects for this event
    delete fgParticles; fgParticles = 0;
    fMatter = 0;
}

//______________________________________________________________________________
void MyEvent::SetHeader(Int_t i, Int_t run, TDatime date, Int_t primary, Double_t energy)
{
    // set event header with event identification and startup parameters
    fEvtHdr.Set(i, run, date, primary, energy);
}

//______________________________________________________________________________
void MyEvent::AddTrack(Int_t id, Int_t parent_id)
{
    // Add a new track to the list of tracks for this event.
    if((GetParticle(id)->GetPdgCode() != PHOTON) &&
       (GetParticle(id)->GetPdgCode() != NEUTRINO_E) &&
       (GetParticle(id)->GetPdgCode() != NEUTRINO_MUON) &&
       (GetParticle(id)->GetPdgCode() != NEUTRINO_TAU) &&
       (GetParticle(id)->GetPdgCode() != ANTINEUTRINO_E) &&
       (GetParticle(id)->GetPdgCode() != ANTINEUTRINO_MUON) &&
       (GetParticle(id)->GetPdgCode() != ANTINEUTRINO_TAU) ) {
         gGeoManager->AddTrack(id, GetParticle(id)->GetPdgCode(), GetParticle(id));
         fgTrack = (TGeoTrack *)gGeoManager->GetTrackOfId(id);
         fgTrack->SetName(GetParticle(id)->GetName());
    }

}

//______________________________________________________________________________
MyParticle *MyEvent::AddParticle(Int_t id, Int_t pdg_code, const TVector3 &pos,const TVector3 &mom)
{
    // Add a new particle to the list of particles for this event.
    // To avoid calling the very time consuming operator new for each track,
    // the standard but not well know C++ operator "new with placement"
    // is called. If particle[i] is 0, a new MyParticle object will be created
    // otherwise the previous particle[i] will be overwritten.
    TClonesArray &parts = *fParticles;
    MyParticle *part = new(parts[fNparticles++]) MyParticle(id,pdg_code,CREATED,UNDEFINE,pos,mom);
    //Save reference to last Track in the collection of Tracks
    fLastParticle = part;
    return part;
}

//______________________________________________________________________________
Int_t MyEvent::dE_dX(Int_t id)
{
    // Compute de/dx for particle "id" into detector material
    // for more infos, please refer to the particle data booklet
    // from which the formulas has been extracted

    Double_t gamma,abs_beta,abs_p,abs_loss,dX;

    // if particle's energy is equal to its mass, it is at rest, 
    // so set its status as dead
    if(GetParticle(id)->Energy() <= GetParticle(id)->GetMass()) return(DEAD);
    else {
        // absolute value of momentum
        abs_p = GetParticle(id)->P();
        if(abs_p <= 0) {
            // if absolute value of momentum is less or equal to zero,
            // set it to the particle's mass (minimum allowed value for momentum)
            GetParticle(id)->SetMomentum(0.0, 0.0, 0.0,GetParticle(id)->GetMass());
        }
        else {
            // Compute energy loss in detector's material
            // cf Bethe Bloch formula
            TVector3 p_0(GetParticle(id)->GetvMoment() * (1 / abs_p));
            abs_beta = abs_p / GetParticle(id)->Energy();
            dX = fDetector.GetdT(fMatter) * CSpeed * abs_beta;
            abs_beta *= abs_beta;
            if(abs_beta < .9999999999) gamma = 1/TMath::Sqrt(1.0-abs_beta);
            else gamma = MAX_GAMMA;
            abs_loss = (fDetector.GetPreconst(fMatter) * dX / abs_beta) *
                       (TMath::Log(2.0 * GetParticle(id)->GetMass() * gamma * gamma * abs_beta /
                        fDetector.GetI(fMatter)) - abs_beta);
            if(abs_loss < 0) abs_loss = -abs_loss;
            if(abs_loss >= (GetParticle(id)->Energy() - GetParticle(id)->GetMass())) {
                // if energy loss leave less energy to the particle than 
                // its mass, set its momentum equal to its mass 
                // (minimum allowed value for momentum)
                GetParticle(id)->SetMomentum(0.0, 0.0, 0.0, GetParticle(id)->GetMass());
            }
            else {
                // else decrease its energy by calculated energy loss
                GetParticle(id)->SetMoment(GetParticle(id)->GetvMoment(),
                    GetParticle(id)->Energy() - abs_loss);
                abs_p = TMath::Sqrt((GetParticle(id)->Energy() * GetParticle(id)->Energy()) -
                             (GetParticle(id)->GetMass() * GetParticle(id)->GetMass()));
                GetParticle(id)->SetMoment(p_0 * abs_p);
                // Add calculated energy loss at total particle's energy loss
                GetParticle(id)->AddELoss(abs_loss);
            }
        }
        if(GetParticle(id)->Energy() > GetParticle(id)->GetMass()) return(ALIVE);
        else return(DEAD);
    }
}

//______________________________________________________________________________
Int_t MyEvent::Bremsstrahlung(Int_t id)
{
    // compute bremsstrahlung for particle "id"
    Double_t  ratio;
    Int_t     d_num1,d_num2;
    Char_t    strtmp[80];
    MyParticle *part;

    // find two ids for children particles
    if((FindFreeId(&d_num1) != DEAD) && (FindFreeId(&d_num2) != DEAD)) {
        // compute the particle's energy ratio...
        ratio = (GetParticle(id)->Energy() - GetParticle(id)->GetMass()) /
                (2 * GetParticle(id)->P());
        // create first child if fact, electron continues with less energy 
        // and in a different direction. To that end the electron is added 
        // to its own list of children, because otherwise it would vanish.
        part = AddParticle(d_num1, GetParticle(id)->GetPdgCode(), GetParticle(id)->GetvLocation(),
                    GetParticle(id)->GetvMoment() * ratio);
        part->SetFirstMother(id);
        // as its first child is in fact the same particle, 
        // keep the same decay time
        part->SetTimeOfDecay(GetParticle(id)->GetTimeOfDecay());
        GetParticle(id)->SetChild(0, d_num1);
        // add a track related to this particle
        AddTrack(d_num1, id);

        // add a particle related list tree item to the event list tree
        gTmpLTI = gEventListTree->AddItem(gLTI[id], part->GetName());
        sprintf(strtmp,"%1.2e GeV",part->Energy());
        gEventListTree->SetToolTipItem(gTmpLTI, strtmp);
        gLTI[d_num1] = gTmpLTI;

        // create second child
        part = AddParticle(d_num2,PHOTON, GetParticle(id)->GetvLocation(),
                    GetParticle(id)->GetvMoment() * ratio);
        part->SetFirstMother(id);
        // generate time of decay (not used in this case, as it is a photon,
        // but to keep the same philosophy in every case...
        part->GenerateTimeOfDecay();
        GetParticle(id)->SetChild(1, d_num2);
        
        // add a track related to this particle
        AddTrack(d_num2, id);

        // add a particle related list tree item to the event list tree
        gTmpLTI = gEventListTree->AddItem(gLTI[id],part->GetName());
        sprintf(strtmp,"%1.2e GeV",part->Energy());
        gEventListTree->SetToolTipItem(gTmpLTI, strtmp);
        gLTI[d_num2] = gTmpLTI;

        // increment number of children by the two created particles
        GetParticle(id)->SetNChildren(2);

        return(ALIVE);
    }
    else return(DEAD);
}

//______________________________________________________________________________
Int_t MyEvent::Pair_production(Int_t id)
{
    // compute the pair production for particle "id"
    Double_t ratio = 1.0;
    Int_t    d_num1,d_num2;
    Char_t   strtmp[80];
    MyParticle *part;

    // find two ids for children particles
    if((FindFreeId(&d_num1) != DEAD) && (FindFreeId(&d_num2) != DEAD)) {
        // compute energy ratio for particles creation
        ratio = TMath::Sqrt((GetParticle(id)->Energy() * GetParticle(id)->Energy())/4.0)
                          / GetParticle(id)->P();
        // create first child
        part = AddParticle(d_num1, POSITRON, GetParticle(id)->GetvLocation(),
                    GetParticle(id)->GetvMoment() * ratio);
        part->SetFirstMother(id);
        // generate time of decay (not used in this case, as it is an electron
        // or a positron, but to keep the same philosophy in every case...
        part->GenerateTimeOfDecay();
        GetParticle(id)->SetChild(0, d_num1);
        // add a track related to this particle
        AddTrack(d_num1, id);

        // add a particle related list tree item to the event list tree
        gTmpLTI = gEventListTree->AddItem(gLTI[id], part->GetName());
        sprintf(strtmp,"%1.2e GeV",part->Energy());
        gEventListTree->SetToolTipItem(gTmpLTI, strtmp);
        gLTI[d_num1] = gTmpLTI;

        // create second child
        part = AddParticle(d_num2, ELECTRON, GetParticle(id)->GetvLocation(),
                    GetParticle(id)->GetvMoment() * ratio);
        part->SetFirstMother(id);
        // generate time of decay (not used in this case, as it is an electron
        // or a positron, but to keep the same philosophy in every case...
        part->GenerateTimeOfDecay();
        GetParticle(id)->SetChild(1, d_num2);
        // add a track related to this particle
        AddTrack(d_num2, id);

        // add a particle related list tree item to the event list tree
        gTmpLTI = gEventListTree->AddItem(gLTI[id], part->GetName());
        sprintf(strtmp,"%1.2e GeV",part->Energy());
        gEventListTree->SetToolTipItem(gTmpLTI, strtmp);
        gLTI[d_num2] = gTmpLTI;

        // increment number of children by the two created particles
        GetParticle(id)->SetNChildren(2);

        return(ALIVE);
    }
    else return(DEAD);

}

//______________________________________________________________________________
Int_t MyEvent::Action(Int_t id)
{
    // main event's action
    Int_t  nchild;
    CheckMatter(id);
    if(GetParticle(id)->GetDecayType() == UNDEFINE)
        Define_decay(id);
    if(GetParticle(id)->GetPdgCode() == PHOTON){
        // compute the step delta x to be covered by the particle
        TVector3 delta_x(GetParticle(id)->GetvMoment() * (CSpeed * fDetector.GetdT(fMatter) / GetParticle(id)->Energy()));
        // check if moved too far (out of detector's bouds) 
        if(Move(id, delta_x) == DEAD)
            // set its status as dead 
            DeleteParticle(id);
        else {
            // if distance covered is greater than particle's decay length,
            // apply pair production and check if particle is dead. If not,
            // increment total alive particles by the two created children,
            // then set the particle status as dead
            if(GetParticle(id)->GetPassed() >= GetParticle(id)->GetDecayLength()) {
                if(Pair_production(id) == DEAD) return(DEAD);
                else {
                    fAliveParticles += 2;
                    DeleteParticle(id);
                }
            }
        }
    }
    else if((GetParticle(id)->GetPdgCode() == NEUTRINO_E) ||
            (GetParticle(id)->GetPdgCode() == NEUTRINO_TAU) ||
            (GetParticle(id)->GetPdgCode() == NEUTRINO_MUON) ||
            (GetParticle(id)->GetPdgCode() == ANTINEUTRINO_E) ||
            (GetParticle(id)->GetPdgCode() == ANTINEUTRINO_TAU) ||
            (GetParticle(id)->GetPdgCode() == ANTINEUTRINO_MUON) ) {
            // if current particle is a neutrino ( or antineutrino )
            // set its status as dead ( estimate its probability of 
            // interaction as null )
            DeleteParticle(id);
    }
    else { // particle is not a photon or neutrino
        // if current particle is charged, apply magnetic field influence
        if(GetParticle(id)->GetPDG()->Charge() != 0)
            ScatterAngle(id);
        if((fB != 0) && (GetParticle(id)->GetPDG()->Charge() != 0)) Magnetic_field(id);
        // compute the step delta x to be covered by the particle
        TVector3 delta_x(GetParticle(id)->GetvMoment() * (CSpeed * fDetector.GetdT(fMatter) / GetParticle(id)->Energy()));
        // check if moved too far (out of detector's bouds) 
        if(Move(id, delta_x) == DEAD) {
            // set its status as dead 
            DeleteParticle(id);
        }
        else {
            // check energy loss, and if too much energy loss ( particle at rest )
            // set its status as dead 
            if(dE_dX(id) == DEAD) DeleteParticle(id);
            else {
                // if at end of particle's life time, decay it
                if(CheckDecayTime(id) == 1) {
                    // if no child found
                    if((nchild = Decay(id)) == -1) return(DEAD);
                    else {
                        // else increment total alive particles by amount
                        // of particle's children
                        fAliveParticles += nchild;
                        DeleteParticle(id);
                    }
                }
                // if not at end of particle's life time, check if distance 
                // covered is greater than particle's decay length, apply 
                // defined decay type and check if particle is dead. If not,
                // increment total alive particles by the two created children,
                // then set the particle status as dead
                else if(GetParticle(id)->GetPassed() >= GetParticle(id)->GetDecayLength()) {
                    switch(GetParticle(id)->GetDecayType()) {
                        case BREMS:
                            if(Bremsstrahlung(id) == DEAD) return(DEAD);
                            else {
                                fAliveParticles += 2;
                                DeleteParticle(id);
                            }
                            break;
                        case CONVERSION:
                            if(Pair_production(id) == DEAD) return(DEAD);
                            else {
                                fAliveParticles += 2;
                                DeleteParticle(id);
                            }
                            break;
                    }
                }
            }
        }
    }
    return(ALIVE);
}

//______________________________________________________________________________
void MyEvent::Magnetic_field(Int_t id)
{
    // extrapolate track in a constant field oriented along X axis
    // translated to C++ from GEANT3 routine GHELX3
    Double_t sint, sintt, tsint, cos1t, sin2;
    Double_t f1, f2, f3, v1, v2, v3;
    Double_t pol = GetParticle(id)->GetPDG()->Charge() > 0.0 ? 1.0 : -1.0;
    Double_t h4  = pol * 2.9979251e-04 * fB;
    Double_t hp  = GetParticle(id)->Pz();
    Double_t tet = -h4 * CSpeed * fDetector.GetdT(fMatter) / GetParticle(id)->P();
    if (TMath::Abs(tet) > 0.15) {
        sint  = TMath::Sin(tet);
        sintt = sint / tet;
        tsint = (tet - sint) / tet;
        sin2  = TMath::Sin(0.5 * tet);
        cos1t = 2.0 * sin2 * sin2 / tet;
    } else {
        tsint = tet * tet / 6.0;
        sintt = 1.0 - tsint;
        sint  = tet * sintt;
        cos1t = 0.5 * tet;
    }
    f1 = -tet * cos1t;
    f2 = sint;
    f3 = tet * cos1t * hp;
    v1 = GetParticle(id)->Px() + (f1 * GetParticle(id)->Px() + f3);
    v2 = GetParticle(id)->Py() + (f1 * GetParticle(id)->Py() + f2 * GetParticle(id)->Pz());
    v3 = GetParticle(id)->Pz() + (f1 * GetParticle(id)->Pz() - f2 * GetParticle(id)->Py());
    TVector3 new_mom(v1, v2, v3); 
    GetParticle(id)->SetMoment(new_mom);
}

//______________________________________________________________________________
Int_t MyEvent::Move(Int_t id, const TVector3 &dist)
{
    // Move particle "id" by step dist, update the distance covered
    // then check if out of detector's bounds
    Double_t mpos[3];
    mpos[0] = GetParticle(id)->GetvLocation().x();
    mpos[1] = GetParticle(id)->GetvLocation().y();
    mpos[2] = GetParticle(id)->GetvLocation().z();

    GetParticle(id)->SetLocation(GetParticle(id)->GetvLocation() + dist);
    GetParticle(id)->SetPassed(GetParticle(id)->GetPassed() + dist.Mag());

    if((GetParticle(id)->GetvLocation().x() > fDetector.GetMaxX()) ||
       (GetParticle(id)->GetvLocation().x() < fDetector.GetMinX()) ||
       (GetParticle(id)->GetvLocation().y() > fDetector.GetMaxY()) ||
       (GetParticle(id)->GetvLocation().y() < fDetector.GetMinY()) ||
       (GetParticle(id)->GetvLocation().z() > fDetector.GetMaxZ()) ||
       (GetParticle(id)->GetvLocation().z() < fDetector.GetMinZ())) {
        return(DEAD);
    }
    // If not out of bounds, set related Track's next point
    else {
        if((GetParticle(id)->GetPdgCode() != PHOTON) &&
           (GetParticle(id)->GetPdgCode() != NEUTRINO_E) &&
           (GetParticle(id)->GetPdgCode() != NEUTRINO_MUON) &&
           (GetParticle(id)->GetPdgCode() != NEUTRINO_TAU) &&
           (GetParticle(id)->GetPdgCode() != ANTINEUTRINO_E) &&
           (GetParticle(id)->GetPdgCode() != ANTINEUTRINO_MUON) &&
           (GetParticle(id)->GetPdgCode() != ANTINEUTRINO_TAU) ) {
            fgTrack = (TGeoTrack *)gGeoManager->GetTrackOfId(id);
            if(fgTrack) {
                fgTrack->AddPoint(GetParticle(id)->GetvLocation().x(),
                    GetParticle(id)->GetvLocation().y(),GetParticle(id)->GetvLocation().z(),0.0);
            }
        }
        return(ALIVE);
    }
}

//______________________________________________________________________________
void MyEvent::Define_decay(Int_t id)
{
    // Define decay type for particle "id", then check decay length for it

    Double_t idecay_length = -1.;
    Double_t iactual_length;
    Int_t    idecay_type = CONVERSION;

    if ( (GetParticle(id)->GetPdgCode() == ELECTRON) ||
         (GetParticle(id)->GetPdgCode() == POSITRON)) {
        // check if bremsstrahlung is allowed
        if( (iactual_length = Brems_prob(id)) > 0.) {
            if( (idecay_length == -1) || (iactual_length < idecay_length) ) {
                idecay_length = iactual_length;
                idecay_type = BREMS;
            }
        }
    }
    else if(GetParticle(id)->GetPdgCode() == PHOTON) {
        // check if pair production is allowed
        if( (iactual_length = Pair_prob(id)) > 0. ) {
            if( (idecay_length == -1) ||
                (iactual_length < idecay_length) ) {
                idecay_length = iactual_length;
                idecay_type = CONVERSION;
            }
        }
    }
    if( idecay_length > 0) {
        GetParticle(id)->SetDecayType(idecay_type);
        GetParticle(id)->SetDecayLength(idecay_length);
    }
    else {
        GetParticle(id)->SetDecayType(STABLE);
        GetParticle(id)->SetDecayLength(0.0);
    }
}

//______________________________________________________________________________
Double_t MyEvent::Pair_prob(Int_t id)
{
    // check if pair production is allowed and generate
    // a random decay length related to detector's material
    // radiation length (X0)
    Double_t p;

    if(GetParticle(id)->Energy() > 2.0 * m_e) {
        p = gRandom->Uniform(0.,1.0);
        return ((-9.)*fDetector.GetX0(fMatter)*TMath::Log(p)/7.);
    }
    return (-1.);
}

//______________________________________________________________________________
Double_t MyEvent::Brems_prob(Int_t id)
{
    // Check if bremsstrahlung is allowed and generate
    // a random decay length related to detector's material
    // radiation length (X0)
    Double_t p, retval;

    if(GetParticle(id)->Energy() > GetParticle(id)->GetMass()) {
        p = gRandom->Uniform(0.,1.0);
        retval = (-fDetector.GetX0(fMatter))*TMath::Log(p);
        return (retval);
    }
    else return (-1.);
}

//______________________________________________________________________________
void MyEvent::DeleteParticle(Int_t id)
{
    // Add this particle's energy loss at the total 
    // energy loss into the detector
    fDetector.AddELoss(GetParticle(id)->GetELoss());
    // Mark the particle's status as dead and decrement
    // the total alive particles
    GetParticle(id)->SetStatus(DEAD);
    fAliveParticles --;
}

//______________________________________________________________________________
Int_t MyEvent::FindFreeId(Int_t *FreeId)
{
    // give next available particle's id
    fTotalParticles++;
    *FreeId = fTotalParticles;
    if(fTotalParticles > fLast) fLast = fTotalParticles;
    return(ALIVE);
}

//______________________________________________________________________________
Int_t MyEvent::Particle_color(Int_t id)
{
    // return color index related to particle's energy
    Int_t i;
    for(i=0;i<10;i++)
        if(GetParticle(id)->Energy() > E_thresh[i]) break;
    if(i > 9) i = 9;
    return(gColIndex + i);
}

//______________________________________________________________________________
void MyEvent::ScatterAngle(Int_t id)
{
    // compute scatter angle into the detector's material
    // for the current particle
    // for more infos, please refer to the particle data booklet
    // from which the formulas has been extracted : 
    // Multiple scattering through small angles
    Double_t alpha,beta;
    Double_t abs_p,p1,p2,r_2;
    Double_t fact1,fact2;

    do {
        p1 = gRandom->Uniform(-1.0, 1.0);
        p2 = gRandom->Uniform(-1.0, 1.0);
        r_2 = (p1 * p1) + (p2 * p2);
    } while(r_2 > 1.0);
    abs_p = GetParticle(id)->P();
    alpha = TMath::Sqrt(-2.0 * TMath::Log(r_2) / r_2) * fDetector.GetTheta0(fMatter) / abs_p;
    beta  = gRandom->Uniform(0.0, 2.0 * TMath::Pi());
    alpha *= p1;
    TVector3 x_0(GetParticle(id)->GetvMoment().Orthogonal());
    TVector3 p_0(GetParticle(id)->GetvMoment() * (1.0 / abs_p));
    TVector3 y_0(x_0.Cross(p_0));
    fact1 = TMath::Sin(alpha);
    fact2 = fact1 * TMath::Cos(beta);
    fact1 *= TMath::Sin(beta);
    TVector3 vtmp1(x_0 * fact1);
    TVector3 vtmp2(y_0 * fact2);
    TVector3 vtmp3(vtmp2 + p_0);

    GetParticle(id)->SetMoment(vtmp1 + vtmp3);
    GetParticle(id)->SetMoment(GetParticle(id)->GetvMoment() *
        (abs_p/ GetParticle(id)->P()));
}

//______________________________________________________________________________
Int_t MyEvent::CheckDecayTime(Int_t id)
{
    if ( (GetParticle(id)->GetPdgCode() == PHOTON) ||
         (GetParticle(id)->GetPdgCode() == ELECTRON) ||
         (GetParticle(id)->GetPdgCode() == POSITRON ))
         return 0;
    Double_t timeofdecay = GetParticle(id)->GetTimeOfDecay();
    if(timeofdecay == 0.0)  return 0;
//    if(timeofdecay == 0.0)  timeofdecay = gRandom->Uniform(1.0e-9, 1.0e-6);
    Double_t distToDecay = timeofdecay * 0.996 * CSpeed;
    // check if actual particle life is greater than particle life time
    if (GetParticle(id)->GetPassed() >= distToDecay) {
        return 1;
    }
    return 0;
}

//______________________________________________________________________________
Int_t MyEvent::Decay(Int_t id)
{
    Char_t   strtmp[80];
    Double_t ratio;
    Int_t    d_num[5];
    Int_t    n_daughters;
    Int_t    ptype[5];
    Double_t mass[5];
    Int_t    i, index;
    Double_t sumBR = 0.0;
    MyParticle *Particle[5];
    MyParticle *part;

    // compute total branching ratio
    for(i=0;i<GetParticle(id)->GetPDG()->NDecayChannels();i++) {
        sumBR += GetParticle(id)->GetPDG()->DecayChannel(i)->BranchingRatio();
    }
    // choose random decay in respect to the branching ratio
    float r = gRandom->Uniform(sumBR);
    index = 0;
    while ((r -= GetParticle(id)->GetPDG()->DecayChannel(index)->BranchingRatio()) > 0
        && index < GetParticle(id)->GetPDG()->NDecayChannels()) index++;

    // set number of daughters
    n_daughters = GetParticle(id)->GetPDG()->DecayChannel(index)->NDaughters();
    for(i=0;i<n_daughters;i++) {
        // create temporary child particle to obtain its mass
        ptype[i] = GetParticle(id)->GetPDG()->DecayChannel(index)->DaughterPdgCode(i);
        Particle[i] = new MyParticle(0,ptype[i], CREATED, UNDEFINE,
            GetParticle(id)->GetvLocation(), GetParticle(id)->GetvMoment());
        mass[i] = Particle[i]->GetMass();
        delete Particle[i];
    }

    // find ids for children
    for(i=0;i<n_daughters;i++) {
        if(FindFreeId(&d_num[i]) == DEAD) return -1;
    }
    // total children mass
    Double_t total_mass = 0.0;
    for(i=0;i<n_daughters;i++) {
        total_mass += mass[i];
    }

    // compute energy ratio
    ratio = TMath::Sqrt(((GetParticle(id)->Energy() * GetParticle(id)->Energy())/(2*(n_daughters+1)))
        - (total_mass * total_mass)) / GetParticle(id)->P();

    for(i=0;i<n_daughters;i++) {

        // create child
        part = AddParticle(d_num[i], ptype[i], GetParticle(id)->GetvLocation(),
                    GetParticle(id)->GetvMoment() * ratio);
        part->SetFirstMother(id);
        // generate time of decay (may be useful in this case)
        part->GenerateTimeOfDecay();
        GetParticle(id)->SetChild(i, d_num[i]);
        // add a track related to this child
        AddTrack(d_num[i], id);

        // add a child related list tree item to the event list tree
        gTmpLTI = gEventListTree->AddItem(gLTI[id],
            part->GetName());
        sprintf(strtmp,"%1.2e GeV",part->Energy());
        gEventListTree->SetToolTipItem(gTmpLTI, strtmp);
        gLTI[d_num[i]] = gTmpLTI;

    }
    // increment number of children by the number of created particles
    GetParticle(id)->SetNChildren(n_daughters);
    return(n_daughters);

}

void MyEvent::CheckMatter(Int_t id)
{
    TGeoNode *Node = gGeoManager->FindNode(
              GetParticle(id)->GetvLocation().x(),
              GetParticle(id)->GetvLocation().y(),
              GetParticle(id)->GetvLocation().z());
    if(Node) fMatter = Node->GetNumber();
}
