// C/C++ headers
//
#include <fstream>
#include <string.h>
#include <iostream>
#include <stdio.h>
#include <ctype.h>
//
// ROOT headers
//

#include <TTree.h>

#include <TSystem.h>
#include <TStopwatch.h>
#include <TSystemDirectory.h>
#include <TGraphAsymmErrors.h>
#include <TString.h>
#include <TFile.h>
#include <TClass.h>
#include <TGraph.h>
#include <TLine.h>
#include <TH1F.h>
#include <TMath.h>
#include <TRandom3.h>
#include <TF1.h>
//
// RunInfo header
//
#include <RunInfo.h>
#include <PamLevel2.h>
#include <TClonesArray.h>

//PamVMC headers
#include "PamVMCDigMgr.h"
#include "PamVMCTrkHit.h"
#include "PamVMCTrkF77GpsSpe.h"
#include "PamVMCPrimaryGenerator.h"
using namespace std;
//This program is standalone Digitizer for PamVMC


void Usage ();

TClonesArray* FindColl(TTree*t, TString brname, TString type = "PamVMCDetectorHit");
void RegisterPDG(); 
//Here user should define all nuclei and particles that are not in PDG tabe of ROOT
//otherwise application will crash on TOF digitization whrer charge information needed;

void UnCompressCalo(TClonesArray* cal_comp, TClonesArray & cal_u);

int main(int argc, char * argv[] ){

  if(argc!=4){
    Usage();
    return 0;
  }
  
  TString DataPATH  = gSystem->Getenv("PWD");
  TString inputfilename(argv[1]);
  if(!inputfilename.Contains(".root")){
    cerr<<"Error! Not root file on input!"<<endl;
    return 0;
  }
  
  TString outputfilename(argv[2]);

  Int_t seed = atoi(argv[3]);
  TRandom3* rnd = new TRandom3(seed);
  gRandom = rnd;

  TStopwatch timer;
  timer.Start();
  TFile* rfin =  new TFile(inputfilename, "UPDATE");
  if(!rfin){
    cerr<<"Error! Can't read root file!"<<endl;
    return 0;
  }

  TTree::SetMaxTreeSize(1000*Long64_t(2000000000));
     
  TTree * hits_tree = (TTree*)rfin->Get("hits");
  if(!hits_tree){
    cerr<<"Tree hits are not found in file "<<rfin->GetName()<<endl;
    TString treename = rfin->GetName();
    treename.Replace(treename.Index(".root",5),5,"");
    hits_tree = (TTree*)rfin->Get(treename);
    if(!hits_tree) return 0;
  }

 

  PamVMCDigMgr* d_mgr = PamVMCDigMgr::Instance();
  d_mgr->Reset();
  //AC CARD: C1D1, C2D1, CAT: TOP1, CAS: SID1
  PamVMCAcDig* ac = new PamVMCAcDig();
  ac->RegisterCollections("C1D1", FindColl(hits_tree,"C1D1"));
  ac->RegisterCollections("C2D1", FindColl(hits_tree,"C2D1"));
  ac->RegisterCollections("TOP1", FindColl(hits_tree,"TOP1"));
  ac->RegisterCollections("SID1", FindColl(hits_tree,"SID1"));
  d_mgr->SetDIG("AC",ac);
  //TOF: S11Y, S12X, S21X, S22Y, S31Y, S32X
  PamVMCTofDig* tof = new PamVMCTofDig();
  tof->RegisterCollections("S11Y", FindColl(hits_tree,"S11Y"));
  tof->RegisterCollections("S12X", FindColl(hits_tree,"S12X"));
  tof->RegisterCollections("S21X", FindColl(hits_tree,"S21X"));
  tof->RegisterCollections("S22Y", FindColl(hits_tree,"S22Y"));
  tof->RegisterCollections("S31Y", FindColl(hits_tree,"S31Y"));
  tof->RegisterCollections("S32X", FindColl(hits_tree,"S32X"));
  d_mgr->SetDIG("Tof",tof);


  //Calorimeter CAST
#define NSTRIPS 4224
  PamVMCCaloDig* cal = new PamVMCCaloDig();
  TClonesArray *cal_u = new TClonesArray("PamVMCDetectorHit",NSTRIPS); //uncompressed array
  TClonesArray* cal_comp = FindColl(hits_tree,"CAST"); // compressed array from file
  cal->RegisterCollections("CAST", cal_u);  
  d_mgr->SetDIG("CAST",cal);

  //Tracker GPSSPE
  PamVMCTrkDig* trk = new PamVMCTrkDig();
  TBranch *gps = hits_tree->GetBranch("GPSSPE");
  trk->RegisterCollections("TSPA", FindColl(hits_tree,"TSPA"));
  pGPSSPEHits * hits = new pGPSSPEHits();
  if(gps){
    hits_tree->SetBranchAddress("GPSSPE",&hits);
    hits_tree->SetBranchStatus("GPSSPE",1);
    cout<<"TRKCOLL="<<hits<<endl;
    trk->RegisterTrkData(hits);
  }  
  d_mgr->SetDIG("TSPA",trk);

  //S4
  PamVMCS4Dig* s4 = new PamVMCS4Dig();
  s4->RegisterCollections("S4", FindColl(hits_tree,"S4"));  
  d_mgr->SetDIG("S4",s4);
  //ND: NDTI
  PamVMCNDDig* nd = new PamVMCNDDig();
  nd->RegisterCollections("NDTI", FindColl(hits_tree,"NDTI"));  
  d_mgr->SetDIG("NDTI",nd);

  //Primaries
  TClonesArray *prims = FindColl(hits_tree,"PRIM","PamVMCPrimary");  

  //Initialization
  PamVMCRawMgr* r_mgr = PamVMCRawMgr::Instance();
  r_mgr->CreateOutFile(outputfilename.Data());
  d_mgr->Initialize(gRandom);
  r_mgr->WriteToFile();

  Int_t nev = hits_tree->GetEntries();
  cout<<"TOTAL NUMBER OF EVENTS:"<<nev<<endl;
  RegisterPDG();
  //Event loop
  d_mgr->PrintCollections();
  for(Int_t i = 0; i<nev; i++){
    hits_tree->GetEntry(i);
    cout<<"--> Digitizing evnt"<<endl;
    UnCompressCalo(cal_comp, *cal_u);
    d_mgr->Digitize(i,((PamVMCPrimary*)prims->At(0))->fPDG);
    cout<<"--> Writing evnt"<<endl;
    r_mgr->WriteEvent();
  }

  d_mgr->FinishRun();
  r_mgr->FinishRun();
  d_mgr->PrintCollections();
  timer.Stop();
  gDirectory->Delete("tmp.root");
  rfin->Close();
  return 0;   
}

