// Classe traceur d histogramme                 96-99
//  C. Magneville , R. Ansari               2000-2007
// (C)  CEA-DAPNIA          LAL-IN2P3/CNRS

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <math.h>
#include <typeinfo>

#include "pihisto.h"

//------ Implementation classe P1DHistoWrapper
P1DHistoWrapper::P1DHistoWrapper(int_4 asx)
  :  P1DArrayAdapter(asx) , 
     mScale(1.) , mOff(0.) , mRetFg(0)
{
}

P1DHistoWrapper::~P1DHistoWrapper()
{
}

int P1DHistoWrapper::GetStatInfoAsText(vector<string> & /* text */ )
{
  return 0;
}

int P1DHistoWrapper::DecodeOptionString(vector<string> & opt, bool rmdecopt)
{
  if(opt.size() < 1) return(0); 

  vector<string> udopt;  // On gardera ici les options non decodees
  unsigned int k = 0;
  int ndec = opt.size();
  for( k=0; k<opt.size(); k++ ) {
    string & opts = opt[k];
    if(opts.substr(0,7) == "hs1") {
      mScale = 1.;  mOff = 0.; 
    }
    else if(opts.substr(0,7) == "hscale=") {
      mScale = atof(opts.substr(7).c_str());
    }
    else if(opts.substr(0,8) == "hoffset=") {
      mOff = atof(opts.substr(8).c_str());
    }
    else if(opts.substr(0,8) == "hbincont") {
      mRetFg = 0;
    }    
    else if(opts.substr(0,7) == "hbinerr") {
      mRetFg = 1;
    }    
    else if(opts.substr(0,7) == "hbinent") {
      mRetFg = 2;
    }    
    else {
      ndec--;
      // S'il faut supprimer les options decodees
      if (rmdecopt)  udopt.push_back(opts);
    }
  }
  // S'il faut supprimer les options decodees, on remplace l'argument opt
  // par le vecteur des options non decodees.
  if (rmdecopt)  opt = udopt;
  return(ndec);
}

int
P1DHistoWrapper::OptionToString(vector<string> & opt) const
{
  char buff[64];
  sprintf(buff, "hscale=%g", mScale);  opt.push_back(buff);
  sprintf(buff, "hoffset=%g", mOff);  opt.push_back(buff);
  if (mRetFg == 2) opt.push_back("hbinent");
  else if (mRetFg == 1) opt.push_back("hbinerr");
  else opt.push_back("hbincont");

  return 1;
}


//++
// Class	PIHisto
// Lib		PIext
// include	pihisto.h
//
//	Classe traceur d'objet histogramme (classe *P1DHistoWrapper*)
//--
//++
// Links	Parents
// PIDrawer
//--
//++
// Titre	Constructeur, méthodes
//--
//++
// PIHisto(P1DHistoWrapper* histowp, bool ad=false)
//	Constructeur. Si "ad == true", l'objet "histowp" est détruit par 
//	le destructeur de l'objet "PIHisto"
//	Note : "histowp" doit être créé par new si ad==true
//
//--


PIHisto::PIHisto(P1DHistoWrapper* histowp, bool ad)
: PIDrawer(), mHistoWp(histowp)
{
  mAdDO = ad;     // Flag pour suppression automatique de mHistoWp
  stats=true;
  pline=true;
  error=false;
  filled=false;
  spoX=-0.01; spoY=-0.01;
  SetName("HistoDrw");
}

PIHisto::~PIHisto()
{
  if(mAdDO) delete mHistoWp;
}

