#include <stdio.h>
#include <stdlib.h>
#include <pisysdep.h>
#include PIAPP_H

#include <iostream>
#include <math.h>

#include "sopnamsp.h"
#include "pihisto2d.h"
#include "srandgen.h"

static int dbg = 0;


//------ Implementation classe P2DHistoWrapper
P2DHistoWrapper::P2DHistoWrapper(int_4 asx, int_4 asy) 
  :  P2DArrayAdapter(asx, asy) , 
     mScale(1.) , mOff(0.) , mRetFg(0)
{
}

P2DHistoWrapper::~P2DHistoWrapper()
{
}


double P2DHistoWrapper::MeanVal(int ix1, int ix2, int jy1, int jy2)
{
  int ec;
  if (ix1>ix2) { ec=ix1; ix1=ix2; ix2=ec; }
  if (jy1>jy2) { ec=jy1; jy1=jy2; jy2=ec; }    
  double ss = 0.;
  for(int j=jy1; j<=jy2; j++) 
    for(int i=ix1; i<=ix2; i++)  ss += (double)((*this)(i, j));
  ss /= (double)((jy2-jy1+1)*(ix2-ix1+1));
  return ss;     
}

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

int P2DHistoWrapper::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
P2DHistoWrapper::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	PIHisto2D
// Lib		PIext
// include	pihisto2d.h
//
//	Classes de dessin des histogrammes a 2 dimensions pour 
//	objets *Histo2D* 
//--

//++
// Links	Parents
// PIDrawer
//--

//++
// Titre	Constructeur, méthodes
//--

//++
PIHisto2D::PIHisto2D(P2DHistoWrapper* histowp, bool ad)
//
//	Createur d'une classe de dessin pour l'histogramme 2D histo.
//--
: PIDrawer(), mHistoWp(histowp), mAdDO(ad), mLogScale(10.), mFPoints(0.5)
{
// mAdDO : Flag pour suppression automatique de mHistoWp
// Attention: mFPoints n'est initialise que si on display par nuages de points
//            mLogScale n'est initialise que si on utilise une echelle log
UseScale();
UseColors();
UseDisplay();
UseDyn();
UseFrac();
SetStats();
SetStatPosOffset();
SetName("Histo2DDrw"); 
// PIHisto2D has specific control tools
mFgSpecContWind = true;
}

//++
PIHisto2D::~PIHisto2D()
//
//	Destructeur.
//--
{
// La fonction virtuelle DeactivateControlWindow() doit etre appele
// a ce niveau - En effet au niveau du destructeur de base, il 
// semble pointer sur la fonction de la classe de base
//                    Reza - Octobre 2002 
// Desactivation totale de la fenetre de controle specialise 
// ---> parametre d'appel PIBaseWdgGen* wdg=NULL
DeactivateControlWindow(NULL);
if(mAdDO && mHistoWp!=NULL) delete mHistoWp;
}

//++
void PIHisto2D::UseColors(bool fg,CMapId cmap,bool revcmap)
//
//	Choix de la couleur si fg=true avec la color map cmap.
//	(pour la couleur cmap cf picmap.h).
//      Independamment du choix du display, la dynamique est
//      codee sur la color map donnant ainsi une double
//      information. Par exemple, carres de tailles variables
//      en couleur. Cette option est incontournable dans le cas
//      d'un display par des carres de taille fixe.
//      revcmap doit etre mis a "true" si on veut avoir une color map
//      inversee.
//| -**- gestion dans H2WinArg par menu deroulant Black&White etc...
//--
{
mFgCol = fg; mCmap = cmap; mRevCmap = revcmap;
}

//++
void PIHisto2D::UseScale(unsigned short type,double logscale)
//
//	Pour changer les echelles (lineaire ou logarithmique)
//| Type = 0 : echelle lineaire
//|      = 1 : echelle log10
//| -**- Explication du codage en type=0 (lineaire) :
//|   1. [hmin,hmax] -> [0,1]
//|           h      ->   f = (h-hmin)/(hmax-hmin)
//|   2. codage de f=[0,1] sur la dynamique du display choisi
//| -**- Explication du codage en type=1 (logarithmique base 10) :
//|   1. map lineaire entre 0 et 1:
//|      [hmin,hmax] -> [0,1]
//|           h      ->   f = (h-hmin)/(hmax-hmin)
//|   2. transformation logarithmique de base 10 :
//|      [0,1] -> [0,1]
//|        f   -> lf = log10(1.+f*(logscale-1))/log10(logscale)
//|   3. codage de lf=[0,1] sur la dynamique du display choisi
//| -**- gestion dans H2WinArg par menu deroulant Lineaire/Log10
//|         et "logscale" par saisie de valeur dans champ LogScal
//--
{
if(type==0)       mTypScal=0;
else if(type==1) {mTypScal=1; if(logscale>1.) mLogScale=logscale;}
else              mTypScal=0;
}

