// @(#)root/graf:$Name:  $:$Id: TMultiGraph.cxx,v 1.14 2004/06/18 10:46:58 brun Exp $
// Author: Rene Brun   12/10/2000

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TROOT.h"
#include "TMultiGraph.h"
#include "TGraph.h"
#include "TH1.h"
#include "TVirtualPad.h"
#include "Riostream.h"

#include <ctype.h>


ClassImp(TMultiGraph)

//______________________________________________________________________________
//
//   A TMultiGraph is a collection of TGraph (or derived) objects
//   Use TMultiGraph::Add to add a new graph to the list.
//   The TMultiGraph owns the objects in the list.
//   Drawing options are the same as for TGraph
//   Example;
//     TGraph *gr1 = new TGraph(...
//     TGraphErrors *gr2 = new TGraphErrors(...
//     TMultiGraph *mg = new TMultiGraph();
//     mg->Add(gr1,"lp");
//     mg->Add(gr2,"cp");
//     mg->Draw("a");
//
//  The drawing option for each TGraph may be specified as an optional
//  second argument of the Add function.
//  If a draw option is specified, it will be used to draw the graph,
//  otherwise the graph will be drawn with the option specified in
//  TMultiGraph::Draw

//______________________________________________________________________________
TMultiGraph::TMultiGraph(): TNamed()
{
// TMultiGraph default constructor

   fGraphs    = 0;
   fHistogram = 0;
   fMaximum   = -1111;
   fMinimum   = -1111;
}

//______________________________________________________________________________
TMultiGraph::TMultiGraph(const char *name, const char *title)
       : TNamed(name,title)
{
// constructor with name and title
   fGraphs    = 0;
   fHistogram = 0;
   fMaximum   = -1111;
   fMinimum   = -1111;
}

//______________________________________________________________________________
TMultiGraph::~TMultiGraph()
{
// TMultiGraph destructor


   if (!fGraphs) return;
   TGraph *g;
   TIter   next(fGraphs);
   while ((g = (TGraph*) next())) {
     g->ResetBit(kMustCleanup);
   }
   fGraphs->Delete();
   delete fGraphs;
   fGraphs = 0;
   delete fHistogram;
   fHistogram = 0;
}

//______________________________________________________________________________
void TMultiGraph::Add(TGraph *graph, Option_t *chopt)
{
   // add a new graph to the list of graphs
   // note that the graph is now owned by the TMultigraph.
   // Deleting the TMultiGraph object will automatically delete the graphs.
   // You should not delete the graphs when the TMultigraph is still active.

   if (!fGraphs) fGraphs = new TList();
   graph->SetBit(kMustCleanup);
   fGraphs->Add(graph,chopt);
}

//______________________________________________________________________________
void TMultiGraph::Browse(TBrowser *)
{
    Draw("alp");
    gPad->Update();
}

//______________________________________________________________________________
Int_t TMultiGraph::DistancetoPrimitive(Int_t px, Int_t py)
{
// Compute distance from point px,py to each graph
//

//*-*- Are we on the axis?
   const Int_t kMaxDiff = 10;
   Int_t distance = 9999;
   if (fHistogram) {
      distance = fHistogram->DistancetoPrimitive(px,py);
      if (distance <= 0) return distance;
   }


//*-*- Loop on the list of graphs
   if (!fGraphs) return distance;
   TGraph *g;
   TIter   next(fGraphs);
   while ((g = (TGraph*) next())) {
      Int_t dist = g->DistancetoPrimitive(px,py);
      if (dist <= 0) return 0;
      if (dist < kMaxDiff) {gPad->SetSelected(g); return dist;}
   }
   return distance;
}

//______________________________________________________________________________
void TMultiGraph::Draw(Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*Draw this multigraph with its current attributes*-*-*-*-*-*-*
//*-*                  ==========================================
//
//   Options to draw a graph are described in TGraph::PainGraph
//
//  The drawing option for each TGraph may be specified as an optional
//  second argument of the Add function.
//  If a draw option is specified, it will be used to draw the graph,
//  otherwise the graph will be drawn with the option specified in
//  TMultiGraph::Draw

  AppendPad(option);
}

//______________________________________________________________________________
TH1F *TMultiGraph::GetHistogram() const
{
//    Returns a pointer to the histogram used to draw the axis
//    Takes into account the two following cases.
//       1- option 'A' was specified in TMultiGraph::Draw. Return fHistogram
//       2- user had called TPad::DrawFrame. return pointer to hframe histogram

   if (fHistogram) return fHistogram;
   if (!gPad) return 0;
   gPad->Modified();
   gPad->Update();
   if (fHistogram) return fHistogram;
   TH1F *h1 = (TH1F*)gPad->FindObject("hframe");
   return h1;
}