void
PIHisto::UpdateLimits()
{
  if (!mHistoWp)  return;
  if ( mHistoWp->NBins() < 1 ) {
    SetLimits(mHistoWp->XMin(), mHistoWp->XMax(), 0., 1.);
  }
  double v,hmin,hmax;
  hmin = hmax = (*mHistoWp)(0);
  for (int_4 i=1; i<mHistoWp->NBins(); i++) {
    v = (*mHistoWp)(i);
    if(v<hmin) hmin = v;
    if(v>hmax) hmax = v;
  }
  if ( isLogScaleY() ) {
    if ( hmin >= 0.) {
      if ( hmax <= hmin ) {
	hmax = hmin*10.;
	if (hmax <= 0.) hmax = 1.;
	if (hmin <= 0.) hmin = hmax / 100.; 
      }
      else if ( (hmin == 0.) && (hmax > hmin) ) hmin = hmax/1.e6;
      else hmin /= 1.1;
    }
    else if (hmax > 0.) hmax *= 1.1;
  }
  else {
      v = 0.05*(hmax-hmin);
      hmin -= v;   hmax += v;
  }
  if(hmax<=hmin) hmax = hmin+1.;
    
  SetLimits(mHistoWp->XMin(), mHistoWp->XMax(), hmin, hmax);
}

void
PIHisto::Draw(PIGraphicUC* g, double /*xmin*/, double /*ymin*/, double /*xmax*/, double /*ymax*/)
{
  if (axesFlags != kAxesNone) DrawAxes(g);

  if (!mHistoWp)  return;
  if (mHistoWp->NBins()<=0) return;
 
  mHistoWp->Update();

  // Tracer d'une polyline si demandee
  bool drawpline=false;
  if (pline) drawpline=true;
  // Tracer des marqueurs si demande ou si HProf
  bool drawmarker=false;
  if (GetGraphicAtt().GetMarker() != PI_NotDefMarker) drawmarker = true;
  // Tracer des erreurs ?
  bool drawerr=false;
  if (error) drawerr = true;
  // Fill de l'histo ?
  bool drawfill=false;
  if(filled) drawfill=true; else drawfill=false;
  // Et aussi si on ne demande ni ligne ni marqueur ni erreur ni fill ?
  if( !drawmarker && !drawfill && !drawerr ) drawpline=true;

  // Remplissage des bins avec la couleur courante (trace en premier)
  if(drawfill) {
    for(int i=0; i<mHistoWp->NBins(); i++) {
      double left   = mHistoWp->BinLowEdge(i);
      double width  = mHistoWp->BinWidth();
      double bottom = 0;
      double height = (*mHistoWp)(i);
      g->DrawFBox(left,bottom,width,height);
    }
  }

  // Trace des marqeurs
  if(drawmarker) {
    double x1,y1; 
    for(int i=0; i<mHistoWp->NBins(); i++) {
      x1 = mHistoWp->BinCenter(i);
      y1 = (*mHistoWp)(i);
      g->DrawMarker(x1,y1);
    }
  }

  // Trace des erreurs
  if(drawerr) {
    if(GetGraphicAtt().GetLineAtt()==PI_NotDefLineAtt) g->SelLine(PI_ThinLine);
    double x1,x2,y1,y2;  
    double bw = mHistoWp->BinWidth();
    for(int i=0; i<mHistoWp->NBins(); i++) {
      if(mHistoWp->Error(i)>0.) {
        // barres d'erreur verticales
        x1 = x2 = mHistoWp->BinCenter(i);
        y1 = (*mHistoWp)(i) - mHistoWp->Error(i);
        y2 = (*mHistoWp)(i) + mHistoWp->Error(i);
        g->DrawLine(x1,y1, x1, y2);
        // limites de barres d'erreurs (horizontales)
        x1 -= bw/3.;  x2 += bw/3.;
        g->DrawLine(x1,y1, x2, y1);
        g->DrawLine(x1,y2, x2, y2);
      }
    }
  }

  // Trace de la ligne continue si demandee
  if(drawpline) {
    if(GetGraphicAtt().GetLineAtt()==PI_NotDefLineAtt) g->SelLine(PI_ThinLine);
    PIGrCoord* x1 = new PIGrCoord[2*mHistoWp->NBins()+2];
    PIGrCoord* y1 = new PIGrCoord[2*mHistoWp->NBins()+2];
    double dx = mHistoWp->BinWidth();
    int npt=0;
    x1[npt] = mHistoWp->BinLowEdge(0);
    y1[npt] = 0;
    npt++;
    for(int i=0; i<mHistoWp->NBins(); i++) {
      x1[npt] = mHistoWp->BinLowEdge(i);
      y1[npt] = (*mHistoWp)(i);
      npt++;
      x1[npt] = (double)x1[npt-1] + dx;
      y1[npt] = y1[npt-1];
      npt++;
    }
    x1[npt] = x1[npt-1];
    y1[npt] = 0;
    npt++;
    g->DrawPolygon(x1,y1,npt,false);
    delete [] x1; delete [] y1;
  }

  // Trace/Ecriture des statistiques
  // A faire a la fin - DrawStats change l'attribut de ligne 
  if(stats) DrawStats(g);
}