//++
void PIHisto2D::UseDisplay(unsigned short type,double fnpt)
//
//	Type de Display
//| Type = 0 : carres de tailles variables
//| Type = 1 : nuages de points
//|            Le nombre de points a utiliser est fnpt*N
//|            ou N est le nombre de pixels ecran contenu
//|            dans un bin de l'histogramme.
//| Type = 2 : code a la "hbook2" " .+123...9AB...YZ*"
//|            (cf detail PIHisto2D::HPrint2)
//| Type = 3 : carres de taille fixe (couleur).
//| -**- gestion dans H2WinArg par menu deroulant Carres_Var etc...
//|         et "fnpt" par saisie de valeur dans champ PerPt
//--
{
if(type==0) mTypDisp = 0;
else if(type==1) {
  mTypDisp = 1;
  if(fnpt<0.) mFPoints = 0.;
    else if(fnpt>1.) mFPoints = 1.;
      else mFPoints = fnpt;
}
else if(type==2) mTypDisp = 2;
else if(type==3) mTypDisp = 3;
else             mTypDisp = 1;
}

//++
void PIHisto2D::UseDyn(double fmin,double fmax)
//
//	Gestion de la dynamique a representer:
//|  La dynamique va etre transformee de [hmin,hmax] vers [0,1] selon
//|  [hmin,hmax] -> [0,1]
//|       h      ->   f = (h-hmin)/(hmax-hmin)
//|  Par la suite, selon ce qui est demande, f va coder le display 
//|  ou etre transforme en une autre echelle [0,1] (ex: echelle log10).
//|  Si hmax<=hmin, ils sont forces a la dynamique totale de l'histo2D.
//| -**- gestion dans H2WinArg par saisie de valeurs dans champ Dyn
//--
{
if( (mHistoWp) && (mHistoWp->NBinX()>0) && (mHistoWp->NBinY()>0) && (fmin>=fmax) ) {
  double v,hmin,hmax;
  hmin = hmax = (*mHistoWp)(0,0);
  for (int_4 i=0; i<mHistoWp->NBinX(); i++) 
    for (int_4 j=0; j<mHistoWp->NBinY(); j++) {
      v = (*mHistoWp)(i, j);
      if(v<hmin) hmin = v;
      if(v>hmax) hmax = v;
    }
  fmin = hmin;  fmax = hmax;
}

if(fmin>=fmax) fmax = fmin+1.;
mHMin = fmin; mHMax = fmax;
}

//++
void PIHisto2D::UseFrac(double frmin,double frmax)
//
//	Pour definir la fraction de la dynamique a dessiner:
//| Selon le type de display (f=[0,1] cf PIHisto2D::UseDyn),
//|  - on ne dessine rien si f <= frmin dans les cas de display avec
//|    des nuages de points ou des carres de tailles variables.
//|    Pour un display "a la hbook2" on force frmin = 0.
//|  - frmax n'est utilise que pour la representation avec
//|    des carres de tailles variables: c'est la taille
//|    maximum que peut avoir le carre exprimee en unite
//|    de la taille du bin (ex: si frmax=0.8 le carre
//|    le + grand qui pourra etre dessine dans un bin
//|    aura une taille egale a 0.8*(taille du bin)).
//| -**- gestion dans H2WinArg par saisie de valeurs dans champ Frac
//--
{
if(frmin<0.  || frmin>=1.) frmin = 0.;
if(frmax<=0. || frmax>1. ) frmax = 1.;
if(frmin>=frmax) {frmin=0.1; frmax=0.9;}
mFracMin = frmin; mFracMax = frmax;
}

//++
void PIHisto2D::Print(int lp)
//
//	Print de l'etat des options du display.
//--
{
printf("PIHisto2D::Print FgCol=%d Cmap=%d (Rev=%d) TypScal=%d TypDisp=%d (FPoints=%g)\n"
      ,(int)mFgCol,(int)mCmap,(int)mRevCmap,mTypScal,mTypDisp,mFPoints);
printf("                 Dyn=%g,%g Frac=%g,%g LogSc=%g H=%p\n"
      ,mHMin,mHMax,mFracMin,mFracMax,mLogScale,mHistoWp);
fflush(stdout);
}

//++
void PIHisto2D::UpdateLimits()
//
//	Definition des tailles graphiques en fonction
//	des caracteristiques de l'histogramme a dessiner.
//--
{
 if(!mHistoWp) return;
 SetLimits(mHistoWp->XMin(), mHistoWp->XMax(), mHistoWp->YMin() , mHistoWp->YMax());
}

//++
void PIHisto2D::ShowControlWindow(PIBaseWdgGen* wdg)
//
//	Affichage de la fenetre de controle H2WinArg
//--
{
  H2WinArg::SetCurrentPIHisto2D(this);
  H2WinArg::ShowPIHisto2DTools(wdg);
}

//++
void PIHisto2D::DeactivateControlWindow(PIBaseWdgGen* wdg)
//
//	Desactivation de la fenetre de controle specialisee
//--
{
  if (H2WinArg::GetCurrentPIHisto2D() == this) { 
    // si wdg != NULL, c'est un Detach (Drawer detache du PIBaseWdg
    // si wdg == NULL, c'est un delete du PIHisto2D (du PIDrawer)
    if ((wdg == NULL) || (H2WinArg::GetCurrentBaseWdg() == wdg)) {
      H2WinArg::SetCurrentBaseWdg(NULL);
      H2WinArg::SetCurrentPIHisto2D(NULL);
      H2WinArg::HidePIHisto2DTools();
    }
  }
  PIDrawer::DeactivateControlWindow(wdg);
  return;
}