//______________________________________________________________________________
TAxis *TMultiGraph::GetXaxis() const
{
   // Get x axis of the graph.

   if (!gPad) return 0;
   TH1 *h = GetHistogram();
   if (!h) return 0;
   return h->GetXaxis();
}

//______________________________________________________________________________
TAxis *TMultiGraph::GetYaxis() const
{
   // Get y axis of the graph.

   if (!gPad) return 0;
   TH1 *h = GetHistogram();
   if (!h) return 0;
   return h->GetYaxis();
}

//______________________________________________________________________________
void TMultiGraph::Paint(Option_t *option)
{
// paint all the graphs of this multigraph

  if (fGraphs->GetSize() == 0) return;
  
  char *l;
  static char chopt[33];
  Int_t nch = strlen(option);
  Int_t i;
  for (i=0;i<nch;i++) chopt[i] = toupper(option[i]);
  chopt[nch] = 0;
  Double_t *x, *y;
  TGraph *g;

  l = strstr(chopt,"A");
  if (l) {
     *l = ' ';
     TIter   next(fGraphs);
     Int_t npt = 100;
     Double_t maximum, minimum, rwxmin, rwxmax, rwymin, rwymax, uxmin, uxmax, dx, dy;
     rwxmin    = gPad->GetUxmin();
     rwxmax    = gPad->GetUxmax();
     rwymin    = gPad->GetUymin();
     rwymax    = gPad->GetUymax();
     char *xtitle = 0;
     char *ytitle = 0;
     Int_t firstx = 0;
     Int_t lastx  = 0;
     
     if (fHistogram) {
        //cleanup in case of a previous unzoom
        if (fHistogram->GetMinimum() >= fHistogram->GetMaximum()) {
           Int_t nch = strlen(fHistogram->GetXaxis()->GetTitle());
           firstx = fHistogram->GetXaxis()->GetFirst();
           lastx  = fHistogram->GetXaxis()->GetLast();
           if (nch) {
              xtitle = new char[nch+1];
              strcpy(xtitle,fHistogram->GetXaxis()->GetTitle());
           }
           nch = strlen(fHistogram->GetYaxis()->GetTitle());
           if (nch) {
              ytitle = new char[nch+1];
              strcpy(ytitle,fHistogram->GetYaxis()->GetTitle());
           }
           delete fHistogram;
           fHistogram = 0;
        }
     }
     if (fHistogram) {
        minimum = fHistogram->GetYaxis()->GetXmin();
        maximum = fHistogram->GetYaxis()->GetXmax();
        uxmin   = gPad->PadtoX(rwxmin);
        uxmax   = gPad->PadtoX(rwxmax);
     } else {
        rwxmin = 1e100;
        rwxmax = -rwxmin;
        rwymin = rwxmin;
        rwymax = -rwymin;
        while ((g = (TGraph*) next())) {
           Int_t npoints = g->GetN();
           x = g->GetX();
           y = g->GetY();
           for (i=0;i<npoints;i++) {
              if (x[i] < rwxmin) rwxmin = x[i];
              if (x[i] > rwxmax) rwxmax = x[i];
              if (y[i] > rwymax) rwymax = y[i];
              if (y[i] < rwymin) rwymin = y[i];
           }
           g->ComputeRange(rwxmin, rwymin, rwxmax, rwymax);
           if (g->GetN() > npt) npt = g->GetN();
        }
        if (rwxmin == rwxmax) rwxmax += 1.;
        if (rwymin == rwymax) rwymax += 1.;
        dx = 0.05*(rwxmax-rwxmin);
        dy = 0.05*(rwymax-rwymin);
        uxmin    = rwxmin - dx;
        uxmax    = rwxmax + dx;
        if (gPad->GetLogy()) {
           if (rwymin <= 0) rwymin = 0.001*rwymax;
           minimum = rwymin/(1+0.5*TMath::Log10(rwymax/rwymin));
           maximum = rwymax*(1+0.2*TMath::Log10(rwymax/rwymin));
        } else {
           minimum  = rwymin - dy;
           maximum  = rwymax + dy;
        }
        if (minimum < 0 && rwymin >= 0) minimum = 0;
        if (maximum > 0 && rwymax <= 0) maximum = 0;
     }

     if (fMinimum != -1111) rwymin = minimum = fMinimum;
     if (fMaximum != -1111) rwymax = maximum = fMaximum;
     if (uxmin < 0 && rwxmin >= 0) {
        if (gPad->GetLogx()) uxmin = 0.9*rwxmin;
        //else                 uxmin = 0;
     }
     if (uxmax > 0 && rwxmax <= 0) {
        if (gPad->GetLogx()) uxmax = 1.1*rwxmax;
        //else                 uxmax = 0;
     }
     if (minimum < 0 && rwymin >= 0) {
        if(gPad->GetLogy()) minimum = 0.9*rwymin;
        //else                minimum = 0;
     }
     if (maximum > 0 && rwymax <= 0) {
        if(gPad->GetLogy()) maximum = 1.1*rwymax;
        //else                maximum = 0;
     }
     if (minimum <= 0 && gPad->GetLogy()) minimum = 0.001*maximum;
     if (uxmin <= 0 && gPad->GetLogx()) {
        if (uxmax > 1000) uxmin = 1;
        else              uxmin = 0.001*uxmax;
     }
     rwymin = minimum;
     rwymax = maximum;
     if (fHistogram) {
        fHistogram->GetYaxis()->SetLimits(rwymin,rwymax);
     }

//*-*-  Create a temporary histogram to draw the axis
     if (!fHistogram) {
        // the graph is created with at least as many channels as there are points
        // to permit zooming on the full range
        rwxmin = uxmin;
        rwxmax = uxmax;
        fHistogram = new TH1F(GetName(),GetTitle(),npt,rwxmin,rwxmax);
        if (!fHistogram) return;
        fHistogram->SetMinimum(rwymin);
        fHistogram->SetBit(TH1::kNoStats);
        fHistogram->SetMaximum(rwymax);
        fHistogram->GetYaxis()->SetLimits(rwymin,rwymax);
        fHistogram->SetDirectory(0);
        if (xtitle) {fHistogram->GetXaxis()->SetTitle(xtitle); delete [] xtitle;}
        if (ytitle) {fHistogram->GetYaxis()->SetTitle(ytitle); delete [] ytitle;}
        if (firstx != lastx) fHistogram->GetXaxis()->SetRange(firstx,lastx);
     }
     fHistogram->Paint("0");
   }

   if (fGraphs) {
      TObjOptLink *lnk = (TObjOptLink*)fGraphs->FirstLink();
      TObject *obj;

      while (lnk) {
         obj = lnk->GetObject();
         if (strlen(lnk->GetOption())) obj->Paint(lnk->GetOption());
         else                          obj->Paint(chopt);
         lnk = (TObjOptLink*)lnk->Next();
      }
   }
}