int
PIHisto::DecodeOptionString(vector<string> & opt, bool rmdecopt)
{
  int optsz1 = opt.size();
  if(optsz1<1) return(0); 
  int ndec1 = PIDrawer::DecodeOptionString(opt, rmdecopt);
  if(optsz1-ndec1<1) return(ndec1);  // si tout a ete decode

  vector<string> udopt;  // On gardera ici les options non decodees
  unsigned int k = 0;
  int ndec = opt.size();
  for( k=0; k<opt.size(); k++ ) {
    string opts = opt[k];
    if(opts=="sta" || opts=="stat" || opts=="stats") stats=true;
    else if(  opts=="nsta"   || opts=="nstat"
           || opts=="nostat" || opts=="nostats") stats=false;
    else if(opts=="err") error=true;
    else if(opts=="noerr" || opts=="nerr") error=false;
    else if(opts=="fill") { filled=true; pline=false; }  
    else if(opts=="nofill" || opts=="nfill") filled=false;
    else if(opts=="pline") { pline=true; filled=false; }
    else if(opts=="nopline") pline=false;
    else if(opts.substr(0,11) == "statposoff=") {
      double xo=0., yo=0.;
      sscanf(opts.substr(11).c_str(),"%lf,%lf",&xo, &yo);
      spoX=xo; spoY=yo;
    }
    else {
      ndec--;
      // S'il faut supprimer les options decodees
      if (rmdecopt)  udopt.push_back(opts);
    }
  }

  // S'il faut supprimer les options decodees, on remplace l'argument opt
  // par le vecteur des options non decodees.
  if (rmdecopt)  opt = udopt;

  // Decodage des options par le histo-wrapper
  int ndec2 = 0;
  if ( ( optsz1-ndec1-ndec > 0) && (mHistoWp) ) {
    ndec2 = mHistoWp->DecodeOptionString(opt, rmdecopt);
  }
  return(ndec+ndec1+ndec2);
}

int
PIHisto::OptionToString(vector<string> & opt) const
{
 PIDrawer::OptionToString(opt);

 if(stats) opt.push_back("stat"); else opt.push_back("nostat");
 if(error) opt.push_back("err");  else opt.push_back("noerr");
 if(filled) opt.push_back("fill"); else opt.push_back("nofill");
 if(pline) opt.push_back("pline"); else opt.push_back("nopline");

 char str[256]; sprintf(str,"statposoff=%g,%g",spoX,spoY);
 opt.push_back(str);

 if (mHistoWp) mHistoWp->OptionToString(opt);
 return 1;
}

