#include "PDynamicData.h"
#include "PStdData.h"
#include "PUtils.h"
#include "TString.h"
#include "TObjArray.h"
#include "TObjString.h"
#include "PDefines.h"
PDynamicData &fDynamicData() {
    static PDynamicData *ans = new PDynamicData();
    return *ans;
}
PDynamicData *makeDynamicData() {
    return &fDynamicData();
}
PDynamicData::PDynamicData() {
    
    PDataBase *base = makeDataBase();
    i_result = NULL;
    c_result = NULL;
    d_result = NULL;
    t_result = NULL;
    num_pchannels = 0;
    pid_param   = base->GetParamInt("pid");
    name_param  = base->GetParamString("name");
    model_param = base->GetParamTObj("model");
    didx_param  = base->GetParamInt("didx");
    scfactor_param = base->GetParamDouble("scfactor");
    sccount_param = base->GetParamInt("sccount");
    pnmodes_param = base->GetParamInt ("pnmodes");
    link_param    = base->GetParamInt ("link");
    enhance_br_param = base->GetParamDouble ("enhance_br");
    Info("PDynamicData()", "(%s)", PRINT_CTOR);
}
Double_t PDynamicData::GetDecayPartialWidth(Double_t mass, Int_t didx) {
    
    
    
    
    Double_t width;
    
    if (makeStaticData()->GetDecayEmin(didx) > mass) return 0;
    PChannelModel *model = GetDecayModel(didx);
    if (!model) {
	Error("GetDecayPartialWidth", "Model not found for decay index %i", didx);
	return makeStaticData()->GetDecayPartialWidth(didx);
    }
    Double_t scfactor = GetDecaySCFactor(didx);
    if (model->GetWidth(mass, &width)) return (width*scfactor);
    return makeStaticData()->GetDecayPartialWidth(didx)*scfactor;
}
double PDynamicData::GetParticleTotalWidthSum(Double_t mass, Int_t id, Int_t flag) {
    
    
    
    
    
    
    
    
    
    
    
    
    static double mo    = 0.;
    static double wt;
    static int io       = 0;
    static int old_flag = 0;
    if (!makeStaticData()->IsParticleValid(id)) { 
	Warning("GetParticleTotalWidthSum", "id %i out of range", id);
	return 0.;
    }
    int twidx = makeStaticData()->GetTWidx(id);
    if (!twidx) GetParticleDepth(id);  
    if (twidx == -1) {
	mo = mass;
	io = id;
	return wt = makeStaticData()->GetParticleTotalWidth(id);
	
    }
    
    if (io != id) {                        
	io = id;
	mo = 0.;
    }
    if (mo!=mass || flag!=old_flag) {     
	mo = mass;
	old_flag = flag;
	if (mass<PData::LMass(id) || mass>PData::UMass(id)) 
	    return 0.;    
	double g_tmp = 0.;         
	
	Int_t key = makeDataBase()->GetEntryInt("pid", id);
	Int_t *didx;
	Int_t listkey = -1;
	while (makeDataBase()->MakeListIterator
	       (key, "pnmodes", "link", &listkey)) {
	    makeDataBase()->GetParamInt(listkey, "didx", &didx); 
	    if (makeStaticData()->GetPWidx(*didx) == -1
		&& mass >= makeStaticData()->GetDecayEmin(*didx)) 
		
		g_tmp +=
		    makeStaticData()->GetDecayBR(*didx)*
		    makeStaticData()->GetParticleTotalWidth(id);
	    
	    else if (flag == 0) 
		g_tmp += GetDecayPartialWidth(mass, *didx);         
	    else if (makeStaticData()->IsDecayHadronic(*didx))
		g_tmp += GetDecayPartialWidth(mass, *didx);  
	    
	}
	wt = g_tmp;          
    }
    return wt;
}
double PDynamicData::GetDecayBR(int idx, double m) {
    
    PChannelModel *model = GetDecayModel(idx);
    if (!model) {	
	return makeStaticData()->GetDecayBR(idx);
    }
    Double_t br;
    Double_t tw = GetParticleTotalWidth(m, makeStaticData()->GetDecayParent(idx));
    if (model->GetBR(m, &br, tw)) return br;
    return makeStaticData()->GetDecayBR(idx);
}
void PDynamicData::ListDecayBR(int id, double m) {
    
    
    if (!makeStaticData()->IsParticleValid(id)) {  
	return;
    }
    printf("\n%s branchings for mass=%f GeV   [low|pole|up|w|w0]=[%f|%f|%f|%e|%e] GeV\n\n",
	   makeStaticData()->GetParticleName(id), PData::LMass(id),
	   makeStaticData()->GetParticleMass(id),
	   makeStaticData()->GetParticleMass(id), PData::UMass(id),
	   makeStaticData()->GetParticleTotalWidth(id), GetParticleTotalWidthSum(m,id));
    if (m<PData::LMass(id) || m>PData::UMass(id)) { 
	Warning("ListDecayBR", "mass out of range");
	return;
    }
    
    Int_t key = makeDataBase()->GetEntryInt("pid", id);
    Int_t listkey = -1;
    int i = 1;
    double sum = 0;
    while (makeDataBase()->MakeListIterator
	   (key, "pnmodes", "link", &listkey)) {
	int   pos = makeStaticData()->GetDecayIdxByKey(listkey);
	double br = GetDecayBR(pos,m);  
	sum += br;
	double pw = GetDecayPartialWidth(m, pos);
	printf("%d: idx=%d sw=%2d  width=%e GeV    br=%f %%  (Width1()=%e)\n",
	       i, pos, makeStaticData()->GetPWidx(pos),
	       makeStaticData()->GetDecayPartialWidth(pos),
	       100.*br, pw);
	i++;
    }
    printf("\nTotal width=%e GeV   Sum(br)=%f %%\n", GetParticleTotalWidthSum(m,id), 100.*sum);
}
int PDynamicData::PickDecayChannel(const int &id, const double &m) {
    
    
    
    if (!makeStaticData()->IsParticleValid(id)) {
	
	Warning("PickDecayChannel","id %i out of range", id);
	return -1;
    } else if (m<PData::LMass(id) || m>PData::UMass(id)) return -2; 
    
    int n = makeStaticData()->
	GetParticleNChannels(id); 
    
    if (!n) return -3;                        
    double r = PUtils::sampleFlat(), br = 0.; 
    double sum = 0.;
    Int_t key = makeDataBase()->GetEntryInt(pid_param, id);
    Int_t *didx;
    Int_t listkey = -1;
    while (makeDataBase()->MakeListIterator
	   (key, pnmodes_param, link_param, &listkey)) {	
	makeDataBase()->GetParamInt 
	    (listkey, didx_param , &didx); 
	sum+=GetDecayBR(*didx,m)*makeStaticData()->GetEnhanceChannelBR(*didx); 
	
    }
    if (sum <= 0.) {
	Warning("PickDecayChannel", "sum=%f\n", sum);
	return -4;
    }
    listkey = -1;
    while (makeDataBase()->MakeListIterator(key, pnmodes_param, link_param,  &listkey)) {
	if (!makeDataBase()->GetParamInt(listkey, didx_param , &didx)) {
	    Warning("PickDecayChannel", "didx not found for listkey %i", listkey);
	}
	br += GetDecayBR(*didx,m)*makeStaticData()->GetEnhanceChannelBR(*didx);    
	if (r < br/sum) {
	    return *didx; 
	}
    }
    
    Warning("PickDecayChannel", "id=%d sum=%f\n", id, sum);
    return -5;
}
int PDynamicData::PickDecayChannel(const int &id, const double &m, int *array) {
    
    
    
    
    if (!array) {  
	Warning("PickDecayChannel", "Invalid address of array");
	return -1;
    } else if (!makeStaticData()->IsParticleValid(id)) {            
	
	Warning("PickDecayChannel", "id %i out of range", id);
	return -1;
    }
    
    array[0] = 0;  
    Int_t p  = PickDecayChannel(id, m);
    array[0] = 10; 
    if (p >= 0) makeStaticData()->GetDecayMode(p, array); 
    
    return p;
}
Double_t PDynamicData::GetParticleScalingFactor(Int_t didx) {
    if (! makeDataBase()->GetParamDouble (pid_param, didx, scfactor_param, &d_result)) {
	return 1.;
    }
    return *d_result;
}
void PDynamicData::SetParticleScalingFactor(Int_t didx, Double_t factor) {
    if (! makeDataBase()->GetParamDouble (pid_param, didx, scfactor_param, &d_result)) {
	makeDataBase()->SetParamDouble (makeDataBase()->GetEntryInt(pid_param, didx) , "scfactor", new Double_t(factor));
	return;
    }
    *d_result *= factor;
}
Double_t PDynamicData::GetDecaySCFactor(Int_t didx) {
    if (! makeDataBase()->GetParamDouble (didx_param, didx, scfactor_param, &d_result)) {
	return 1.;
    }
    return *d_result;
}
void PDynamicData::SetDecaySCFactor(Int_t didx, Double_t factor) {
    if (! makeDataBase()->GetParamDouble (didx_param, didx, scfactor_param, &d_result)) {
	Warning("SetDecaySCFactor", "factor not present");
	return;
    }
    *d_result *= factor;
}
bool PDynamicData::CheckSCAbort(Int_t didx) {
    
    if (! makeDataBase()->GetParamInt (didx_param, didx , sccount_param, &i_result)) {
	makeDataBase()->SetParamInt (makeDataBase()->GetEntryInt(didx_param, didx) , 
				     "sccount", new Int_t(10)); 
	return kTRUE;
    }
    (*i_result)--;
    if (*i_result <= 0) return kFALSE;
    return kTRUE;
}
PChannelModel *PDynamicData::GetDecayModel(Int_t didx) {
    
    if (! makeDataBase()->GetParamTObj (didx_param, didx, model_param, &t_result)) {
	return NULL;
    }
    return (PChannelModel *) t_result;
}
PChannelModel *PDynamicData::GetDecayModelByKey(Int_t key) {
    
    if (! makeDataBase()->GetParamTObj (key, model_param, &t_result)) {
	return NULL;
    }
    return (PChannelModel *) t_result;
}
PChannelModel *PDynamicData::GetDecayModelByKey(Int_t key, Int_t defkey) {
    
    
    Int_t listkey = makeStaticData()->GetSecondaryKey(key, defkey);
    if (listkey<0) return NULL;
    if (makeDataBase()->GetParamTObj (listkey, model_param, &t_result)) {
	if ((((PChannelModel *) t_result) -> GetDef())  == defkey)
	    return (PChannelModel *) t_result;
	else {
	    Warning("GetDecayModelByKey", "Consistency check failed");
	    return NULL;
	}
    }
    return NULL;
}
PChannelModel *PDynamicData::GetParticleModel(Int_t pid) {
    
    if (! makeDataBase()->GetParamTObj (pid_param, pid, model_param, &t_result)) {
	return NULL;
    }
    return (PChannelModel *) t_result;
}
PChannelModel *PDynamicData::GetParticleSecondaryModel(const char *name, 
						       const char *modelname) {
    Int_t sec_key = makeStaticData()->MakeDirectoryEntry("modeldef", NMODEL_NAME, LMODEL_NAME, modelname);
    if (sec_key < 0) 
	return NULL;
    Int_t primary_key = makeStaticData()->GetParticleKey(name);
    Int_t listkey = makeStaticData()->GetSecondaryKey(primary_key, sec_key);
    if (listkey < 0) 
	return NULL;
    Int_t model_param = makeDataBase()->GetParamTObj("model");
    TObject *t_result = NULL;
    if (makeDataBase()->GetParamTObj (listkey, model_param, &t_result)) {
	if ((((PChannelModel *) t_result) -> GetDef())  == sec_key)
	    return (PChannelModel *) t_result;
	else {
	    Warning("GetSecondaryModel", "Consistency check failed");
	    return NULL;
	}
    }
    return NULL;
}
Double_t PDynamicData::GetParticleTotalWidth(Double_t mass, Int_t pid) {
    
    
    PChannelModel *model = GetParticleModel(pid);
    Double_t width;
    if (model) {
	if (model->GetWidth(mass, &width))
	    return width;
    }
    return makeStaticData()->GetParticleTotalWidth(pid);
}
Double_t PDynamicData::GetParticleTotalWeight(Double_t mass, Int_t pid, Int_t didx) {
    
    
    
    PChannelModel *model = GetParticleModel(pid);
    if (model) {
	Double_t w = model->GetWeight(&mass, &didx);	
	if (w > 0) 
	    return w;
	return 0;
    }
    
    return 0; 
}
bool PDynamicData::SetDecayModel(Int_t didx, PChannelModel *model) {
    Int_t key = makeDataBase()->GetEntryInt("didx", didx);
    if (! makeDataBase()->SetParamTObj (key, "model", (TObject *)model)) {
	return kFALSE;
    }   
    return kTRUE;
}
bool PDynamicData::SetDecayModelByKey(Int_t didx, PChannelModel *model) {
    if (! makeDataBase()->SetParamTObj (didx, "model", (TObject *)model)) {
	return kFALSE;
    }   
    return kTRUE;
}
double PDynamicData:: GetParticleLife(const int &id, double m, int idx) {
    
    
    
    
    
    
    const long double hbar = 6.582122e-25;
    double w0 = 
	makeStaticData()->GetParticleTotalWidth(
	    makeStaticData()->IsParticleValid(id)); 
    if (id == 50 || 
	makeStaticData()->IsParticle(id,"dilepton")) {  
	
	if (w0 > 0.0) return hbar/w0;
	else if (m > 0.0) return hbar/m;
	else return 0.0;
    }
    double tau0 = (w0>0.) ? hbar/w0 : 1.e16; 
    
  
    if (m>0. && w0>0.) {  
	if (idx == -1) {  
	    double w = GetParticleTotalWidth(m,id);     
	    if (w > 0.) 
		return hbar/w;  
            
	} else {     
	    double w1 = GetDecayPartialWidth(m,idx);     
	    
	    if (w1 > 0.) 
		return hbar/w1;      
	    
	}
    }
    return tau0;   
}
int PDynamicData::GetParticleDepth(const int &id, int flag) {
    
    
    
    
    
  
    if (makeStaticData()->IsParticleValid(id) == 0) {
	Warning("GetParticleDepth", "Particle id %i out of range", id);
	return 0;
    } else if (makeStaticData()->GetTDepth(id)) {
	return (flag) ? 
	    ((makeStaticData()->GetHDepth(id)!=-1) ? 
	     makeStaticData()->GetHDepth(id) : 0) :
	    ((makeStaticData()->GetTDepth(id)!=-1) ? 
	     makeStaticData()->GetTDepth(id) : 0);
    }
    
    
    
    
    
    int n = makeStaticData()->GetParticleNChannels(id);
    
    if (!n) {                             
	makeStaticData()->SetTWidx(id,  -1);      
	makeStaticData()->SetTDepth(id, -1);      
	makeStaticData()->SetHDepth(id, -1);      
    } else { 
	int iter = 0, 
	    iter2 = 0;
	
	
	Int_t key = makeDataBase()->GetEntryInt("pid", id);
	Int_t listkey = -1;
	
	Int_t tid[11];
	while (makeDataBase()->MakeListIterator
	       (key, "pnmodes", "link", &listkey)) {
	    tid[0] = 10; 
	    makeStaticData()->GetDecayModeByKey(listkey, tid); 
	    int pos = makeStaticData()->GetDecayIdxByKey(listkey);
	    
	    
	    PChannelModel *model = GetDecayModel(pos);
	    if (!model) {
		Warning("GetParticleDepth", "pwidx -1");
		makeStaticData()->SetPWidx(pos, -1); 
	    } else {
		iter  = TMath::Max(iter,  model->GetDepth()+1);
		iter2 = TMath::Max(iter2, model->GetDepth(1)+1);
	    }
	}
	if (!iter && !iter2)
	    makeStaticData()->SetTWidx(id, -1);
	makeStaticData()->SetTDepth(id, (iter)?iter:-1);          
	makeStaticData()->SetHDepth(id, (iter2)?iter2:-1);        
    }
    return (flag) ? 
	((makeStaticData()->GetHDepth(id)!=-1) ? 
	 makeStaticData()->GetHDepth(id) : 0) :
	((makeStaticData()->GetTDepth(id)!=-1) ? 
	 makeStaticData()->GetTDepth(id) : 0);
}
void  PDynamicData::PrintParticle(int pid) {
    return PrintParticleByKey(makeDataBase()->GetEntryInt("pid", pid));
};
void  PDynamicData::PrintParticleByKey(int key) {
    makeDataBase()->ListEntries(key, 0, "name,pid");
    if (!makeStaticData()->GetParticleNChannelsByKey(key)) {
	cout << "This particle is stable" << endl;
	return;
    }
    PChannelModel *m = GetParticleModel(makeStaticData()->GetParticleIDByKey(key));
    if (m) m->Print();
    else cout << "<No particle model defined>" << endl;
    cout << "This particle decays via the following modes:" << endl;
    
    Int_t listkey = -1;
    while (makeDataBase()->MakeListIterator(key, "pnmodes", "link", &listkey)) {
	PrintDecayByKey(listkey);
	m=GetDecayModelByKey(listkey);
	if (m) m->Print();
	    
    }
};
void PDynamicData::PrintDecayByKey(int key) {
    makeDataBase()->ListEntries(key, 0, "name,didx");
};
PParticle *PDynamicData::GetBatchParticle(const char *name, Int_t make_val) {
    Int_t key_a = makeStaticData()->MakeDirectoryEntry("batch_objects", NBATCH_NAME, LBATCH_NAME, name);
    TObject *ret;
    Int_t batch_particle_param = makeDataBase()->GetParamTObj("batch_particle");
    if (batch_particle_param < 0) 
	batch_particle_param = makeDataBase()->MakeParamTObj("batch_particle", "PParticle storage for batch");
    if (!makeDataBase()->GetParamTObj(key_a ,batch_particle_param,&ret)) {
	if (make_val) {
	    PParticle *delme =  new PParticle("dummy", 0);
	    makeDataBase()->SetParamTObj(key_a, "batch_particle", delme);
	    return delme;
	} else return NULL;
    }
    return (PParticle *)ret;
};
TH1 *PDynamicData::GetBatchHistogram(const char *name) {
    Int_t key_a = makeStaticData()->MakeDirectoryEntry("batch_objects", NBATCH_NAME, LBATCH_NAME, name);
    TObject *ret;
    Int_t batch_histogram_param = makeDataBase()->GetParamTObj("batch_histogram");
    if (batch_histogram_param < 0) 
	batch_histogram_param = makeDataBase()->MakeParamTObj("batch_histogram", "Histogram storage for batch");
    if (!makeDataBase()->GetParamTObj(key_a, batch_histogram_param, &ret)) {
	return NULL;
    }
    return (TH1 *)ret;
};
Bool_t PDynamicData::SetBatchHistogram(const char *name, TH1 *histo) {
    Int_t key_a = makeStaticData()->MakeDirectoryEntry("batch_objects", NBATCH_NAME, LBATCH_NAME, name);
    
    Int_t batch_histogram_param = makeDataBase()->GetParamTObj("batch_histogram");
    if (batch_histogram_param < 0) 
	batch_histogram_param = makeDataBase()->MakeParamTObj("batch_histogram", "Histogram storage for batch");
   
    return makeDataBase()->SetParamTObj(key_a, "batch_histogram", histo);
};
ClassImp(PDynamicData)