//++
void PIHisto2D::Draw(PIGraphicUC* g,double xmin,double ymin,double xmax,double ymax)
//
//	Dessin de l'histogramme.
//| -**- Code de dessin selon choix des options:
//|  (detail voir UseColors UseScale UseDisplay UseDyn UseFrac)
//|  - [hmin,hmax] -> f=[0,1]
//|    (Choix hmin,hmax champ Dyn de H2WinArg)
//|  - Eventuellement ech Log -> re-codage log10 entre f=[0,1]
//|    (Choix menu deroulant et champ LogScal de H2WinArg)
//|  - Restriction de f=[0,1] -> f=[Frac(min),Frac(max)]
//|    (Choix champ Frac de H2WinArg)
//| -**- Puis selon display:
//|  0 carres variables, menu "Carres Var." de H2WinArg:
//|      if(f>Frac(min)) taille carre = f * Frac(max) * taille_du_bin
//|  1 nuage de points, menu "....." et champ PerPt de H2WinArg:
//|      if(f>Frac(min)) npoints = f * PerPt * npoints_ecran_dans_bin
//|  2 code hbook2, menu ".12..Z*" de H2WinArg:
//|      if(f>0) map de f=]0,1] dans ".+...Z*"
//|  3 carres pleins, menu "Carres Pleins" et couleurs de H2WinArg):
//|      couleur = lut[ f * nombre_d_entree_dans_la_lut ]
//--
{
if (axesFlags != kAxesNone) DrawAxes(g);

if(!mHistoWp) return;
if(mHistoWp->NBinX()<=0 || mHistoWp->NBinY()<=0) return;

// Caracteristiques histogramme
double dx = mHistoWp->WBinX(),dy = mHistoWp->WBinY();
double p1dx,p1dy;
g->DGrC2UC(1.,1.,p1dx,p1dy);

// Gamme a representer entre [0,1] mais >=fracmin et scale fracmax
double fracmin=FMin(), fracmax=FMax();
double llscale = log10(LogScale());

// gestion Couleurs.
PIColors fgcoul = GetGraphicAtt().GetFgColor();
PIColors bgcoul = GetGraphicAtt().GetBgColor();
PIColorMap* cmap=NULL;
int ncol = 0;
if (mFgCol) {
  cmap = new PIColorMap(mCmap);
  cmap->ReverseColorIndex(mRevCmap);
  ncol = cmap->NCol();
  if(mTypDisp==3) fracmin=-1.;
}

// gestion epaisseur de ligne
PILineAtt LineAtt = GetGraphicAtt().GetLineAtt();
if(LineAtt == PI_NotDefLineAtt) GetGraphicAtt().SetLineAtt(PI_ThinLine);

// gestion Markers ou plot avec des points.
PIMarker Mk = GetGraphicAtt().GetMarker();
int MkSz = GetGraphicAtt().GetMarkerSize();
int npt = 1;
if(mTypDisp==1) {
  g->SelMarker(1,PI_DotMarker);
  npt = (int)(NPixBin(g)*FPoints()); if(npt<=0) npt = 2;
}

// gestion Font.
PIFontAtt FontAtt = GetGraphicAtt().GetFontAtt();
int FontSize = GetGraphicAtt().GetFontSzPt();
if(mTypDisp==2) {
  double dxg,dyg,dg;
  g->DUC2GrC(dx,dy,dxg,dyg);
  dg =(dxg<dyg) ? dxg : dyg;
  int npix = (int) (dg*0.9); if(npix<8) npix = 8;
  g->SelFontSzPt(npix,FontAtt);
  fracmin = 0;
}

// Plot de l'histogramme
for(int i=0; i<mHistoWp->NBinX(); i++)
  for(int j=0; j<mHistoWp->NBinY(); j++) {

  r_8 left0,bottom0;
  mHistoWp->BinLowEdge(i,j,left0,bottom0);

  // Gestion de la dynamique a dessiner
  double frac = ((*mHistoWp)(i,j)-HMin())/(HMax()-HMin());
  if(frac<0.) continue;
  if(mTypScal==1) {              // echelle log10
    frac = log10(1.+frac*(LogScale()-1.))/llscale;
    if(frac<0.) continue;
  }
  if(frac<=fracmin) continue;
  if(frac>1.) frac = 1.;
  double fracred = frac * fracmax;

  // Gestion de la couleur
  int icol = 0;
  if (cmap) {
    icol = int(ncol*frac);
    if(icol>=ncol) icol = ncol-1; else if(icol<0) icol=0;
    g->SelForeground(*cmap,icol);
  }

// Pour ne pas dessiner en dehors des axes 
  if ( (left0+dx/2. < xmin) || (left0+dx/2. > xmax) || 
         (bottom0+dy/2. < ymin) || (bottom0+dy/2. > ymax) ) continue;

  // Dessin proprement dit selon le choix graphique.
  if(mTypDisp==0) {
    //..... carres de tailles variables
    double left   = left0   + 0.5*(1.-fracred)*dx, width  = fracred*dx;
    double bottom = bottom0 + 0.5*(1.-fracred)*dy, height = fracred*dy;
    if (cmap) g->DrawFBox(left,bottom,width,height);
    else      g->DrawBox(left,bottom,width,height);
  } else if(mTypDisp==1) {
    //..... nuage de points .....
    int ipt  = int(npt *frac);
    for(int k=0;k<ipt;k++) {
      double x = left0 + drand01()*dx;
      double y = bottom0 + drand01()*dy;
      g->DrawMarker(x,y);
    }
  } else if(mTypDisp==2) {
    //..... type hbook2/hprint .+23-Z*
    char c[2];
    c[0] = HPrint2(frac); c[1]='\0';
    double x = left0 + dx/2.;
    double y = bottom0 + dy/2.;
    g->DrawString(x,y,c,PI_HorizontalCenter|PI_VerticalCenter);
  } else if(mTypDisp==3) {
    //..... carres de tailles fixes (avec gestion de continuite)
    if (cmap) g->DrawFBox(left0,bottom0,dx+p1dx,dy+p1dy);
    else      g->DrawBox(left0,bottom0,dx+p1dx,dy+p1dy);
  }

}

// Remise dans les conditions ulterieures pour la suite du graphique.
GetGraphicAtt().SetMarkerAtt(MkSz,Mk);
GetGraphicAtt().SetColAtt(fgcoul,bgcoul);
g->SelFontSzPt(FontSize,FontAtt);
GetGraphicAtt().SetLineAtt(LineAtt);
if (cmap) delete cmap;

// Fin du dessin, ecriture de la statistique.
if(stats) DrawStats(g);
}