//______________________________________________________________________________
void TMultiGraph::Print(Option_t *option) const
{
// Print the list of graphs

   TGraph *g;
   if (fGraphs) {
     TIter   next(fGraphs);
     while ((g = (TGraph*) next())) {
       g->Print(option);
     }
   }
}

//______________________________________________________________________________
void TMultiGraph::RecursiveRemove(TObject *obj)
{
   // Recursively remove this object from a list. Typically implemented
   // by classes that can contain mulitple references to a same object.

   if (!fGraphs) return;
   TObject *objr = fGraphs->Remove(obj);
   if (!objr) return;
   delete fHistogram; fHistogram = 0;
   if (gPad) gPad->Modified();
}

//______________________________________________________________________________
void TMultiGraph::SavePrimitive(ofstream &out, Option_t *option)
{
    // Save primitive as a C++ statement(s) on output stream out

   char quote = '"';
   out<<"   "<<endl;
   if (gROOT->ClassSaved(TMultiGraph::Class())) {
       out<<"   ";
   } else {
       out<<"   TMultiGraph *";
   }
   out<<"multigraph = new TMultiGraph();"<<endl;
   out<<"   multigraph->SetName("<<quote<<GetName()<<quote<<");"<<endl;
   out<<"   multigraph->SetTitle("<<quote<<GetTitle()<<quote<<");"<<endl;

   TGraph *g;
   if (fGraphs) {
     TIter   next(fGraphs);
     while ((g = (TGraph*) next())) {
       g->SavePrimitive(out,"multigraph");
     }
   }
   out<<"   multigraph->Draw("
      <<quote<<option<<quote<<");"<<endl;
}

//______________________________________________________________________________
void TMultiGraph::SetMaximum(Double_t maximum)
{
   fMaximum = maximum;
   if (fHistogram)  fHistogram->SetMaximum(maximum);
}

//______________________________________________________________________________
void TMultiGraph::SetMinimum(Double_t minimum)
{
   fMinimum = minimum;
   if (fHistogram) fHistogram->SetMinimum(minimum);
}
