#ifndef _PREACTION_H_
#define _PREACTION_H_
#include "TClonesArray.h"
#include "TTree.h"
#include "TFile.h"
#include "TMethodCall.h"
#include "PChannel.h"
#include "PDistributionManager.h"
#include "PStdData.h"
#include "PFileOutput.h"
#include "PBulkInterface.h"
#include "PProjector.h"
#include "PPlutoBulkDecay.h"
#define MAX_FILEOUTPUT 5
#define MIN_PARTICLE_STACKSIZE 500
#define INC_PARTICLE_STACKSIZE 100
#define MAX_PARTICLE_STACKSIZE 10000
#define MAX_REACTION_FILTERS   100
class TPythia6;
class PFilter;
Int_t select(PParticle*);
class PReaction : public TObject  {
    friend class PDecayManager;
 public:
    PReaction();
    PReaction(const char *filename);
    PReaction(PChannel **, const char *, int n=2, int f0=1, int f1=0, int f2=0, int f3=0, TTree *ttree=NULL);
    PReaction(PChannel **, int n=2, unsigned int ff=0, TTree *ttree=NULL, const char *filename=NULL);
    PReaction(Double_t momentum, const char *beam, const char *target, const char *reaction, const char *file_name=NULL,
	      Int_t f0=1, Int_t f1=0, Int_t f2=0, Int_t f3=0, TTree *ttree=NULL); 
    
    PReaction(const char *command,  const char *beam, const char *target, 
	      const char *reaction, const char *file_name=NULL,
	      Int_t f0=1, Int_t f1=0, Int_t f2=0, Int_t f3=0, TTree *ttree=NULL); 
    
    PReaction(PParticle *, const char *reaction, const char *file_name=NULL,
	      Int_t f0=1, Int_t f1=0, Int_t f2=0, Int_t f3=0, TTree *ttree=NULL); 
    
    void AddReaction(const char *reaction);
    
    ~PReaction();
    
    void InitChannels();
    int loop(int i=-1, int wf=0, int verbose=1) {
	
	return Loop(i, wf, verbose);
    };
    int Loop(int i=-1, int wf=0, int verbose=1);
    
    int GetNumAll() { return ndpar; }
    
    int *GetCIndex() { 
	return cindex.GetArray(); }
    
    void VertexYes() {
	
	if (IsExtTree()) return;
	getVERTEX = 1;
	ropt = ropt | ff2;
    }
    void VertexNo()  {
	
	if (IsExtTree()) return;
	getVERTEX = 0;
	ropt=~((~ropt)|ff2);
    }
    void allParticles() {
	
	if (IsExtTree()) return;
	allPARTICLES = 1;
	ropt = ropt | ff0;
    }
    void trackedParticles() {
	
	if (IsExtTree()) return;
	allPARTICLES = 0;
	ropt=~((~ropt)|ff0);
    }
    void asciiYes() {
	
	if (IsExtTree()) return;
	asciiOUTPUT = 1;
	ropt = ropt | ff3;
	SetName(filename);
    }
    void asciiNo() {
	
	if (IsExtTree()) return;
	asciiOUTPUT = 0;
	ropt=~((~ropt)|ff3);
	SetName(filename);
    }
    void SetName(const char *);
    
    PParticle **GetProducts() { return particle_stack; }
    
    void SetFilter(int ch_id, PFilter *filter);
    
    
    
    PFilter **GetFilter() { return NULL; }
    
    void Print(const Option_t *delme=NULL) const;
    void PrintReport() const; 
    void Close();
    
    void SetHGeant(int flag) {HGeant = (flag!=0);}  
    