//++
void PIHisto2D::GetClickInfo(string& info,double x,double y,double x0,double y0,bool fgdiff)
//
//	Info specifique du drawer pour la position x,y
//--
{
P2DHistoWrapper* h = HistoWrapper();
if(h == NULL) return;

int_4 i,j;
i = floor((x-h->XMin())/h->WBinX()); 
j = floor((y-h->YMin())/h->WBinY());

if(i>=0 && i<h->NBinX() && j>=0 && j<h->NBinY()) {
  char str[64];
  if(fgdiff) {
    int i0,j0;
    i0 = floor((x0-h->XMin())/h->WBinX()); 
    j0 = floor((y0-h->YMin())/h->WBinY());
    if(i0>=0 && i0<h->NBinX() && j0>=0 && j0<h->NBinY()) {
      sprintf(str," DV=%g",(*h)(i,j)-(*h)(i0,j0));
      info += str;
    } else {
      info += " DV=?";
    }
  }
  sprintf(str," V=%g",(*h)(i,j));
  info += str;
} else {
  info += " V=?";
}

return;
}

//++
void PIHisto2D::DrawStats(PIGraphicUC* g)
//
//	Dessin des informations statistiques de l'histogramme.
//--
{
  if (!mHistoWp) return;
  if (GetGraphicAtt().GetLineAtt() == PI_NotDefLineAtt)  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
  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;
  }


  //  printf("H[%d,%d] Dynamique: [%g,%g] Frac [%g,%g]\n"
  //        ,mHistoWp->NBinX(),mHistoWp->NBinY(),HMin(),HMax(),FMin(),FMax());
}

//++
char PIHisto2D::HPrint2(double f)
//
//	Codage des valeurs en caracteres (fct privee).
//| f entre [0,1] mappee entre valeur=[0,37]
//| si <0 alors =0, si >1 alors 1
//| Display 4   ==> 4<=valeur<5
//|         C   ==> 12<=valeur<13
//|             ==> valeur<=0
//|         *   ==> valeur>=1
//|         .   ==> 0<valeur<1
//|------------------------------------------
//|             1         2         3
//|   01234567890123456789012345678901234567
//|   .+23456789ABCDEFGHIJKLMNOPQRSTUVWXYZ* 
//|------------------------------------------
//--
{
char str[39] = " .+23456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*";
int i;
if(f<=0.) i = 0;
else if(f>=1.) i = 37;
else { i = (int) (f*36.); i++;}
if(i<0) i=0; else if (i>=38) i = 37;
return str[i];
}

//++
int PIHisto2D::NPixBin(PIGraphicUC* g)
//
//	Nombre de pixels ecran dans un bin d'histogramme
//	(fct privee).
//--
{
double dx = mHistoWp->WBinX(),dy = mHistoWp->WBinY();
double dxg,dyg;
g->DUC2GrC(dx,dy,dxg,dyg);
int np = (int) dxg * (int) dyg;
//printf("PIHisto2D::NPixBin H dx=%g dy=%g, G dx=%g dy=%g, np = %d\n"
//      ,dx,dy,dxg,dyg,np);
return np;
}

