#include "hloop.h"
#include "hades.h"
#include "htree.h"
#include "hrecevent.h"
#include "hpartialevent.h"
#include "heventheader.h"
#include "hgeantheader.h"
#include "hcategory.h"
#include "htool.h"
#include "htime.h"
#include "hrootsource.h"
#include "hruntimedb.h"
#include "htaskset.h"
#include "TDirectory.h"
#include "TROOT.h"
#include "TGlobal.h"
#include "TList.h"
#include "TKey.h"
#include "TObject.h"
#include "TString.h"
#include "TObjString.h"
#include "TObjArray.h"
#include "TCollection.h"
#include "TOrdCollection.h"
#include "TChainElement.h"
#include "TSystem.h"
#include <iostream>
#include <fstream>
#include <iomanip>
ClassImp(HLoop)
HLoop *gLoop=0;
HLoop::HLoop(Bool_t createHades)
{
    
    
    
    
    
    gLoop = this;
    fChain        = new TChain("T");
    fHead         = NULL;
    fTree         = NULL;
    fFileCurrent  = NULL;
    fGeantMedia   = NULL;
    fMaxEntries   = 0;
    fCurrentEntry = 0;
    fCurrentName  ="unset";
    fIsNewFile    = kTRUE;
    fIsSkipped    = kFALSE;
    fUseTaskSet   = kFALSE;
    fHasCreatedHades = kFALSE;
    fRefID        = -1;
    fFirstEvent   = kTRUE;
    fTreeCacheSize        = 8000000; 
    fTreeCacheDefaultSize = 8000000; 
    fIsCacheSet           = kFALSE;
    fRecEvent     = new HRecEvent();
    for(Int_t s=0;s<6;s++) fsectors[s]=1;
    if(createHades){
	if(gHades == 0) {
            fHasCreatedHades = kTRUE;
	    Hades* hades = new Hades();
            hades->setEvent(fRecEvent);
            Info("HLoop()","HADES created and event structure set !" );
	} else {
	    Error("HLoop()","HADES already exists, but should be created new! Check your macro ..." );
            exit(1);
	}
    } else {
	if(gHades != 0) {
	    gHades->setEvent(fRecEvent);
            Info("HLoop()","HADES already exists, event structure replaced !" );
	} else {
            Info("HLoop()","HADES does not exists! No functions via gHades will be available!" );
	}
    }
}
HLoop::~HLoop()
{
    delete fChain;
    if(fHasCreatedHades && gHades) delete gHades;
    if(fGeantMedia) delete fGeantMedia;
}
void HLoop::setTreeCacheSize(Long64_t cs )
{
    
    
    
    
    fTreeCacheSize = cs;
    fIsCacheSet    = kTRUE;
}
Bool_t HLoop::addFile (TString infile)
{
    
    
    TObjArray* elements = fChain->GetListOfFiles();
    Int_t nfiles        = elements->GetEntries();
    if(gSystem->AccessPathName(infile.Data()) == 0) {
	cout<<setw(4)<<nfiles<<" add file : "<<infile.Data()<<endl;
	fChain->AddFile(infile.Data());
        return kTRUE;
    } else {
        Error("addFile()","File = %s not found! Will be skipped .... ",infile.Data());
        return kFALSE;
    }
}
Bool_t HLoop::addFiles(TString expression){
    
    TObjArray* arr = HTool::glob(expression);
    Int_t n = arr->GetEntries();
    if(n == 0) {
	Warning("addFiles()","No file matching expression '%s' found !",expression.Data());
    } else {
	for(Int_t i = 0; i < n; i ++){
	    TString name = ((TObjString*)arr->At(i))->GetString();
	    addFile(name);
	}
    }
    delete arr;
    return kTRUE;
}
Bool_t HLoop::addFilesList(TString filelist)
{
    
    
    
    
    
    if(gSystem->AccessPathName(filelist.Data()) == 0 )
    {
	ifstream in;
	in.open(filelist.Data());
	TString name;
        Char_t line[10000];
	while(!in.eof()){
	    in.getline(line,10000);
	    name=line;
	    name.ReplaceAll(" ","");   
	    if(!in.fail()              &&
	       name.CompareTo("")  !=0 &&  
	       name.BeginsWith("#")==0     
	      )
	    {
		addFile(name);
	    }
	}
        in.close();
       return kTRUE;
    } else {
       Error("addFilesList()","File = %s not found! Will be skipped .... ",filelist.Data());
       return kFALSE;
    }
}
Bool_t HLoop::addMultFiles(TString commaSeparatedList)
{
    
    TObjArray* arr = commaSeparatedList.Tokenize(",");
    Int_t n = arr->GetEntries();
    if(n == 0) {
	Warning("addMultFiles()","No file in input string '%s' found !",commaSeparatedList.Data());
    } else {
	for(Int_t i = 0; i < n; i ++){
	    TString name = ((TObjString*)arr->At(i))->GetString();
	    if(!addFile(name)){
		delete arr;
		return kFALSE;
	    }
	}
    }
    delete arr;
    return kTRUE;
}
Bool_t HLoop::setInput(TString readCategories)
{
    
    
    
    
    
    
    
    
    
    
    
    TObjArray* fList  = fChain->GetListOfFiles();
    if(fList->GetEntries() == 0){
        Error("setInput()","No File in List!");
	return kFALSE;
    }
    TChainElement* el = (TChainElement*)fList->At(0);
    TString file = el->GetTitle();
    TFile* fIn = (TFile*)gROOT->GetListOfFiles()->FindObject(file.Data());
    if (!fIn) {
	fIn = new TFile(file.Data());
	if (!fIn) {
	    Error("setFile()","Could not open infile %s",file.Data());
	    return kFALSE;
	}
    }
    fIn->cd();
    fChain->LoadTree(0);
    fTree = (HTree*) fChain->GetTree();
    if(!fTree) {
	Error("setInput()","Could not find Tree in infile %s",file.Data());
	return kFALSE;
    }
    fGeantMedia = (HGeantMedia*) fIn->Get("GeantMedia");
    fHead = fRecEvent->getHeader();
    TBranch* brhead = fTree->GetBranch("EventHeader.");
    if(!brhead) {
	Error("setFile()","Eventheader Branch not found !");
	return kFALSE;
    } else {
	fChain->SetBranchAddress("EventHeader.",&fHead);
	fChain->SetBranchStatus("EventHeader.",1);
	fChain->SetBranchStatus("EventHeader.*",1);
    }
    fChain->SetBranchStatus("Event.",0);
    fChain->SetBranchStatus("Event.*",0);
    TList* dirkeys = gDirectory->GetListOfKeys();
    for(Int_t d = 0; d < dirkeys->GetEntries(); d ++){
	TKey* kdir = (TKey*)dirkeys->At(d);
	TString dirname = kdir->GetName();
	if(dirname.BeginsWith("dir"))
	{
	    TString partialEvent = dirname;
	    partialEvent.ReplaceAll("dir","");
	    if(fIn->cd(dirname.Data()) )
	    {
		TList* catkeys = gDirectory->GetListOfKeys();
		for(Int_t c = 0; c <catkeys->GetEntries(); c ++)
		{
		    fIn->cd(dirname.Data());
		    TKey*   kcat      = (TKey*) catkeys->At(c);
		    TString catname   = kcat->GetName();
		    TString classname = kcat->GetClassName();
		    if(classname == "HPartialEvent") {
			fPartial[catname] = (HPartialEvent*) gDirectory->Get(catname.Data());
                        if(!fPartial[catname]) {
			    Error("setInput()","NULL pointer for partial event %s retrieved!",catname.Data()) ;
			    return kFALSE;
			}
			TBranch* br = fTree->GetBranch(Form("%s.",catname.Data()));
			if(!br) {
			    Error("setInput()","Branch not found : %s !",Form("%s.",catname.Data()));
			    return kFALSE;
			} else {
			    fChain->SetBranchAddress(Form("%s.",catname.Data()),&fPartial[catname]);
			    fChain->SetBranchStatus (Form("%s.",catname.Data()),1);
			    fChain->SetBranchStatus (Form("%s.*",catname.Data()),1);
			}
			fRecEvent->addPartialEvent(fPartial[catname]);
			continue;
		    }
		    
		    if(classname == "HLinearCategory" || classname == "HMatrixCategory")
		    {
			fEvent[catname] = (HCategory*) gDirectory->Get(catname.Data());
			if(!fEvent[catname]) {
			    Error("setInput()","NULL pointer for category %s retrieved!",catname.Data()) ;
			    return kFALSE;
			}
			fStatus  [catname] = 0;
                        fPartialN[catname] = partialEvent;
			TBranch* br = fTree->GetBranch(Form("%s.",catname.Data()));
			if(!br) {
			    Error("setInput()","Branch not found : %s !",Form("%s.",catname.Data()));
			    return kFALSE;
			} else {
			    fChain->SetBranchAddress(Form("%s.",catname.Data()),&fEvent[catname]);
			    fChain->SetBranchStatus (Form("%s.",catname.Data()),1);
			    fChain->SetBranchStatus (Form("%s.*",catname.Data()),1);
			    fStatus[catname] = 1;
			}
		    }
		    
		} 
	    } else {
		Error("setInput()","Could no enter dir %s !",dirname.Data());
		return kFALSE;
	    }
	}
    }
    fIn->cd();
    fMaxEntries = fTree->GetEntries();
    TString readCat = readCategories;
    readCat.ReplaceAll(" ","");
    TObjArray* catNames = readCat.Tokenize(",");
    Int_t n = catNames->GetEntries();
    
    for(Int_t i = 0; i < n; i ++){
	TString name = ((TObjString*)catNames->At(i))->GetString();
	
	if(i == 0){
	    if(name.CompareTo("-*") == 0){
		setStatus("-*", 0);
		continue;
	    }
	} else {
	    if(name.Contains("*") != 0){
		Warning("setInput()","Wild cards are only supported for '-*' in the first position of the string! Will ignore this one!");
		continue;
	    }
	}
	
	
	if(name.BeginsWith("+")) { 
	    name.ReplaceAll("+","");
	    if(getCategory(name)) { 
		setStatus(name, 1);
	    }
	}
	
	
	if(name.BeginsWith("-")) { 
	    name.ReplaceAll("-","");
	    if(getCategory(name)) { 
		setStatus(name, 0);
	    }
	}
	
    }
    
    if(n == 0) {
	
	
	map<TString,HCategory*>::iterator iter;
	for( iter = fEvent.begin(); iter != fEvent.end(); ++iter ) {
	    addCatName(iter->first,getCategory(iter->first)->getCategory());
	}
    } else {
	map<TString,Int_t>::iterator iter;
	for( iter = fStatus.begin(); iter != fStatus.end(); ++iter ) {
	    if(iter->second == 1 ) {
		addCatName(iter->first,getCategory(iter->first)->getCategory());
	    }
	}
    }
    
    fIn->Close();
    printChain();
    Long64_t cs = fChain->GetCacheSize();
    if(!fIsCacheSet ){
	cs = fTreeCacheDefaultSize;
    } else {
        cs = fTreeCacheSize;
    }
    if(fTreeCacheSize > 0) Info("setInput()","Setting Tree Cache size = %lld byte.",cs);
    else                   Info("setInput()","Tree cache is disabled");
    if(cs > 0){
	fChain->SetCacheSize(cs);
	
	
    }
    return kTRUE;
}
void HLoop::printCategories()
{
    
    cout<<"--------------------------------------"<<endl;
    cout<<"current event :"<<endl;
    map<TString,HCategory*>::iterator iter;
    for( iter = fEvent.begin(); iter != fEvent.end(); ++iter ) {
	cout << setw(20)<<iter->first.Data() << " = " << iter->second <<" status = "<<fStatus[iter->first]<< endl;
    }
    cout<<"--------------------------------------"<<endl;
}
void HLoop::printChain()
{
    
    TObjArray* elements = fChain->GetListOfFiles();
    cout<<"--------------------------------------"<<endl;
    cout<<" Analize Chain : .... Can take while  for N file = "<<elements->GetEntries()<<endl;
    fMaxEntries = fChain->GetEntries();
    cout<<"N total entries = "<<fMaxEntries<<endl;
    for(Int_t i = 0; i < elements->GetEntries(); ++i ) {
        TChainElement* element = (TChainElement*)elements->At(i);
	cout <<setw(4)<<i<< setw(80)<<element->GetTitle() << ", N entries = " << element->GetEntries()<< endl;
    }
    cout<<"--------------------------------------"<<endl;
}
void HLoop::printBranchStatus()
{
    
    cout<<"--------------------------------------"<<endl;
    cout<<"Branch status :"<<endl;
    TObjArray* branches = fChain->GetListOfBranches();
    for(Int_t i = 0; i < branches ->GetEntries(); ++i ) {
        TBranch* br = (TBranch*) branches->At(i);
	cout <<setw(4)<<i<< setw(30)<<br->GetName() << ", status = " <<(Int_t) fChain->GetBranchStatus(br->GetName())<< endl;
    }
    cout<<"--------------------------------------"<<endl;
}
void HLoop::clearCategories()
{
    
    map<TString,HCategory*>::iterator iter;
    for( iter = fEvent.begin(); iter != fEvent.end(); ++iter ) {
	if(fStatus[iter->first]) iter->second->Clear();
    }
}
HCategory* HLoop::getCategory(TString catname,Bool_t silent)
{
    
    TString simname  = catname;
    TString realname = catname;
    if(realname.EndsWith("Sim")==1) realname.Replace(realname.Length(),3,"");
    if(simname .EndsWith("Sim")==0) simname+="Sim";
    map<TString,HCategory*>::iterator iter = fEvent.find(realname);
    if( iter == fEvent.end() ){
	iter = fEvent.find(simname);
	if(iter == fEvent.end()){
	    if(!silent)Error("getCategory()","Category \"%s\" not found!",catname.Data());
	    return 0;
	} else  return iter->second;
    } else return iter->second;
}
Bool_t HLoop::getCategoryStatus(TString catname,Bool_t silent)
{
    
    map<TString,Int_t>::iterator iter = fStatus.find(catname);
    if( iter == fStatus.end() ){
	if(!silent)Error("getCategory()","Category \"%s\" not found!",catname.Data());
	return kFALSE;
    } else {
	return (Bool_t)fStatus[catname];
    }
}
HPartialEvent* HLoop::getPartialEvent(TString catname,Bool_t silent)
{
    
    map<TString,HPartialEvent*>::iterator iter = fPartial.find(catname);
    if( iter == fPartial.end() ){
	if(!silent)Error("getPartialEvent()","PartialEvent \"%s\" not found!",catname.Data());
	return 0;
    }
    else return iter->second;
}
HGeantHeader* HLoop::getGeantHeader(Bool_t silent)
{
    
    
    HPartialEvent* pEvt = getPartialEvent("Simul",silent);
    HGeantHeader* header = NULL;
    if(pEvt) header = (HGeantHeader*) pEvt->getSubHeader();
    return header;
}
Bool_t HLoop::setStatus(TString catname, Int_t stat )
{
    
    
    
    if(catname.CompareTo("-*") == 0 || catname.CompareTo("+*") == 0 ){
	map<TString,HCategory*>::iterator iter;
	for( iter = fEvent.begin(); iter != fEvent.end(); ++iter ) {
	    if(catname.CompareTo("-*") == 0)  {
		fChain->SetBranchStatus(Form("%s.",iter->first.Data()),0);
		fChain->SetBranchStatus(Form("%s.*",iter->first.Data()),0);
		fStatus[iter->first] = 0;
	    }
	    if(catname.CompareTo("+*") == 0)  {
		fChain->SetBranchStatus(Form("%s.",iter->first.Data()),1);
		fChain->SetBranchStatus(Form("%s.*",iter->first.Data()),1);
		fStatus[iter->first] = 1;
	    }
	}
	return kTRUE;
    }
    if(!getCategory(catname)) {
	return kFALSE;
    }
    TString simname  = catname;
    TString realname = catname;
    if(realname.EndsWith("Sim")==1) realname.Replace(realname.Length(),3,"");
    if(simname .EndsWith("Sim")==0) simname+="Sim";
    map<TString,Int_t>::iterator iter = fStatus.find(realname);
    if( iter == fStatus.end() ){
	iter = fStatus.find(simname);
	if( iter == fStatus.end() ) {
	    Error("getCategory()","Category \"%s\" not found!",catname.Data());
	    return kFALSE;
	} else {
	    fChain->SetBranchStatus(Form("%s.",simname.Data()),stat);
	    fChain->SetBranchStatus(Form("%s.*",simname.Data()),stat);
	    iter->second = stat;
	    return kTRUE;
	}
    }
    else {
	fChain->SetBranchStatus(Form("%s.",realname.Data()),stat);
	fChain->SetBranchStatus(Form("%s.*",realname.Data()),stat);
	iter->second = stat;
	return kTRUE;
    }
    return kFALSE;
}
Int_t HLoop::nextEvent(Int_t iev)
{
    
    
    
    
    
    if(!fHead) {
	Warning("nextEvent()","File not initialized ! Call setInput(....) before!");
	exit(1);
    }
    fCurrentEntry = iev;
    clearCategories();
    
    
    Int_t nbytes = fChain->GetEntry(fCurrentEntry);  
    if(nbytes == 0) {
	Warning("nextEvent()","Entry %i not found!",(Int_t)fCurrentEntry);
    } else if(nbytes == -1) {
	Warning("nextEvent()","Entry %i read with Io error!",(Int_t)fCurrentEntry);
    }
    
    
    
    TString name = fChain->GetFile()->GetName();
    if(name != fCurrentName) {
	fIsNewFile = kTRUE;
        Int_t entries = 0;
	TObjArray* elements = fChain->GetListOfFiles();
	for(Int_t i = 0; i < elements->GetEntries(); ++i ) {
	    TChainElement* element = (TChainElement*)elements->At(i);
	    if(name.CompareTo(element->GetTitle() ) == 0) {
		entries = element->GetEntries();
                break;
	    }
	}
	fCurrentName = fChain->GetFile()->GetName() ;
	fFileCurrent = fChain->GetFile() ;
	fTree        = fChain->GetTree() ;
	
	
	
	
	
	
        
	for (Int_t s=0;s<6;s++){ fsectors[s]=1;} 
	if(fSectorSelector.getNFiles() > 0){ 
	    TString hldname = HTime::stripFileName(fCurrentName,kTRUE,kTRUE); 
	    if(hldname != "" ) { 
		fSectorSelector.getSectors(fsectors,hldname); 
	    }
	}
	
	cout<<"---------------------------------------------------------"<<endl;
	cout<<name.Data() <<",  sectors : "<<fsectors[0]<<" "<<fsectors[1]<<" "<<fsectors[2]<<" "<<fsectors[3]<<" "<<fsectors[4]<<" "<<fsectors[5]<<" "<<endl;
	cout<<"at entry "<<setw(8)<<fCurrentEntry<<" with entries = "<<entries<<endl;
        cout<<"---------------------------------------------------------"<<endl;
    } else {
       fIsNewFile = kFALSE;
    }
    
    if(fFirstEvent){
	if(gHades){
	    
	    const TObjArray* l = gHades->getTaskList();
	    HTaskSet* task;
	    TIter iter(l);
	    Int_t ct = 0 ;
	    while((task = (HTaskSet*)iter())) {
		TOrdCollection* list = task->getSetOfTask();
		ct += list->GetEntries();
	    }
	    if(ct > 0) fUseTaskSet = kTRUE;
	    if(fUseTaskSet && gHades->getSetup() == 0 ) {
		Error("","TaskSet used, but Spectrometer not set!"); exit(1);
		fUseTaskSet = kFALSE;
	    }
	    
	    if(fUseTaskSet){
		HRootSource* source = new HRootSource();
		source->addFile((Text_t*)fCurrentName.Data());
		source->setCurrentRunId(fHead->getEventRunNumber());
		source->setGlobalRefId(fRefID);
		gHades->setDataSource(source);
		if(!gHades->init(kTRUE)){ Error("","HADES init failed!"); exit(1);}
		HRuntimeDb* rtdb = gHades->getRuntimeDb();
		if (rtdb){
		    if(!rtdb->initContainers(fHead->getEventRunNumber(),
					     fRefID,
					     fCurrentName.Data())
		      ){
			Error("","Rtdb init failed!"); exit(1);
		    }
		}
		gHades->reinitTasks();
		gHades->printDefinedTaskSets ();
                rtdb->disconnectInputs();
	    }
	}
	fFirstEvent = kFALSE;
    }
    
    
    if(fUseTaskSet && !fFirstEvent && gHades && fIsNewFile){
	HRootSource* source = (HRootSource*)gHades->getDataSource();
	if(source){
	    HRuntimeDb* rtdb = gHades->getRuntimeDb();
	    rtdb->reconnectInputs();
	    source->setCurrentRunId(fHead->getEventRunNumber());
	    source->setGlobalRefId(fRefID);
	    if(!rtdb->initContainers(fHead->getEventRunNumber(),
				     fRefID,
				     fCurrentName.Data())
	      ){
		Error("","Rtdb init failed!"); exit(1);
	    }
	    gHades->reinitTasks();
	    rtdb->disconnectInputs();
	}
    }
    
    
    
    if(fUseTaskSet && gHades){
	Int_t retVal = gHades->executeTasks();
	if(retVal == kSkipEvent){
	    fIsSkipped = kTRUE;
	    fRecEvent->Clear();
	} else {
            fIsSkipped = kFALSE;
	}
    }
    
    return nbytes;
}
Bool_t HLoop::addCatName(TString catname,Short_t catNum)
{
    
    
    if(fHead == 0) {
        Error("addCatName()","You have to call setInput() before calling this function !");
	exit(1);
    }
    map<TString,Short_t>::iterator iter = fNameToCat.find(catname);
    if( iter == fNameToCat.end() ){
	if(getCategory(catname,kTRUE)){
            Bool_t ret =  fRecEvent->addCategory((Cat_t)catNum,getCategory(catname),fPartialN[catname].Data());
            fNameToCat[catname] = catNum;
	    return ret;
       } else {
	   Error("addCatName()","Category \"%s\" does not exists in input! Will skip ...",catname.Data());
           return kFALSE;
       }
    } else {
	Error("addCatName()","Category \"%s\" already exists with number = %i!",catname.Data(),fNameToCat[catname]);
        return kFALSE;
    }
    return kTRUE;
}
Bool_t HLoop::isNewFile(TString& name)
{
    
    
    
    name = fCurrentName;
    return fIsNewFile;
}
TObject* HLoop::getFromInputFile(TString name)
{
    
    
    
    if(name=="") return 0;
    if(fFileCurrent){
	TDirectory* dold =  gDirectory;
        fFileCurrent->cd();
        TObject* obj = fFileCurrent->Get(name.Data());
	dold->cd();
        return obj;
    } else return 0;
}