void Build_Timing_Tree(TFile * f, const Char_t *pattern, Int_t& max_slaves);

void Draw_PerfProfiles(const Char_t* filename) {
   // Plots total processing time as a function of number of slaves
   // using each of the 3 selectors.

   // filename: name of file that Run_Node_Tests.C wrote its output into

   gROOT->SetStyle("Plain");
   gStyle->SetOptStat(0);
   gStyle->SetNdivisions(505);
   gStyle->SetTitleFontSize(0.07);

   if(!TString(gSystem->GetLibraries()).Contains("Proof"))
      gSystem->Load("libProof.so");
   
   TFile f(filename);
   if (f.IsZombie()) {
      cout << "file " << filename << " cannot be opened" << endl;
      return;
   }

   TString perfstats_name = "PROOF_PerfStats";

   Int_t ns_holder;
   Int_t run_holder;
   Float_t time_holder;

   Int_t procmax_slaves = 0;
   TTree* tt_proc    = Build_Timing_Tree(&f, perfstats_name+"_Proc_",
                                         procmax_slaves);
   tt_proc->SetMarkerStyle(4);
   //set branch addresses
   tt_proc->GetBranch("perfproctime")->GetLeaf("nslaves")->SetAddress(&ns_holder);
   tt_proc->GetBranch("perfproctime")->GetLeaf("run")->SetAddress(&run_holder);
   tt_proc->GetBranch("perfproctime")->GetLeaf("time")->SetAddress(&time_holder);

   Int_t procoptmax_slaves = 0;
   TTree* tt_procopt = Build_Timing_Tree(&f, perfstats_name+"_ProcOpt_",
                                         procoptmax_slaves);
   tt_procopt->SetMarkerStyle(5);
   //set branch addresses
   tt_procopt->GetBranch("perfproctime")->GetLeaf("nslaves")->SetAddress(&ns_holder);
   tt_procopt->GetBranch("perfproctime")->GetLeaf("run")->SetAddress(&run_holder);
   tt_procopt->GetBranch("perfproctime")->GetLeaf("time")->SetAddress(&time_holder);

   Int_t noprocmax_slaves = 0;
   TTree* tt_noproc  = Build_Timing_Tree(&f, perfstats_name+"_NoProc_",
                                         noprocmax_slaves);
   tt_noproc->SetMarkerStyle(6);
   //set branch addresses
   tt_noproc->GetBranch("perfproctime")->GetLeaf("nslaves")->SetAddress(&ns_holder);
   tt_noproc->GetBranch("perfproctime")->GetLeaf("run")->SetAddress(&run_holder);
   tt_noproc->GetBranch("perfproctime")->GetLeaf("time")->SetAddress(&time_holder);

   f.Close();

   Int_t nslaves = procmax_slaves>procoptmax_slaves?procmax_slaves:procoptmax_slaves;
   if (nslaves<noprocmax_slaves) nslaves=noprocmax_slaves;
   
   TProfile* procprof = new TProfile("procprof", "Total Processing Time",
                                     nslaves+1, 0, nslaves+1);
   procprof->SetMarkerStyle(26);
   tt_proc->Draw("time:nslaves>>procprof");
   procprof->GetXaxis()->SetTitle("Number of Slaves");
   procprof->GetYaxis()->SetTitle("Processing Time [s]");

   TProfile* procoptprof = new TProfile("procoptprof", "Total Processing Time",
                                        nslaves+1, 0, nslaves+1);
   procoptprof->SetMarkerStyle(25);
   tt_procopt->Draw("time:nslaves>>procoptprof","","same");

   TProfile* noprocprof = new TProfile("noprocprof", "Total Processing Time",
                                       nslaves+1, 0, nslaves+1);
   noprocprof->SetMarkerStyle(24);
   tt_noproc->Draw("time:nslaves>>noprocprof","","same");

   Float_t lm = gPad->GetLeftMargin();
   Float_t rm = gPad->GetRightMargin();
   Float_t tm = gPad->GetTopMargin();
   Float_t bm = gPad->GetBottomMargin();

   Float_t legxoffset = 0.1;
   Float_t legwidth = 0.2;
   Float_t legyoffset = 0.02;
   Float_t legheight = 0.15;

   TLegend* leg = new TLegend(lm+legxoffset*(1.0-lm-rm),
                              1.0-tm-(legyoffset+legheight)*(1.0-tm-bm),
                              lm+(legxoffset+legwidth)*(1.0-lm-rm),
                              1.0-tm-legyoffset*(1.0-tm-bm));
   leg->SetBorderSize(1); 
   leg->SetFillColor(0);
   leg->AddEntry(procprof,"Full Event","p");
   leg->AddEntry(procoptprof,"Partial Event","p");
   leg->AddEntry(noprocprof,"No Data","p");
   leg->Draw();

   gPad->Update();
   TPaveText* titlepave = dynamic_cast<TPaveText*>(gPad->GetListOfPrimitives()->FindObject("title"));
   if (titlepave) {
      Double_t x1ndc = titlepave->GetX1NDC();
      Double_t x2ndc = titlepave->GetX2NDC();
      titlepave->SetX1NDC((1.0-x2ndc+x1ndc)/2.);
      titlepave->SetX2NDC((1.0+x2ndc-x1ndc)/2.);
      titlepave->SetBorderSize(0);
      gPad->Update();
   }
   gPad->Modified();
}