//++
int PIHisto2D::DecodeOptionString(vector<string> & opt, bool rmdecopt)
//
//	Decodage des options
//--
{
  // Decodage des options generales pidrawer
  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

  // Options generales pidrawer interessant le display Histo2D
  bool rev; 
  if(GetGraphicAtt().GetColMapId(rev) != CMAP_OTHER) {
    UseColors(true,GetGraphicAtt().GetColMapId(),rev);
  } else UseColors(false);

  // Decodage des options propres au display Histo2D
  vector<string> udopt;  // On gardera ici les options non decodees
  unsigned int k = 0;
  int ndec = opt.size();
  bool listopt=false;
  for( k=0; k<opt.size(); k++ ) {
    string opts = opt[k];
    if(opts=="h2help") {
      string info; GetOptionsHelpInfo(info);
      size_t q = info.find("PIHisto2D");
      if(q<info.length()-1) cout<<info.substr(q)<<endl;
    } else if(opts=="h2list") {
      listopt=true;
    } else if(opts=="sta" || opts=="stat" || opts=="stats") {
      SetStats(true);
    } else if(  opts=="nsta"   || opts=="nstat"
             || opts=="nostat" || opts=="nostats") {
      SetStats(false);
    } else  if(opts.substr(0,11) == "statposoff=") {
      double xo=0., yo=0.;
      sscanf(opts.substr(11).c_str(),"%lf,%lf",&xo, &yo);
      SetStatPosOffset(xo, yo);
    } else if(opts.substr(0,8)=="h2scale=") {
      unsigned short t=TypScale(); double ls=LogScale();
      if(opts.substr(8,3)=="lin") t=0;
      else if(opts.substr(8,3)=="log")
        {t=1; sscanf(opts.c_str(),"h2scale=log,%lf",&ls);}
      UseScale(t,ls);
    } else if(opts.substr(0,7)=="h2disp=") {
      unsigned short t=TypDisplay(); double fpts=FPoints();
      if(opts.substr(7,3)=="var") t=0;
      else if(opts.substr(7,3)=="hbk") t=2;
      else if(opts.substr(7,3)=="img") t=3;
      else if(opts.substr(7,3)=="pts")
        {t=1; sscanf(opts.c_str(),"h2disp=pts,%lf",&fpts);}
      UseDisplay(t,fpts);
    } else if(opts.substr(0,6)=="h2dyn=") {
      double hmin=HMin(),hmax=HMax(); size_t q = opts.find(',');
      sscanf(opts.c_str(),"h2dyn=%lf",&hmin);
      if(q<opts.length()-1) sscanf(opts.substr(q+1).c_str(),"%lf",&hmax);
      UseDyn(hmin,hmax);
    } else if(opts.substr(0,7)=="h2frac=") {
      double fmin=FMin(),fmax=FMax(); size_t q = opts.find(',');
      sscanf(opts.c_str(),"h2frac=%lf",&fmin);
      if(q<opts.length()-1) sscanf(opts.substr(q+1).c_str(),"%lf",&fmax);
      UseFrac(fmin,fmax);
   } 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);
  }

  // Liste des options si demande
  if(listopt) Print();

  return(ndec+ndec1+ndec2);
}

int PIHisto2D::OptionToString(vector<string> & opt) const
{
 PIDrawer::OptionToString(opt);
 char str[256];

 if(stats) opt.push_back("stat"); else opt.push_back("nstat");

 sprintf(str,"statposoff=%f,%f",spoX,spoY);
 opt.push_back(str);

 if(mTypDisp==0) sprintf(str,"h2disp=var");
  else if(mTypDisp==1) sprintf(str,"h2disp=pts,%g",mFPoints);
    else if(mTypDisp==2) sprintf(str,"h2disp=hbk");
      else if(mTypDisp==3) sprintf(str,"h2disp=img");
 opt.push_back(str);

 if(mTypScal==0) sprintf(str,"h2scale=lin");
   else if(mTypScal==1) sprintf(str,"h2disp=log,%g",mLogScale);
 opt.push_back(str);

 sprintf(str,"h2dyn=%g,%g",mHMin,mHMax);
 opt.push_back(str);

 sprintf(str,"h2frac=%g,%g",mFracMin,mFracMax);
 opt.push_back(str);

 if(mRevCmap) opt.push_back("revcmap");

 // Les options du Histo2DWrapper :
 if(mHistoWp) mHistoWp->OptionToString(opt);
 return 1;
}


//++
void PIHisto2D::GetOptionsHelpInfo(string& info)
//
//	Help relatif au options
//--
{
info += " ---- PIHisto2D options help info (see also ALT-O): \n" ;
info += "- h2help: get this help text\n";
info += "- h2list: list choosen options\n";
info += "- sta,stat,stats:            activate   statistic display\n";
info += "  nsta,nstat,nostat,nostats: deactivate statistic display\n";
info += "- h2disp=typ[,fracpts]: choose display type\n";
info += "    typ=var: variable size boxes\n";
info += "    typ=hbk: \"a la hbook2\"\n";
info += "    typ=img: image like (use \"h2col\" for color map)\n";
info += "    typ=pts: point clouds (fracpts=max possible fraction\n";
info += "             of used pixels per bin [0,1])\n";
info += "- h2scale=lin/log[,logscale]: choose linear or logarithmic scale\n";
info += "- h2dyn=[hmin][,hmax]: choose histogramme range for display\n";
info += "- use graphic att. to define color table (ex: grey32,midas_heat,...)\n";
info += "            (see general graphicatt description)\n";
info += "- use \"revcmap\" to reverse color table\n";
info += "- h2frac=[fmin][,fmax]: choose sub-range display [0,1]\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;
}

