using namespace std;
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cmath>
#include "PHadronDecayM1.h"
PHadronDecayM1::PHadronDecayM1()  {
};
PHadronDecayM1::PHadronDecayM1(const Char_t *id, const Char_t *de, Int_t key) :
    PHadronDecay(id, de, key) {
    if (is_channel < 0)
	Warning("PHadronDecayM1", "The model (%s) should be bound to CHANNELS only", de);
  
    
    Int_t tid[11];
    tid[0] = 10; 
    makeStaticData()->GetDecayModeByKey(key, tid); 
    parent_id = makeStaticData()->GetDecayParentByKey(key);
    if (tid[0] != 2) 
	Warning("PHadronDecayM1", "(%s):  Only 2 body decay", de);
    
    
    if (makeStaticData()->GetParticleTotalWidth(tid[2]) >
	makeStaticData()->GetParticleTotalWidth(tid[1])) {
	unstable_position = 2;
	stable_position   = 1;
    } else{
	unstable_position = 1;
	stable_position   = 2;
    }
	
    unstable_mass = makeStaticData()->GetParticleMass(tid[unstable_position]); 
    
    unstable_ml = PData::LMass(tid[unstable_position]);       
    
    stable_mass = makeStaticData()->GetParticleMass(tid[stable_position]); 
    unstable_id = tid[unstable_position];
    stable_id   = tid[stable_position];
    scale = 1.1;  
    
    id1 = tid[1];
    id2 = tid[2];
    parent    = NULL;
    daughter1 = NULL;
    daughter2 = NULL;
    model1    = NULL;
    model2    = NULL;
    didx1         = -1;
    didx2         = -1;
    didx_unstable = -1;
};
PDistribution *PHadronDecayM1::Clone(const char*) const {
    return new PHadronDecayM1((const PHadronDecayM1 &)* this);
};
Bool_t PHadronDecayM1::Init(void) {
    
    
    parent = GetParticle("parent");
    if (!parent) {
	Warning("Init", "Parent not found");
	return kFALSE;
    }
    
    
    daughter1 = GetParticle(makeStaticData()->GetParticleName(id1));
    daughter2 = GetParticle(makeStaticData()->GetParticleName(id2));
    return kTRUE;
}
Double_t PHadronDecayM1::EvalPar(const Double_t *x, const Double_t *params) {
    if (params) {
	draw_option = (int)params[0];
	didx_option = (int)params[1];
    }
    return Eval(x[0]);
}
 