TClonesArray* FindColl(TTree*t, TString brname, TString type){
  if(t->GetBranch(brname)){
    TClonesArray* coll = 0;//new TClonesArray(type,4224);
    t->GetBranch(brname)->SetAddress(&coll);
    cout<<"coll="<<coll<<endl;
    return coll; 
  }
  return 0;
}

void RegisterPDG(){
  TDatabasePDG::Instance()->AddParticle("He-4","He-4",3.7274,kTRUE,0., 6. /* |e|/3 charge*/,"Ion",1000020040);
}

void UnCompressCalo(TClonesArray* cal_comp, TClonesArray & cal_u){
  if(!cal_comp) return;
  PamVMCDetectorHit* hit = NULL;
  PamVMCDetectorHit* newhit = NULL;
  cal_u.Clear("C");
  for(Int_t j = 0; j<cal_comp->GetEntries(); j++){
      hit=((PamVMCDetectorHit*)cal_comp->At(j));
      newhit = (PamVMCDetectorHit*)cal_u.New(hit->GetPOS());
      *newhit = *hit;
  }
}

void Usage(){
  cout<<"NAME"<<endl;
  cout<<"       vmc_dig - a standalone digitizer for PamVMC application"<<endl;
  cout<<"SYNOPSIS"<<endl;
  cout<<"       vmc_dig  INPROOTFILE OUTRAWFILE SEED"<<endl; 
  cout<<"DISCRIPTION"<<endl;
  cout<<"       This tool convert hits collections of PamVMC in raw format "<<endl; 
  cout<<"       There are only 3 parameters: input root/output raw filenames and integer random seed."<<endl;
  cout<<"       If seed is 0, then TRandom3(0) object will be created."<<endl;
}