void
PIHisto::GetOptionsHelpInfo(string& info)
{
info += " ---- PIHisto options help info : \n" ;
info += " sta,stat,stats:            activate   statistic display\n";
info += " nsta,nstat,nostat,nostats: deactivate statistic display\n";
info += " pline/nopline: display/do not display as polyline (def= pline) \n";
info += " err/noerr,nerr: draw/do not draw error bars (def= noerr) \n";
info += " fill/nofill,nfill: fill/do not fill histo (def= nofill) \n";
info += " - Use marker attribute (marker=...) to draw markers \n"; 
info += " statposoff=OffsetX,OffsetY : Position offset for Stats drawing \n";
info += "      as a fraction of total size \n";
info += " ---- HistoWrapper options : \n" ;
info += " hbincont: select bin content as Y value for display (default) \n";
info += " hbinerr: select bin error as Y value for display \n";
info += " hbinent: select bin entries as Y value for display \n";
info += " hscale=value : multiplicative factor for Y value \n" ;
info += " hoffset=value : additive coefficient for Y value \n" ;
info += " hs1: set hscale=1 hoffset=0  (default) \n" ;
// On recupere ensuite la chaine info de la classe de base
PIDrawer::GetOptionsHelpInfo(info);
return;
}

void
PIHisto::DrawStats(PIGraphicUC* g)
{
  if (!mHistoWp) return;
  //  if (mLAtt == PI_NotDefLineAtt)  g->SelLine(PI_ThinLine);
  g->SelLine(PI_ThinLine);
// Reza(12/2008): utilisation de flag et fraction pour selection automatique taille de fonte 
  if (mAFSz) {  
    double fsz = (YMax() - YMin())*mFontSzF;
    g->SelFontSz(fsz);  // au lieu de 1/30*(ymax-ymin)  
  }
  // La hauteur de la cellule
  PIGrCoord a, d;
  double cH = (double)g->GetFontHeight(a,d);
  vector<string> lines;
  int nlig = mHistoWp->GetStatInfoAsText(lines);
  if (nlig < 1) return;

  double cellHeight = nlig*1.2 * cH;

  int idxll = 0;
  
  int kl;
  // on recherche la ligne la plus longue
  for(kl=1; kl<nlig; kl++)
    if ( lines[kl].length() > lines[idxll].length() )  idxll = kl;

  double cellWidth =  1.1 * (double)g->CalcStringWidth(lines[idxll].c_str());

  double ofpx = spoX*(XMax()-XMin());
  double ofpy = spoY*(YMax()-YMin());
  
  double xu, yu, cw;
  // Les limites du cadre
  xu = g->DeltaUCX(XMax(), -cellWidth);
  yu = g->DeltaUCY(YMax(), -cellHeight);
  double recw = XMax()-xu;
  double rech = YMax()-yu;
  xu += ofpx;  yu += ofpy;
  g->DrawBox(xu, yu, recw, rech);

  // L'ecriture des labels (attention aux inversions possibles des axes!)
  cw = (g->isAxeXDirRtoL()) ? -0.05*cellWidth : -0.95*cellWidth;
  xu = g->DeltaUCX(XMax(),cw);

  cw = (g->isAxeYDirUpDown()) ? -0.15*cH : -1.15*cH;
  yu = g->DeltaUCY(YMax(),cw);
  xu += ofpx;  yu += ofpy;

  for(kl=0; kl<nlig; kl++) {
    g->DrawString(xu, yu, lines[kl].c_str() );
    cw +=  -1.15*cH;
    yu = g->DeltaUCY(YMax(),cw);  yu += ofpy;
  }
                       
}

/* --Methode-- */
double
PIHisto::GetDistanceToPoint(double x, double y)
{
 if (!mHistoWp) return 1.e+9;

 double dist = -1.e+18;
 for(int i=0; i<mHistoWp->NBins(); i++) {
   double xp=mHistoWp->BinCenter(i);
   double yp=(*mHistoWp)(i);
   xp = (xp-x)/(XMax()-XMin())/0.5;
   yp = (yp-y)/(YMax()-YMin())/0.5;
   xp = xp*xp+yp*yp;
   if(dist<0. || xp<dist) dist = xp;
 }
 dist=sqrt(fabs(dist));
 //cout<<dist<<"PIHisto: xlim="<<XMin()<<","<<XMax()<<" ylim="<<YMin()<<","<<YMax()
 //    <<" NBins="<<mHistoWp->NBins()<<endl;
 //cout<<"....d="<<dist<<" x="<<x<<" y="<<y<<endl;

 return dist;
}