Double_t PHadronDecayM1::Eval(Double_t x, Double_t, Double_t, Double_t) const {
    Double_t res;
    Double_t mass[3];
    Int_t didx[3];
    didx[0] = didx[1] = didx[2] = -1;
    
    PChannelModel *pmodel = makeDynamicData()->GetParticleModel(parent_id);
    mass[unstable_position] = x;
    mass[stable_position]   = stable_mass;
    didx[unstable_position] = didx_option;
    
    if (draw_option == 0) {
	if (!pmodel)
	    return ((PChannelModel*)this)->GetWeight(mass, didx);
	Double_t w  = 0;
	Double_t dm = PData::UMass(parent_id)-PData::LMass(parent_id);
	for (mass[0]=PData::LMass(parent_id); mass[0]<(PData::LMass(parent_id)+dm);
	     mass[0]+=dm/100.) { 
	    w += ((PChannelModel*)this)->GetWeight(mass,didx)*pmodel->GetWeight(mass,(Int_t*)&is_channel);
	}
	return w;
    } else if (draw_option == 1) {
	((PChannelModel*)this)->GetWidth(x,&res);
	return res;
    } else if (draw_option == 2) {
	((PChannelModel*)this)->GetBR(x,&res);
	return res;
    }
    return 0;
}
int PHadronDecayM1::GetDepth(int i) {
    
    Int_t a1=0, a2=0;
    model1 = makeDynamicData()->GetParticleModel(id1);
    model2 = makeDynamicData()->GetParticleModel(id2);
    if (model1) a1 = model1->GetDepth(i);
    if (model2) a2 = model2->GetDepth(i);
    
    makeStaticData()->SetDecayEmin(is_channel, 
				   TMath::Min(makeStaticData()->GetParticleEmin(id1),
					      makeStaticData()->GetParticleEmin(id2)));
    
    return TMath::Max(a1+1, a2+1); 
}
Bool_t PHadronDecayM1::Prepare(void) {
    
    
    didx1 = daughter1->GetDecayModeIndex(1);
    didx2 = daughter2->GetDecayModeIndex(1);
    
    if (unstable_position == 1)
	didx_unstable = didx1;
    else
	didx_unstable = didx2;
    return kTRUE;
}
void PHadronDecayM1::SubPrint(Int_t) const {
    
    if (model1) {cout << " "; cout << model1->GetDescription();}
    if (model2) {cout << " "; cout << model2->GetDescription();}
}
Double_t PHadronDecayM1::GetWeight(void) {
    Double_t mass[3];
    mass[0] = parent->M();
    mass[1] = daughter1->M();
    mass[2] = daughter2->M();
    return GetWeight(mass); 
}
Double_t PHadronDecayM1::GetWeight(Double_t *mass, Int_t *didx) {
    
    
    
    
    
    int didx_local1 = didx1,
	didx_local2 = didx2;
    if (didx) {
	didx_local1 = didx[1];
	didx_local2 = didx[2];
    }
    if (unstable_position == 1)
	return BWWeight(unstable_id, mass[0], mass[1], mass[2], didx_local1, 0, didx_local2); 
    return BWWeight(unstable_id, mass[0], mass[2], mass[1], didx_local2, 0, didx_local1); 
}
Bool_t PHadronDecayM1::SampleMass(void) {
    
    Double_t mass[3];
    mass[0] = parent->M();
    if (!SampleMass(mass)) return kFALSE;
    daughter1->SetM(mass[1]);
    daughter2->SetM(mass[2]);
    return kTRUE;
};
Bool_t PHadronDecayM1::SampleMass(Double_t *mass, Int_t *didx) {
    
    
    
    
    
    if (didx) 
	didx_unstable = didx[unstable_position]; 
    Double_t ecm = mass[0];    
    if (sampleM1(ecm)) {
	mass[unstable_position] = unstable_dynamic_mass;
	mass[stable_position]   = stable_mass; 
    } else {
	Warning("SampleMass", "failed in [%s], ecm=%f", GetIdentifier(), ecm);
	return kFALSE;
    }
    if (ecm < (stable_mass+unstable_dynamic_mass)) {
	Warning("SampleMass", "Violation of energy");
	return kFALSE;
    }
    return kTRUE;
};
Bool_t PHadronDecayM1::GetWidth(Double_t mass, Double_t *width, Int_t) {
    if (makeStaticData()->GetPWidx(is_channel) == -1) 
	return 0.; 
    if (!makeStaticData()->GetPWidx(is_channel)) { 
	Info("GetWidth", "Called for %s", makeStaticData()->GetDecayName(is_channel));
	makeDynamicData()->GetParticleDepth(parent_id); 
	mmin = makeStaticData()->GetDecayEmin(is_channel);  
	Double_t w0 = makeStaticData()->GetDecayBR(is_channel);      
	
	mmax = PData::UMass(parent_id);                
	Double_t dm = (mmax-mmin)/(maxmesh-3.);          
	double mass_threshold, mass_ceiling;
	mass_threshold = PData::LMass(unstable_id);
	mass_ceiling   = mmax-PData::LMass(stable_id);
    
	mesh = new PMesh(maxmesh-2, "mesh");
	for (int i=0; i<maxmesh-2; ++i) {
	    Double_t mm = mmin+i*dm;                     
	    Double_t temp0      = 0.;
	    Double_t temp0_norm = 0.;
	    
	    
	    
	    
	    Double_t bw_max = 0;
	    for (int mc=0; mc<mc_max; mc++) {
		Double_t running_unstable_mass = mass_threshold+((Double_t(mc)/Double_t(mc_max))
								 *(mass_ceiling-mass_threshold));
		Double_t w = makeDynamicData()->GetParticleTotalWeight(running_unstable_mass ,unstable_id);
		if (w > bw_max) 
		    bw_max = w;
	    }
	    
	    if (bw_max > 0) 
		for (int mc=0; mc<mc_max; mc++) {
		    Double_t temp1 = 0.;
		    
		    
		    Double_t running_unstable_mass = mass_threshold+((Double_t(mc)/Double_t(mc_max))
								     *(mass_ceiling-mass_threshold));
		    if (mm > (running_unstable_mass+stable_mass)) {
			
			temp1 = PKinematics::pcms(mm, running_unstable_mass, stable_mass);
			
			Double_t w = makeDynamicData()->GetParticleTotalWeight(running_unstable_mass, unstable_id);
			temp1 *= w;
			if (w > (0.01*bw_max)) {  
			    
			    temp0_norm += temp1;
			    
			    temp1 *= HadronWidth(mm,running_unstable_mass, stable_mass);
			    temp0 += temp1;
			}
		    }
		}
	    if (temp0_norm > 0) 
		temp0 /= temp0_norm;
	    temp0 *= w0;
	    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;
}
bool PHadronDecayM1:: sampleM1(const double &ecm) {
    
    
    
    
    
    
    
    double unstable_mh = ecm-stable_mass, 
	ma, peak, a1, a2, area, b=-1, max1;
    unstable_dynamic_mass = 0.;       
    if (unstable_mh <= unstable_ml) {
	Warning("sampleM1", "not enough energy");
	return kFALSE;
    }
    abort = kFALSE;
 repeat:                            
    if (unstable_mh > unstable_mass) {  
	peak = BWWeight(unstable_id, scale*ecm, unstable_mass, stable_mass, didx_unstable);
	
	
	ma = 0.5*(unstable_mass+unstable_mh);     
	a2 = 0.5*peak*(unstable_mh-ma);           
	b  = peak/(unstable_mh - ma);             
    } else {                           
	peak = scale*maxBWWeight(unstable_id,ecm,unstable_ml,unstable_mh,max1,stable_mass, didx_unstable);
	
	ma = unstable_mh;                         
	a2 = 0.;                         
    }
    if (peak == 0.) { 
	
	unstable_dynamic_mass = 0.;
	
	
	return kTRUE;
    }
    a1   = peak*(ma-unstable_ml);      
    area = a1 + a2;                    
  
    
    
    double a, f, y, da;                
    
    
    
    
  
    do {                               
	a = area * PUtils::sampleFlat(); 
    
	if (a <= a1) {    
	    f = peak;   
	    unstable_dynamic_mass = a/f + unstable_ml;  
	    
	} else {        
	    da = 2.*(area-a);
	    f  = sqrt(da*b);                
	    
	    unstable_dynamic_mass = unstable_mh - da/f;     
	    
	}
    
	y = BWWeight(unstable_id, ecm, unstable_dynamic_mass, stable_mass, didx_unstable);         
    } while (f*PUtils::sampleFlat() > y);
  
    if (f < y) {         
	scale = scale*y/f + 0.1;         
	
	goto repeat;                     
    }
    return kTRUE;
}
double PHadronDecayM1::BWWeight(const int &i1, const double &m, 
				const double &m1, const double &m2, int didx_local1, 
				int i2, int didx_local2) {
    
    
    
    
    
    
    
    
    
    
    
    
  
    double pCms = PKinematics::pcms(m, m1, m2);
    PChannelModel *i1_model = makeDynamicData()->GetParticleModel(i1);
    if (!i1_model) {
	Warning("BWWeight", "No target model i1");
	return 0;
    }
 
    double bw = pCms*i1_model->GetWeight((Double_t*)&m1,&didx_local1);
    
    if (i2) { 
	PChannelModel *i2_model = makeDynamicData()->GetParticleModel(i2);
	if (!i2_model) {
	    Warning("BWWeight", "No target model i2");
	    return bw;
	}
	bw *= i2_model->GetWeight((Double_t*)&m2,&didx_local2);
    }
    return bw;
}
double PHadronDecayM1::maxBWWeight(const int &i1, const double &m, const double &lower,
				   const double &upper, double &max1, const double &m2, int didx_local1, 
				   int i2, int didx_local2) {
    
    
    
#define SHFT2(a,b,c) (a)=(b);(b)=(c);
#define SHFT3(a,b,c,d) (a)=(b);(b)=(c);(c)=(d);
    
    const double R   = 0.61803399;
    const double C   = 1.-R;
    const double tol = 1.e-3;
    double ax, bx, cx;
    double f1=0, f2=0, x0, x1, x2, x3;
    ax = lower;
    bx = C*lower+R*upper;
    cx = upper;
    x0 = ax;
    x3 = cx;
    if ( fabs(cx-bx)>fabs(bx-ax) ) {
	x1 = bx;
	x2 = bx+C*(cx-bx);
    } else {
	x2 = bx;
	x1 = bx-C*(bx-ax);
    }
    Int_t counter = 0;
    while ((f1 == 0) && (f2 == 0) && (counter < 10)) {
	f1 = BWWeight(i1, m, x1, m2, didx_local1, i2);
	f2 = BWWeight(i1, m, x2, m2, didx_local1, i2);
	if ((f1 == 0) && (f2 == 0)) 
	    x2 += (x3-x2)*0.1;
	counter++;
    } 
    if (counter == 10) {
	
	abort = kTRUE;
	return 0;
    }
    while ((fabs(x3-x0)>tol*(fabs(x1)+fabs(x2))) && (counter < 100)) {
	if (f2 > f1) {
	    SHFT3(x0, x1, x2, R*x1+C*x3);
	    SHFT2(f1, f2, BWWeight(i1, m, x2, m2, didx_local1, i2, didx_local2));
	} else {
	    SHFT3(x3, x2, x1, R*x2+C*x0);
	    SHFT2(f2, f1, BWWeight(i1, m, x1, m2, didx_local1, i2, didx_local2));
	}
    }
    if (counter == 100) {
	abort = kTRUE;
	return 0;
    }
    if (f1 > f2) {
	max1 = x1;
	return f1;
    } else {
	max1 = x2;
	return f2;
    }
}
ClassImp(PHadronDecayM1)