/////////////////////////////////////////////////////////////////
//  Classe H2WinArg
/////////////////////////////////////////////////////////////////
//++
// Class	H2WinArg
// Lib		PIext
// include	pihisto2d.h
//
//	Fenêtre de dialogue pour le choix des options de tracé pour "PIHisto2D"
//	Classe de fenêtre de dialogue permettant de modifier interactivement
//	Les différents attributs de visualisation pour les *PIImage* .
//--
//++
// Links	Parents
// PIWindow
//--
//++
// Links	Voir aussi
// PIHisto2D
// PIH2DWdg
//--

//++
// Titre	Constructeur, méthodes
//--

PIBaseWdgGen* H2WinArg::mBWdg = NULL;
PIHisto2D* H2WinArg::mH2DDrw = NULL;
static H2WinArg* cur_h2winarg = NULL;

void H2WinArg::ShowPIHisto2DTools()
{
  if (cur_h2winarg == NULL)  cur_h2winarg = new H2WinArg(PIApplicationGetApp());
  cur_h2winarg->Show();
}

void H2WinArg::ShowPIHisto2DTools(PIBaseWdgGen* cbw)
{
  if (cur_h2winarg == NULL)  cur_h2winarg = new H2WinArg(PIApplicationGetApp());
  mBWdg = cbw;
  cur_h2winarg->Show();
}

void H2WinArg::HidePIHisto2DTools()
{
  if (cur_h2winarg != NULL)  cur_h2winarg->Hide();
}

void H2WinArg::SetCurrentBaseWdg(PIBaseWdgGen* cbw)
{
  mBWdg = cbw;
}

void H2WinArg::SetCurrentPIHisto2D(PIHisto2D* h2ddrw)
{
  mH2DDrw = h2ddrw;
}

PIBaseWdgGen* H2WinArg::GetCurrentBaseWdg()
{
  return(mBWdg);
}

PIHisto2D* H2WinArg::GetCurrentPIHisto2D()
{
  return(mH2DDrw);
}

//++
H2WinArg::H2WinArg(PIMsgHandler* par)
//
//	Creation de la fenetre de gestion des parametres
//	des dessins des histogrammes 2D. Cette fenetre de
//	dialogue est partagee par tous les widget de dessin
//	des histogrammes 2D. Pour faire apparaitre la fenetre
//      tapez ALT-O.
//--
//++
//| - Menu 1: Choix du type de display
//|     Carres variables, nuages de points, caracteres a la hbook2
//|     et carres de tailles fixe (couleur ou niveauz de gris).
//| - Menu 2: Choix du type d'echelle
//|     Lineaire ou logarithmique
//| - Menu 3: Choix de la couleur
//|     noir et blanc, niveau de gris et couleurs diverses.
//| - Champ texte Dyn: Pour donner la dynamique, si min>=max
//|     alors prend le min et le max de l'histogramme
//|     (cf PIHisto2D::UseDyn)
//| - Champ texte Frac: fraction mini et maxi
//|     (cf PIHisto2D::UseFrac)
//| - Champ texte LogScal: niveau de scaling pour le choix d'une
//|     echelle logarithmique (cf PIHisto2D::UseScale)
//--
//++
//| - Curseur interactif PerPt: pourcentage de points a dessiner
//|     dans chaque bin (cf PIHisto2D::UseDisplay)
//| - Bouton Apply: dessiner avec les options affichees
//| - Bouton Dismiss: fermeture de la fenetre de dialogue.
//| - Bouton Get: re-prendre les valeurs de display stoquees
//|     pour un histogramme donne.
//| - Bouton Print: Imprimer les caracteristiques du display
//|     et de l'histogramme.
//--
: PIWindow((PIMsgHandler *)par,"H2D-Options",PIWK_dialog,250,260,150,150)
, mFgCol(false), mCmap(CMAP_GREYINV32), mRevCmap(false)
, mTypScal(0)  , mLogScale(10.)
, mTypDisp(0)  , mFPoints(0.5)
, mHMin(1.)    , mHMax(-1.)
, mFracMin(0.1), mFracMax(0.9)
{
if(dbg) printf("H2WinArg::H2WinArg %p par=%p\n",this,par);

// Taille automatique
int bsx, bsy;
PIApplicationPrefCompSize(bsx, bsy);  // environ 6 lettres
int spx = (bsx>=10) ? bsx/10 : 1;  // intervalle entre lettres X
int spy = (bsy>=5) ? bsy/5 : 1;    // intervalle entre lettres Y
int wszx = 5*spx+bsx+int(2.5*bsx); // Taille fenetre en X
int wszy = 11*spy+int(8.5*bsy);    // Taille fenetre en Y
SetSize(wszx, wszy);

// menu du style de display des bins
  int cpx = 2*spx, cpy = 2*spy;
mOPop[0] = new PIOptMenu(this,"optmen-h2d-1",2*bsx,bsy,cpx,cpy);
mOPop[0]->AppendItem("Carres Var."  ,6101);
mOPop[0]->AppendItem("....."        ,6102);
mOPop[0]->AppendItem(".+12..Z*"     ,6103);
mOPop[0]->AppendItem("Carres Pleins",6104);
mOPop[0]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);