    void setHGeant(int flag) {SetHGeant(flag);}; 
    void SetUserSelection(Int_t (*f)(PParticle*)) { 
	SetUserSelection((void*)f);
    }
    void SetUserSelection(void *f);                 
    void SetUserAnalysis(Int_t (*f)(PParticle**,Int_t)) { 
	SetUserAnalysis((void*)f);
    }
    void SetUserAnalysis(void *f);                  
    void SetTrigCond(Int_t n) {nTrigCond = n;}      
    void setDecayAll(Float_t tau=1.) { 
	Warning ("setDecayAll", "This method is depreciated: use SetDecayAll()");
	SetDecayAll(tau);
    }
    void SetDecayAll(Float_t tau=1.) {              
	
	
	
	
	
	
	
	
	
	tauMax = tau;   
    
#ifdef USE_PYTHIA6
    
	PPythiaBulkDecay *pl = new PPythiaBulkDecay();
	pl->SetPythia(fPythia);
	pl->SetTauMax(tauMax);
	AddBulk(pl);
#else
	PPlutoBulkDecay *pl = new PPlutoBulkDecay();
	pl->SetRecursiveMode(1);
	pl->SetTauMax(tauMax);
	AddBulk(pl);
#endif
    
    }
    void SetPythia(TPythia6 *p) {fPythia=p;}        
    void SetMaxFileSize(Int_t bytes) {nMaxBytes = bytes;}  
#if 0
    Int_t testPointer(void *p2f) {   
	
	if (p2f == NULL) return -1;
	Int_t ret = G__isinterpretedp2f(p2f);
	char *fname;
	fname = G__p2f2funcname(p2f);
	printf("Pointer to function %s is of type %d\n",fname,ret);
	return ret;
    }
#endif
    void SetWriteIndex(Bool_t flag) {
	if (flag == kTRUE) writeINDEX = 1;
	else writeINDEX = 0;
    }
    void DisableWeightReset() {weight_reset=0;};
    Int_t GetReactionId() { return reactionId; }
    PDistributionManager *GetDistributionManager(void){
	return makeDistributionManager();
    }
    Bool_t AddFileOutput(PFileOutput *file) {
	if (fileoutput_pos == MAX_FILEOUTPUT ) {
	    Warning("AddFileOutput", "MAX_FILEOUTPUT reached");
	    return kFALSE;
	}
	files[fileoutput_pos++] = file;
	return kTRUE;
    }
    Bool_t AddBulk(PBulkInterface *mybulk);
    Bool_t AddPrologueBulk(PBulkInterface *mybulk); 
    Bool_t Do(const char *command) {
	return GetCurrentProjector()->AddCommand(command);
    }
    Bool_t Do(TH1 *f, const char *command, Int_t flag=1) {
	return GetCurrentProjector()->AddHistogram(f,command,flag);
    }
    Bool_t Do(TH2 *f, const char *command, Int_t flag=1) {
	return GetCurrentProjector()->AddHistogram(f,command,flag);
    }
    Bool_t Do(TH3 *f, const char *command, Int_t flag=1) {
	return GetCurrentProjector()->AddHistogram(f,command,flag);
    }
    Bool_t Output(TNtuple *f, const char *command = (char *)"") {
	return GetCurrentProjector()->AddOutputTNtuple(f,command);
    }
    Bool_t Input(TNtuple *f) {
	return GetCurrentProjector()->AddInputTNtuple(f);
    }
    Bool_t Output(const char *f, const char *command="") {
	return GetCurrentProjector()->AddOutputASCII(f,command);
    }
    Bool_t Input(const char *f, const char *command="") {
	return GetCurrentProjector()->AddInputASCII(f, command);
    }
    Bool_t CloseFile(void) {
	return GetCurrentProjector()->CloseFile();
    }
    PProjector *GetCurrentProjector(void) {
	if (!bulkdecay_pos) {
	    current_projector = new PProjector();
	    AddBulk(current_projector);
	} else {
	    if ((strcmp("PProjector", bulk[bulkdecay_pos-1]->GetName()) == 0)) {
		if ((bulk[bulkdecay_pos-1]->GetPriority() > FILTER_PRIORITY) ||
		    ((PProjector *)bulk[bulkdecay_pos-1])->IsFileOpen()) {
		    current_projector = (PProjector *)bulk[bulkdecay_pos-1];
		} else {
		    current_projector = new PProjector();
		    AddBulk(current_projector);
		}
	    } else {		
		current_projector = new PProjector();
		AddBulk(current_projector);
	    }
		
	}
	return current_projector;
    }
    void Preheating(Int_t num) {pre_heating=num;}; 
    void IsInline(void) {
	
	is_inline = 1;
    }
 private:
    Int_t reactionId;                          
    Int_t (*userSelection)(PParticle*);        
    Int_t (*userAnalysis)(PParticle**,Int_t);  
    Int_t num_filters;           
    Int_t filter_keys[MAX_REACTION_FILTERS], filter_counter[MAX_REACTION_FILTERS]; 
    Double_t *filter_values[MAX_REACTION_FILTERS]; 
    int nchan, ntpar, ndpar, nclones, loop_count, reset_count, status;
    
    
    
    
    
    
    
    static int activeCnt;  
    TArrayI cindex, dindex, ftrack;
    
    
    
    unsigned int ropt, allPARTICLES, asciiOUTPUT, writeINDEX,
	resetCHANNELS, getVERTEX, extTREE, HGeant, decayALL, inactivate_decayed_particles;
    static const unsigned int ff0, ff1, ff2, ff3, ff4;
    
    Float_t tauMax;   
    PChannel **channel;
    
    TString filename, file1, file2, original_filename,reaction_string;
    
    void ConvertFilename(void);
    
    TTree *tree;
    
    PParticle **particle_stack;
    
    int decay_done[MAX_PARTICLE_STACKSIZE];
    int stacksize;
    PParticle **particle;
    
    static TClonesArray *evt[MAX_NUM_BRANCHES+1];
    
    TFile *rootfile;
    
    int size_branches;
    int key_branches[MAX_NUM_BRANCHES];
    int doonce;
    int fileoutput_pos;
    PFileOutput *files[MAX_FILEOUTPUT];
    int bulkdecay_pos, pro_bulkdecay_pos;
    
    PBulkInterface *bulk[MAX_BULKDECAY];
    PBulkInterface *pro_bulk[MAX_BULKDECAY];
    PProjector *current_projector;
  
    Int_t nTrigCond;
    TPythia6 *fPythia;                   
    void SetReactionId(void);
    
    void SetUp(PChannel **);
    
    Bool_t parse_script(const char *e, const char *beam, const char *target, const char *reaction, const char *file_name=NULL,
			Int_t f0=1, Int_t f1=0, Int_t f2=0, Int_t f3=0, TTree *ttree=NULL); 
    void InitLoop();
    
    Int_t IsExtTree() { return extTREE; }
    
    Int_t ParseChannel(PParticle *parent, const char *channel,
		       TList &plutoList, Int_t &numChannels);
    
    PParticle *MakeParticle(char * name);
    Int_t nMaxBytes;
    static FILE *asciif;
    static Int_t globalEventCounter;
    Int_t weight_reset;
    Int_t is_inline;
    
    
    
    const char *r_beam, *r_target;
    
    Double_t *vertex_x, *vertex_y, *vertex_z;
    Double_t *event_impact_param , *event_plane;
    Double_t *weight_version;
    
    static void passEvent(float,  float,  float,  int, int*, int*, int*, int*,
			  float*, float*, float*, float*,
			  float*, float*, float*, float*) {;}
    
    Int_t pre_heating;
    PReaction *sub_reaction;
    ClassDef(PReaction, 0) 
};
#endif // _PREACTION_H_