using namespace std;
#include <sstream>
#include <iostream>
#include <iomanip>
#include "PDalitzDecay.h"
ClassImp(PDalitzDecay)
PDalitzDecay::PDalitzDecay()  {
};
PDalitzDecay::PDalitzDecay(const Char_t *id, const Char_t *de, Int_t key) :
    PChannelModel(id, de, key) {
    if (is_channel < 0)
	Warning("PDalitzDecay", "This model should be bound to CHANNELS only");
    
    alpha = 1./137.0359895;
    mass_pi0 = makeStaticData()->GetParticleMass("pi0");
    mass_n   = makeStaticData()->GetParticleMass("n");
    mass_eta = makeStaticData()->GetParticleMass("eta");
    mass_p   = makeStaticData()->GetParticleMass("p");
    p1   = 4.*alpha/(3.*TMath::Pi());
    p2   = 0.5*p1;
    p3   = 0.5*p2*alpha;
    
    Int_t tid[11];
    tid[0] = 10; 
    makeStaticData()->GetDecayModeByKey(primary_key, tid); 
    parent_id = makeStaticData()->GetDecayParentByKey(primary_key);
    if (tid[0] != 2) 
	Warning("PDalitzDecay", "Only 2 body decay");
    
    if (makeStaticData()->IsParticle(tid[1], "dilepton") ||
	makeStaticData()->IsParticle(tid[2], "dilepton")) 
	sw=0;
    else if (makeStaticData()->IsParticle(tid[1], "dimuon") ||
	     makeStaticData()->IsParticle(tid[2], "dimuon")) 
	sw=1;
    else 
	Warning("PDalitzDecay", "%s: No dilepton/dimuon", de);
    if (makeStaticData()->IsParticle(tid[1], "dilepton") ||
	makeStaticData()->IsParticle(tid[1], "dimuon")) {
	dilepton_position = 1;
	dilepton_pid      = tid[1];
	others_position   = 2;
	other_pid         = tid[2];
    } else {
	dilepton_position = 2;
	dilepton_pid      = tid[2];
	others_position   = 1;
	other_pid         = tid[1];
    }
    if (sw==0) { 
	mass_e  = makeStaticData()->GetParticleMass("e-"); 
	mass_ee = 2.*mass_e;
    } 
    else { 
	mass_e  = makeStaticData()->GetParticleMass("mu-"); 
	mass_ee = 2.*mass_e;
    } 
    
    mass_x = 0.;
    if (makeStaticData()->IsParticle(parent_id, "pi0") ||
	makeStaticData()->IsParticle(parent_id, "eta") ||
	makeStaticData()->IsParticle(parent_id, "eta'") ||
	makeStaticData()->IsParticle(parent_id, "J/Psi")) 
	flag = 1;
    else if (makeStaticData()->IsParticle(parent_id, "w")) {
	flag   = 2;
	mass_x = mass_pi0;
    } else if (makeStaticData()->IsParticle(parent_id, "D0") || 
	     makeStaticData()->IsParticle(parent_id, "NS110")) {
	flag   = 3;
	mass_x = mass_n;
    } else if (makeStaticData()->IsParticle(parent_id, "D+") || 
	     makeStaticData()->IsParticle(parent_id, "NS11+")) {
	flag   = 3;
	mass_x = mass_p;
    } else if (makeStaticData()->IsParticle(parent_id, "pn")) { 
	flag   = 3;
	mass_x = mass_p+mass_n;
    } else if (makeStaticData()->IsParticle(parent_id,"phi")) {
	flag   = 2;
	mass_x = mass_eta; 
    } else {
	flag = 4;
    }
    
    pi0        = makeStaticData()->GetParticleID("pi0");
    eta        = makeStaticData()->GetParticleID("eta"); 
    eta_prime  = makeStaticData()->GetParticleID("eta'");
    w          = makeStaticData()->GetParticleID("w"); 
    Delta_0    = makeStaticData()->GetParticleID("D0"); 
    Delta_plus = makeStaticData()->GetParticleID("D+"); 
    phi        = makeStaticData()->GetParticleID("phi");
    S11_0      = makeStaticData()->GetParticleID("NS110"); 
    S11_plus   = makeStaticData()->GetParticleID("NS11+");
    useQED = 0;
    flatMD = 0;
    if (sw == 0) 
	ml = makeStaticData()->GetParticleMass("dilepton");    
    else 
	ml = makeStaticData()->GetParticleMass("dimuon");
    mass_parent = makeStaticData()->GetParticleMass(parent_id); 
    draw_parent_mass = -1.; 
    rejection_flag = (makeStaticData()->IsParticle(parent_id,"eta'")) ? 1 :   
	((makeStaticData()->IsParticle(parent_id,"w")) ? 2 :     
	 0);                           
    photon_br = PhotonBR(parent_id);
    integral  = NULL;
    formfactor_model = NULL;
};
PDistribution* PDalitzDecay::Clone(const char *) const {
    return new PDalitzDecay((const PDalitzDecay &)* this);
};
Bool_t PDalitzDecay::Init(void) {
    
    
    
    parent = GetParticle("parent");
    if (!parent) {
	Warning("Init", "Parent not found");
	return kFALSE;
    }
    PParticle *daughter1 = GetParticle("daughter");
    PParticle *daughter2 = GetParticle("daughter");
    if ((daughter1->ID() == makeStaticData()->GetParticleID("dilepton")) ||
	(daughter1->ID() == makeStaticData()->GetParticleID("dimuon"))) {
	other    = daughter2;
 	dilepton = daughter1;
    }
    else {
	other    = daughter1;
 	dilepton = daughter2;
    }
    
    return kTRUE;
};
Bool_t PDalitzDecay::FreezeOut(void) {
    formfactor_model = GetSecondaryModel("formfactor");
    return kTRUE;
};
int PDalitzDecay::GetDepth(int i) {
    if (i) return -1; 
    return 0;
}
Bool_t PDalitzDecay::SampleMass(void) {
    
    Double_t masses[3]; 
    
    masses[0]                 = parent->M();
    masses[others_position]   = other->M();
    masses[dilepton_position] = dilepton->M();
    SampleMass(&masses[0]); 
    other->SetM(masses[others_position]);
    
    dilepton->SetM(masses[dilepton_position]);
    return kTRUE;
};
Bool_t PDalitzDecay::SampleMass(Double_t *mass, Int_t *) {
    sampleMD(mass[0], parent_id, 
	     mass[dilepton_position], mass[others_position]);
    return kTRUE;
};
Double_t PDalitzDecay::GetWeight(void) {
    
    Double_t masses[3]; 
    
    masses[0] = parent->M();
    masses[others_position]   = other->M();
    masses[dilepton_position] = dilepton->M();
#if 0
    
    
    
    if (!integral) {
	Int_t maxmesh=200;
	Double_t mmin=PData::LMass(parent->ID());   
	Double_t mmax=PData::UMass(parent->ID());   
	
	Double_t dm=(mmax-mmin)/(maxmesh-1.); 
	integral = new PMesh(maxmesh,"mesh2");
	integral->SetMin(mmin);
	integral->SetMax(mmax);
	for (int j=0;j<maxmesh;++j) {  
	    Double_t local_int=0;
	    Double_t mm=mmin+j*dm;            
	    for (int i=0;i<1000;i++) {
		sampleMD(mm, parent_id, 
			 masses[dilepton_position], masses[others_position]);
		local_int+=dGdM(parent_id,masses[dilepton_position],mm);
	    }
	    integral->SetNode(j,local_int/1000.);
	    
	}
	masses[0]=parent->M();
	masses[others_position]=other->M();
	masses[dilepton_position]=dilepton->M();
    }
#endif
    
    
    
     return (GetWeight(&masses[0]) / 
	     makeDynamicData()->GetParticleTotalWidth(parent->M(),parent_id));
     
     
};
Double_t PDalitzDecay::GetWeight(Double_t *mass, Int_t *) {
    Double_t dgdm = dGdM(parent_id, mass[dilepton_position], mass[0]);
    
    return dgdm;
};
Bool_t PDalitzDecay::GetWidth(Double_t mass, Double_t *width, Int_t) {
    if (makeStaticData()->GetPWidx(is_channel) == -1) 
	return 0.; 
    if (!makeStaticData()->GetPWidx(is_channel) || width_init == 0) { 
	width_init++;
	makeDynamicData()->GetParticleDepth(parent_id); 
	mmin=makeStaticData()->GetDecayEmin(is_channel);  
	Double_t w0 = 1.;      
	
	if (PData::IsMDalitz(is_channel)) 
	    w0 = photon_br;
	
	mmax = PData::UMass(parent_id);                
	Double_t dm = (mmax-mmin)/(maxmesh-3.);          
	double mass_threshold, mass_ceiling;
	mass_threshold = PData::LMass(dilepton_pid);
	mass_ceiling   = PData::LMass(other_pid);
#ifdef INGO_DEBUG
	Info("GetWidth", "Creating mesh in %s (%f,%f)", makeStaticData()-> GetDecayName(is_channel),
	     mass_threshold, mass_ceiling);
#endif
    
	mesh = new PMesh(maxmesh-2, "mesh");
	for (int i=0; i<maxmesh-2; ++i) {
	    Double_t mm = mmin+i*dm;   
	    Double_t temp0 = 0.;
	    mc_max = 10000;
	    for (int mc=0; mc<mc_max; mc++)  {
		Double_t running_ee_mass = mass_threshold+((Double_t(mc)/Double_t(mc_max))
							   *(mm-mass_threshold-mass_ceiling));
		if (running_ee_mass > mass_threshold)
		    temp0 += dGdM(parent_id, running_ee_mass, mm);
	    }
	    temp0 /= mc_max;
	    temp0 *= w0;
	    temp0 *= (mm-mass_ceiling);
	    mesh->SetNode(i, temp0);
	}
	mesh->SetMin(mmin);                 
	mesh->SetMax(mmax);                 
	makeStaticData()->SetPWidx(is_channel, 1);  
    } 
    if (mesh)  {
	*width = mesh->GetLinearIP(mass);
	return kTRUE;
    }
    return kFALSE;
};
Double_t PDalitzDecay::EvalPar(const Double_t *x, const Double_t *) {
    return Eval(x[0]);
};
 