// Menu du choix de la dynamique
  cpy += bsy+spy;
mOPop[1] = new PIOptMenu(this,"optmen-h2d-2",2*bsx,bsy,cpx,cpy);
mOPop[1]->AppendItem("Lineaire",6201);
mOPop[1]->AppendItem("Log10"   ,6202);
mOPop[1]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);

// Menu du choix de la table des couleurs
  cpy += bsy+spy;
mOPop[2] = new PIOptMenu(this,"optmen-h2d-3",2*bsx,bsy,cpx,cpy);
mOPop[2]->AppendItem("Black&White",7000);
mCasc[0] = new PIMenu(mOPop[2]->Menu(),"PIStd-128Col");
mCasc[1] = new PIMenu(mOPop[2]->Menu(), "PIUniCol32");
mCasc[2] = new PIMenu(mOPop[2]->Menu(),"MIDAS-CMap");
int kcc,nsct1=5,nsct2=9,nsct3=17,nsct4=PIColorMap::NumberStandardColorMaps()-2;
for(kcc=0; kcc<nsct1; kcc++) 
  mOPop[2]->AppendItem(PIColorMap::GetStandardColorMapName(kcc).c_str(),7001+kcc);
for(kcc=nsct1; kcc<nsct2; kcc++) 
  mCasc[0]->AppendItem(PIColorMap::GetStandardColorMapName(kcc).c_str(),7001+kcc);
mOPop[2]->AppendPDMenu(mCasc[0]);
for(kcc=nsct2; kcc<nsct3; kcc++) 
  mCasc[1]->AppendItem(PIColorMap::GetStandardColorMapName(kcc).c_str(),7001+kcc);
mOPop[2]->AppendPDMenu(mCasc[1]);
for(kcc=nsct3; kcc<nsct4; kcc++) 
  mCasc[2]->AppendItem(PIColorMap::GetStandardColorMapName(kcc).c_str(),7001+kcc);
mOPop[2]->AppendPDMenu(mCasc[2]);
for(kcc=nsct4; kcc<PIColorMap::NumberStandardColorMaps(); kcc++) 
  mOPop[2]->AppendItem(PIColorMap::GetStandardColorMapName(kcc).c_str(),7001+kcc);
//mOPop[2]->SetValue(7000);
mOPop[2]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);

// Reverse color map
  cpy += bsy+spy;
mCkb = new PICheckBox(this,"Reverse CMap",8001,2*bsx,bsy,cpx,cpy);
mCkb->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);

// Labels et zones de saisie texte
  cpy += bsy+spy;
mLab[0] = new PILabel(this,"     Dyn: ",bsx,bsy,cpx,cpy); 
mLab[0]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
mText[0] = new PIText(this,"Dynamique",int(2.5*bsx),bsy,cpx+bsx+spx,cpy);
mText[0]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
  cpy += bsy+spy;
mLab[1] = new PILabel(this,"    Frac: ",bsx,bsy,cpx,cpy);
mLab[1]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
mText[1] = new PIText(this,"Fraction",int(2.5*bsx),bsy,cpx+bsx+spx,cpy);
mText[1]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
  cpy += bsy+spy;
mLab[2] = new PILabel(this," LogScal: ",bsx,bsy,cpx,cpy);
mLab[2]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
mText[2] = new PIText(this,"LogScale",int(2.5*bsx),bsy,cpx+bsx+spx,cpy);
mText[2]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);

// Labels et curseur mobile
cpy += bsy+spy;
mLab[3] = new PILabel(this,"   PerPt: ",bsx,bsy,cpx,cpy+int(0.25*bsy));
mLab[3]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
mPScal = new PIScale(this,"FracPoints",6401,kSDirLtoR
                        ,int(2.5*bsx),int(1.25*bsy),cpx+bsx+spx,cpy);
mPScal->SetMinMax(0,100);
mPScal->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);

//
SetText();
//

// Boutons
  cpx = 2*bsx+5*spx, cpy = 2*spy;
mBut[0] = new PIButton(this,"Apply",6001,bsx,bsy,cpx,cpy);
mBut[0]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
  cpy += bsy+spy;
mBut[1] = new PIButton(this,"Dismiss",6002,bsx,bsy,cpx,cpy);
mBut[1]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
  cpy += bsy+spy;
mBut[2] = new PIButton(this,"Get",6003,bsx,bsy,cpx,cpy);
mBut[2]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
  cpy += bsy+spy;
mBut[3] = new PIButton(this,"Print",6004,bsx,bsy,cpx,cpy);
mBut[3]->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
// FinishCreate();
}

//++
H2WinArg::~H2WinArg()
//
//	Destructeur.
//--
{
int i;
if(dbg) printf("H2WinArg::~H2WinArg %p\n",this);
for(i=0;i<3;i++) delete mCasc[i];
for(i=0;i<3;i++) delete mOPop[i];
for(i=0;i<4;i++) delete mBut[i];
for(i=0;i<4;i++) delete mLab[i];
for(i=0;i<3;i++) delete mText[i];
delete mCkb;
delete mPScal;
}