TTree* Build_Timing_Tree(TFile * f, const Char_t *pattern, Int_t& max_slaves) {

   TTree* timing_tree = new TTree("Timing Tree", "Timing Tree");
   timing_tree->SetDirectory(0);
   Int_t ns_holder;
   Int_t run_holder;
   Float_t time_holder;
   TBranch* br = timing_tree->Branch("perfproctime", &ns_holder,
                                     "nslaves/I:run/I:time/F");
   br->GetLeaf("nslaves")->SetAddress(&ns_holder);
   br->GetLeaf("run")->SetAddress(&run_holder);
   br->GetLeaf("time")->SetAddress(&time_holder);

   // extract timing info
   max_slaves = 0;
   TIter NextKey(f->GetListOfKeys());
   TKey* key = 0;
   while (key = dynamic_cast<TKey*>(NextKey())) {
      if(!TString(key->GetName()).Contains(TRegexp(pattern)))
         continue;

      TObject* obj = key->ReadObj();
      TTree* t = dynamic_cast<TTree*>(obj);
      if (!t) {
         delete obj;
         continue;
      }

      //parse name to get number of slaves and run
      Int_t Index = 0;
      const Char_t *name = t->GetName();
      while (Index<strlen(name)) {
        if ( name[Index]>='0' && name[Index]<='9')
        break;
        Index++;
      }

      if (Index == strlen(name)) {
         delete t;
         continue;
      } else {
         // this should be the number of slaves
         ns_holder = atoi(name+Index);
      }

      // get past number of slaves
      while (Index<strlen(name)) {
        if ( name[Index]<'0' || name[Index]>'9')
        break;
        Index++;
      }

      if (Index == strlen(name)) {
         delete t;
         continue;
      }

      while (Index<strlen(name)) {
        if ( name[Index]>='0' && name[Index]<='9')
        break;
        Index++;
      }

      if (Index == strlen(name)) {
         delete t;
         continue;
      } else {
         // this should be the run number
         run_holder = atoi(name+Index);
      }

      if(!t->FindBranch("PerfEvents")) {
         delete t;
         continue;
      }

      // extract timing information
      TPerfEvent pe;
      TPerfEvent* pep = &pe;
      t->SetBranchAddress("PerfEvents",&pep);
      Long64_t entries = t->GetEntries();
      Double_t start, end;
      Bool_t started=kFALSE;
      for (Long64_t k=0; k<entries; k++) {
         t->GetEntry(k);
         if (!started) {
            if (pe.fType==TVirtualPerfStats::kPacket) {
               start = pe.fTimeStamp.GetSec()
                       + 1e-9*pe.fTimeStamp.GetNanoSec()
                       - pe.fProcTime;
               started=kTRUE;
            }
         } else {
            if (pe.fType==TVirtualPerfStats::kPacket) {
               end = pe.fTimeStamp.GetSec()
                     + 1e-9*pe.fTimeStamp.GetNanoSec();
            }
         }
      }

      time_holder = end-start;
      timing_tree->Fill();
      if (max_slaves<ns_holder) max_slaves=ns_holder;

      delete t;
   }

   return timing_tree;
}