Double_t PDalitzDecay::Eval(Double_t x, Double_t, Double_t, Double_t) const {
    
    Double_t res;
    Double_t mass[3];
    mass[0] = makeStaticData()->GetParticleMass(parent_id); 
    if (draw_parent_mass > 0) 
	mass[0] = draw_parent_mass;
    mass[dilepton_position] = x;
    mass[others_position]   = mass_x;
    if (draw_option == 0) {
	return ((PChannelModel*)this)->GetWeight(mass);
	
    }
    if (draw_option == 1) {
	((PChannelModel*)this)->GetWidth(x, &res);
	return res;
    }
    if (draw_option == 2) {
	((PChannelModel*)this)->GetBR(x, &res);
	return res;
    }
    return 0;
};
double PDalitzDecay::dGdM(const int &id, const double &m, const double &ecm) {
    
    
    
    
    
    
    
    
    if (m<mass_ee || m>ecm-mass_x) return 0.;
    
    double f1 = mass_e/m, 
	f2 = f1*f1, 
	a = 1.-4.*f2,
	b = 1.+2.*f2, 
	y = sqrt(a)*b/m, c, 
	z = -1;
    switch (flag) {
    case 1:                 
	c = m/ecm;
	z = 1.-c*c;
	z *= p1*z*z;          
		
	break;
    case 2:                 
	c = ecm*ecm;
	c = m/(c-mass_x*mass_x);
	z = 1.+m*c;
	z *= z;
	c *= 2.*ecm;
	z -= c*c;
	z *= p2*sqrt(z);      
	break;
    case 3:                 
	z = p3*PKinematics::pcms(ecm,mass_x,m)/(ecm*ecm);
	
	
	
	break;
    case 4:
	Warning("dGdM", "Unknown decay");
	
	break;
    }
    return y * z * FDalitz(id, m, ecm);
}
double PDalitzDecay:: FDalitz(const int &id, const double &m, double ecm) {
    
    
    double f = 0., m2 = m*m;
    if (id==Delta_0 || id==S11_0 || id==Delta_plus || id==S11_plus) {
	
	Double_t mn = 0.;
	if (id==Delta_0 || id==S11_0) 
	    mn = mass_n;
	else if (id==Delta_plus || id==S11_plus) 
	    mn = mass_p;
	double mm = ecm+mn;
	double ff = 2.7;
	if (formfactor_model)
	    ff = formfactor_model->GetWeight((Double_t*)&m);
	f = ff*mm/(2.*mn*(mm*mm-m2));
	double md = ecm, md2 = md*md, md3 = md2*md, md4 = md3*md,
	    mn2 = mn*mn, mn4 = mn2*mn2, m4 = m2*m2,
	    sum = ((md-mn)*(md-mn)-m2)*
	    (7.*md4 + 14.*md2*m2 + 3.*m4 + 8.*md3*mn + 2.*md2*mn2 + 
	     6.*m2*mn2 + 3.*mn4);
	return sum*f*f;
    }
    if (useQED) return 1.;   
    
    if (formfactor_model) {
	double ff = formfactor_model->GetWeight((Double_t*)&m);
	return ff;
    }
    if (id == pi0) {
	f = (1.+5.5*m2);
	return f*f;
    } else if (id == eta) {
	f = 1.-m2/0.5184;
	return 1./(f*f);
    } else if (id == eta_prime) 
	return 0.33939776/( (0.5776-m2)*(0.5776-m2) + 0.005776);
    else if (id == w)
	return 0.17918225/( (0.4225-m2)*(0.4225-m2) + 0.000676);
    else if (id == phi) {
	f = 1.-m2/(0.77*0.77);
	return 1./(f*f); 
    } else if (id == makeStaticData()->GetParticleID("J/Psi")) 
	return 1.; 
    
    return 0.;
}
void PDalitzDecay::sampleMD(const double &ecm, const int &id, 
			    double &m, const double &m1) {
    
    
    
    
    
    
    
    if (flatMD) {    
	m = ml + (ecm-ml-m1)*PUtils::sampleFlat();
	return;
    }
    
    
    
    if (id == eta_prime) {
	m = GetRandom();
	return;
    }
    
    if (id == w && sw==1) {
	m = GetRandom();
	return;
    }
    int stored_rejection_flag = rejection_flag;
    if (rejection_flag == 2) 
	if (ecm <= mass_parent)
	    
	    rejection_flag = 0;
    double mx = ecm-m1;                  
    double p1 = dGdM(id, ml, ecm);      
    double b = ml, dw = ecm-mass_parent, area;      
    double p2 = -1, mi, a, y, bw = -1, a1 = -1, a2, f;
    if (rejection_flag == 0 && sw == 1) 
	p1 = dGdM(id,ml+0.1,ecm);  
    if (rejection_flag == 1) b /= ecm;             
    if (rejection_flag == 2 && ecm > mass_parent) {  
	mi = mass_parent - m1;       
	p2 = (ecm+ml-mi)*dGdM(id,mi,ecm);
	a1 = (p1+p2)*log(mi/ml);         
	bw = p1/mi + p2/(dw+ml);
	a2 = 0.5*bw*dw;                  
	area = a1 + a2;                  
    } else {                             
	area = log(mx/ml);
	if (rejection_flag == 1) area *= 2.;         
    }
  
    
    
    
    
    
  
    do {                               
	a = area * PUtils::sampleFlat(); 
	if (rejection_flag != 2) {
	    m = b * exp(a);
	    if (rejection_flag == 1) m *= (ecm+ml)/(1.+m);
	    f = p1/m;
	    if (rejection_flag == 1) f += p1/(ecm+ml-m);
	} else if (a > a1) {
	    m = 2.*mx*(area-a)/bw;
	    m = mx - sqrt(m);
	    f = -bw*(m-mx)/dw;
	} else {
	    m = ml*exp(a/(p1+p2));
	    f = p1/m+p2/(mx+ml-m);
	}
	y = dGdM(id, m, ecm);          
    
    } while (f*PUtils::sampleFlat() > y);
  
    rejection_flag = stored_rejection_flag;
    if (f < y) m = ml; 
};
double PDalitzDecay:: PhotonBR(const int &id) {
  
  
  
    
    int i, np, i1, i2;
    
    int x = 0;   
    if (makeStaticData()->IsParticle(id,"pi0")||
	makeStaticData()->IsParticle(id,"eta")||
	makeStaticData()->IsParticle(id,"eta'")) 
	x = makeStaticData()->GetParticleID("g");
    else if (makeStaticData()->IsParticle(id,"D0") || 
	     makeStaticData()->IsParticle(id,"Lambda") || 
	     makeStaticData()->IsParticle(id,"NS110") || 
	     makeStaticData()->IsParticle(id,"ND130")) 
	x = makeStaticData()->GetParticleID("n");
    else if (makeStaticData()->IsParticle(id,"D+")   ||
	     makeStaticData()->IsParticle(id,"NP11+") ||
	     makeStaticData()->IsParticle(id,"ND13+") ||
	     makeStaticData()->IsParticle(id,"NS11+")) 
	x = makeStaticData()->GetParticleID("p");
    else if (makeStaticData()->IsParticle(id,"w")||
	     makeStaticData()->IsParticle(id,"phi")) 
	x = makeStaticData()->GetParticleID("pi0");
    Int_t tid[11];
    Int_t *didx;
    Int_t listkey = -1;
    int key = makeDataBase()->GetEntryInt("pid", id);
    while (makeDataBase()->MakeListIterator
	   (key, "pnmodes", "link", &listkey)) {	
	makeDataBase()->GetParamInt(listkey, "didx", &didx); 
	tid[0] = 10;
	makeStaticData()->GetDecayModeByKey(listkey, tid); 
	int pos = makeStaticData()->GetDecayIdxByKey(listkey);
	
	for (i=0; i<tid[0]; ++i) {            
	    np = tid[0];                    
	    if (np == 2) {                   
		i1 = tid[1];                  
		i2 = tid[2];
		if ((makeStaticData()->IsParticle(i1,"g")
		     &&i2 == x)||    
		    (makeStaticData()->IsParticle(i2,"g")
		     &&i1 == x)) return makeStaticData()->GetDecayBR(pos);
	    };
	}
    }
    
    return 0.;                       
};
void PDalitzDecay::Print(const Option_t *) const {  
    
    BasePrint();
    if (useQED) 
	cout << "Uses QED form factor" << endl;
    else 
	cout << "Uses VMD form factor" << endl;
}