//++
void H2WinArg::Show()
//
//	Initialisation sur ouverture ALT-O
//--
{
 if(dbg) printf("H2WinArg::Show() mH2DDrw=%p\n",mH2DDrw);
 // Pour recuperer les valeurs du Drawer sur lequel on fait ALT-O
 PIWindow::Show();
 return;
}

//++
void H2WinArg::SetText()
//
//	Gestion des fenetres de saisie de texte et des pop-menus.
//--
{
if(dbg) printf("H2WinArg::SetText()\n");
string sdum;
char str[256];

sprintf(str,"%g %g",mHMin,mHMax);
mText[0]->SetText(str);

sprintf(str,"%g %g",mFracMin,mFracMax);
mText[1]->SetText(str);

sprintf(str,"%g",mLogScale);
mText[2]->SetText(str);
if(mTypScal==1) mText[2]->SetSensitive();
  else          mText[2]->SetUnSensitive();

if(mTypDisp==0)      {sdum="Carres Var.";   mOPop[0]->SetValueStr(sdum);}
else if(mTypDisp==1) {sdum=".....";         mOPop[0]->SetValueStr(sdum);}
else if(mTypDisp==2) {sdum=".+12..Z*";      mOPop[0]->SetValueStr(sdum);}
else if(mTypDisp==3) {sdum="Carres Pleins"; mOPop[0]->SetValueStr(sdum);}

if(mTypScal==0)      {sdum="Lineaire"; mOPop[1]->SetValueStr(sdum);}
else if(mTypScal==1) {sdum="Log10";    mOPop[1]->SetValueStr(sdum);}

if(!mFgCol) {mOPop[2]->SetValue(7000);}
else {
  for(int kk=0;kk<PIColorMap::NumberStandardColorMaps();kk++)
    if(mCmap == PIColorMap::GetStandardColorMapId(kk))
      {mOPop[2]->SetValue(7001+kk); break;}
}
mCkb->SetState(mRevCmap);

mPScal->SetValue(int(mFPoints*100.));
if(mTypDisp==1) mPScal->SetSensitive();
  else          mPScal->SetUnSensitive();
}

//++
void H2WinArg::Process(PIMessage msg, PIMsgHandler* sender, void*)
//
//	Gestions des messages.
//--
{
if(dbg) printf("H2WinArg::Process(%d-%d , %p ...) \n"
              ,(int)UserMsg(msg),(int)ModMsg(msg),sender);

// if(!mH2Wdg) return;
if(!mBWdg) return;
// PIHisto2D* mpih = mH2Wdg->GetPIHisto();
PIHisto2D* mpih = mH2DDrw;
if(!mpih) return;

int opt = UserMsg(msg);

     if(opt == 6101) {mTypDisp = 0;}
else if(opt == 6102) {mTypDisp = 1;}
else if(opt == 6103) {mTypDisp = 2;}
else if(opt == 6104) {mTypDisp = 3;}

else if(opt == 6201) {mTypScal = 0;}
else if(opt == 6202) {mTypScal = 1;}

else if(opt == 7000) {mFgCol = false;}
else if(opt >= 7001 && opt <8000) {
  int k  = opt-7001;
  mFgCol = true;
  mCmap  = PIColorMap::GetStandardColorMapId(k);
}

else if(opt == 8001) mRevCmap = mCkb->GetState();

else if(opt == 6401) mFPoints = mPScal->GetValue()/100.;

else if(opt==6001) {
  sscanf(mText[0]->GetText().c_str(),"%lf %lf",&mHMin,&mHMax);
  sscanf(mText[1]->GetText().c_str(),"%lf %lf",&mFracMin,&mFracMax);
  sscanf(mText[2]->GetText().c_str(),"%lf",&mLogScale);
  mpih->UseColors(mFgCol,mCmap,mRevCmap);
  mpih->UseScale(mTypScal,mLogScale);
  mpih->UseDisplay(mTypDisp,mFPoints);
  mpih->UseDyn(mHMin,mHMax);
  mpih->UseFrac(mFracMin,mFracMax);
  mBWdg->Refresh();  // On rafraichit le dessin (tout le PIScDrawWdg)
}
else if(opt==6002) this->Hide();
else if (opt==6003) {
  mFgCol = mpih->Color(); mCmap = mpih->ColMap();
                          mRevCmap = mpih->IsColMapRev();
  mTypScal = mpih->TypScale(); mLogScale = mpih->LogScale();
  mTypDisp = mpih->TypDisplay(); mFPoints = mpih->FPoints();
  mHMin = mpih->HMin(); mHMax = mpih->HMax();
  mFracMin = mpih->FMin(); mFracMax = mpih->FMax();
}
else if(opt==6004) mpih->Print(2);

SetText();

if(dbg) {
  printf("H2WinArg::Process opt=%d col=%d,%d,%d scal=%d disp=%d npt=%g\n"
        ,opt,(int)mFgCol,(int)mCmap,(int)mRevCmap,mTypScal,mTypDisp,mFPoints);
  printf("                  min,max= %g,%g frac= %g,%g logsc= %g\n"
        ,mHMin,mHMax,mFracMin,mFracMax,mLogScale);
}

}
