// Module PI : Peida Interactive     PIDrawer
// Classe de traceurs pouvat être attachés à une PIBaseWdg
//	      E. Aubourg , R.Ansari     96-98
// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA

#include "pidrawer.h"
#include <math.h>

//++
// Class	PIDrawer
// Lib		PI
// include	pidrawer.h
//
//	Classe pour la création d'objets, capables de se dessiner
//	dans une composante graphique ("PIBaseWdg").
//--
//++
// Links	Voir
// PIBaseWdg
//--

//++
// Titre	Constructeurs et méthodes
//--

//++
// PIDawer()
//	Constructeur.
//--

PIDrawer::PIDrawer()
{
  mBWdg = NULL; 
  mGrUC = NULL;
  xW0 = yW0 = 0; xWd = yWd = 100; 
  aXdir = false;  // Vrai si Axe X de Droite vers Gauche 
  aYdir = false;  // Vrai si Axe Y de Haut vers Bas
  SetLimits(-1, 1, -1, 1, kAxeDirLtoR, kAxeDirDownUp);
  limitsFixed = 0;
  SetAxesFlags();   // Pas de trace d'axes par defaut 
  mDndfg = false;  // Pour controle de l'appel de Detach() si delete

  mFCol = mBCol = PI_NotDefColor;
  mLAtt = PI_NotDefLineAtt;
  mFSz = PI_NotDefFontSize;
  mFAtt = PI_NotDefFontAtt;
  mMSz = -1;
  mMrk = PI_NotDefMarker;
  mCmapid = CMAP_OTHER;
}

PIDrawer::~PIDrawer()
{
  list<DrwBWId>::iterator it;
  mDndfg = true;
  for(it = mBWdgList.begin(); it != mBWdgList.end(); it++)
         (*it).wdg->RemoveDrawer((*it).id);
  if (mGrUC)  delete mGrUC;
}

//++
// Titre	Les axes
//--
//++
// void  SetLimits(double xmin, double xmax, double ymin, double ymax, -
//                 int axrl=kAxeDirSame, int ayud=kAxeDirSame)
//	Définit les limites du système de coordonnées.
//|	kAxeDirSame , kAxeDirAuto
//|	kAxeDirLtoR , kAxeDirRtoL  (Axe X)
//|	kAxeDirDownUp , kAxeDirUpDown (Axe Y)	
// void  GetAxesConfig(int& xa, int& ya) 
//	Renvoie la configuration des axes.
// double XMin()  XMax()
//	Limites de l'axe X
// double YMin()  YMax()
//	Limites de l'axe Y
// void  SetAxesFlags(unsigned int flags=kAxesNone)
//	Attributs de tracé d'axes
//|	kStdAxes , kBoxAxes , kTicks , kIntTicks , kExtTicks 
//|	kMajTicks , kMinTicks , kLabels , kGridOn
//|	kAxesDflt , kAxesNone
// unsigned int GetAxesFlags()
//	Renvoie les attributs de tracé d'axes  
// void  DrawAxes(PIGraphicUC* g)
//	Méthode de tracé des axes
//--

void
PIDrawer::SetLimits(double xmin, double xmax, double ymin, double ymax,
                    int axrl, int ayud)
{
  if (axrl == kAxeDirAuto) {
    double ff;
    if (xmax < xmin)  { axrl = kAxeDirRtoL;   ff=xmin;  xmin=xmax;  xmax=ff; }
    else  axrl = kAxeDirLtoR;
  }
  if (ayud == kAxeDirAuto) {
    double ff;
    if (ymax < ymin)  { ayud = kAxeDirUpDown;   ff=ymin;  ymin=ymax;  ymax=ff; }
    else  ayud = kAxeDirDownUp;
  }
  if (xmax <= xmin || ymax <= ymin)
    return; // $CHECK$ exception ?
  if (axrl == kAxeDirLtoR)  aXdir = false;
  else if (axrl == kAxeDirRtoL)  aXdir = true;
  if (ayud == kAxeDirDownUp)  aYdir = false;
  else if (ayud == kAxeDirUpDown)  aYdir = true;
  xMin = xmin;
  xMax = xmax;
  yMin = ymin;
  yMax = ymax;
  aXFlg = axrl;  aYFlg = ayud;
  limitsFixed = 1;
}

void
PIDrawer::GetAxesConfig(int& xa, int& ya)
{
xa = (aXdir) ? kAxeDirRtoL : kAxeDirLtoR;
ya = (aYdir) ? kAxeDirUpDown : kAxeDirDownUp;
}

//++
//  void  Draw(PIGraphicUC* g, double xmin, double ymin, double xmax, double ymax)
//	Méthode qui est appelé lorsquer l'objet PIDrawer doit rafraichir la zone
//	définie par "xmin-xmax" , "ymin-ymax" dans le système de coordonnées du
//	PIDrawer.
//  void  Refresh()
//	Rafraîchit le dessin du PIDrawer sur tous les PIBaseWdg auxquel il est attaché.
//  void  UpdateLimits()
//	Cette méthode doit calculer les limites (X/Y Min-Max) préferées par l'objet
//	et doit appeler "SetLimits()". L'implementation par défaut ne fait rien.
//--

void
PIDrawer::UpdateLimits()
{
// Ne fait rien !
  return;
}

void
PIDrawer::Refresh()
{
  list<DrwBWId>::iterator it;
  for(it = mBWdgList.begin(); it != mBWdgList.end(); it++)
         (*it).wdg->CallDrawer((*it).id);

}

//++
// Titre	Les attributs graphiques
//--
//++
// void  SetColAtt(PIColors fg=PI_NotDefColor, PIColors bg=PI_NotDefColor)
//	Modifie la couleur d'avant-plan et de fond par défaut
// void  SetLineAtt(PILineAtt lat=PI_NotDefLineAtt)
//	Modifie l'attribut type de ligne
// void  SetFontAtt(PIFontSize fsz=PI_NotDefFontSize, PIFontAtt fat=PI_NotDefFontAtt)
//	L'attribut Type de fonte 
// void  SetMarkerAtt(int sz=-1, PIMarker mrk=PI_NotDefMarker)
//	L'attribut type et taille de marker
// void  SetColMapId(CMapId cid=CMAP_OTHER)
//	L'attribut choix de la table de couleurs
// void  SelGraAtt(PIGraphicUC* g)
//	Modifie les attributs graphiques de "g" à partir des attributs courant
//	de l'objet "PIDrawer"
// 
// PIColors  GetFgColAtt()
//	Renvoie l'attribut de couleur d'avant-plan
// PIColors  GetBgColAtt()
//	Renvoie l'attribut de couleur d'arrière-plan
// PILineAtt  GetLineAtt()
//	Renvoie l'attribut de ligne
// PIFontSize GetFontSz()
//	Renvoie l'attribut de taille de fonte
// PIFontAtt  GetFontAtt()
//	Renvoie l'attribut de type de fonte
// int  GetMarkerSz()
//	Renvoie l'attribut de taille de marker
// PIMarker GetMarker()
//	Renvoie l'attribut de type de marker
// CMapId GetColMapId()
//	Renvoie l'attribut de type de table de couleur
//--

void 
PIDrawer::SetColAtt(PIColors fg, PIColors bg)
{
  mFCol = fg;
  mBCol = bg;
}

void 
PIDrawer::SetLineAtt(PILineAtt lat)
{
  mLAtt = lat;
}

void 
PIDrawer::SetFontAtt(PIFontSize fsz, PIFontAtt fat)
{
  mFSz = fsz;
  mFAtt = fat;
}

void 
PIDrawer::SetMarkerAtt(int sz, PIMarker mrk)
{
  mMSz = sz;
  mMrk = mrk;
}

void 
PIDrawer::SetColMapId(CMapId cid)
{
  mCmapid = cid;
}

void 
PIDrawer::SelGraAtt(PIGraphicUC* g)
{
  if (mFCol != PI_NotDefColor)    g->SelForeground(mFCol);
  if (mBCol != PI_NotDefColor)    g->SelBackground(mBCol);
  if (mLAtt != PI_NotDefLineAtt)  g->SelLine(mLAtt);
  if ( (mFSz != PI_NotDefFontSize) || (mFAtt != PI_NotDefFontAtt) )
                                  g->SelFont(mFSz, mFAtt);
  if ( (mMrk != PI_NotDefMarker) || (mMSz >= 0) )
                                  g->SelMarker(mMSz, mMrk);
}


void
PIDrawer::Attach(PIBaseWdgGen* wdg, int id)
{
  if (!wdg) return;
  DrwBWId bwi;
  bwi.id = id;  bwi.wdg = wdg;
  mBWdgList.push_back(bwi);
  return;
}

void
PIDrawer::Detach(PIBaseWdgGen* wdg, int id)
{
  if (mDndfg) return;
  if (!wdg) return;
  list<DrwBWId>::iterator it;
  for(it = mBWdgList.begin(); it != mBWdgList.end(); it++)
     if ( ((*it).id == id) && ((*it).wdg == wdg) ) { mBWdgList.erase(it);  break; }
  return;
}


PIGraphicUC*
PIDrawer::SetDrwWdg(PIBaseWdgGen* drw, int x0, int y0, int dx, int dy, PIGraphicGen* g)
{
  mBWdg = drw;
  xW0 = x0;   yW0 = y0;
  xWd = dx;   yWd = dy;
  if (mGrUC)  delete mGrUC;
  mGrUC = new PIGraphicUC(g, x0, y0, dx, dy);
  mGrUC->SetUCS(xMin, xMax, yMin, yMax, aXFlg, aYFlg);
  return(mGrUC);
}

/* --Methode-Static-- */
void PIDrawer::BestTicks(double rmin,double rmax,int nticks,double& majt)
{
double d = rmax - rmin;
double ld = log10(d);
double fld = floor(fabs(ld));
double del,del0;
double fac[4] = {2.,5.,10.,20.};
if(ld>=0.) { fld -= 1.; del0 = del = pow(10.,fld); }
else { fld += 2.; del0 = del = pow(10., -fld); }
int k=0;
while( d/del > (double) nticks && k<4 ) { del = fac[k]*del0; k++; }
majt = del;
}

void
PIDrawer::CalcTicks()
{
  BestTicks(xMin,xMax,7,xMajTickStep);
  xMinTickStep = xMajTickStep/5;
  xFirstMajTick = int(xMin / xMajTickStep) * xMajTickStep;
  if (xFirstMajTick < xMin) xFirstMajTick += xMajTickStep;
  xFirstMinTick = int(xMin / xMinTickStep) * xMinTickStep;
  if (xFirstMinTick < xMin) xFirstMinTick += xMinTickStep;

  BestTicks(yMin,yMax,12,yMajTickStep);
  yMinTickStep = yMajTickStep/5;
  yFirstMajTick = int(yMin / yMajTickStep) * yMajTickStep;
  if (yFirstMajTick < yMin) yFirstMajTick += yMajTickStep;
  yFirstMinTick = int(yMin / yMinTickStep) * yMinTickStep;
  if (yFirstMinTick < yMin) yFirstMinTick += yMinTickStep;

  yMajTickLen = (xMax-xMin)/100;
  yMinTickLen = (xMax-xMin)/250;
  xMajTickLen = (yMax-yMin)/100;
  xMinTickLen = (yMax-yMin)/250;
}

void
PIDrawer::SetAxesFlags(unsigned int flags)
{
  axesFlags = flags;
  if (axesFlags & (kIntTicks | kExtTicks | kMajTicks | kMinTicks))
    axesFlags |= kTicks;
  if ((axesFlags & (kTicks | kIntTicks | kExtTicks)) == kTicks)
    axesFlags |= kIntTicks | kExtTicks;
  if ((axesFlags & (kTicks | kMajTicks | kMinTicks)) == kTicks)
    axesFlags |= kMajTicks;
}


void
PIDrawer::DrawAxes(PIGraphicUC* g)
{
  g->NoClip();
  if (mLAtt == PI_NotDefLineAtt)  g->SelLine(PI_ThinLine);
  else g->SelLine(mLAtt);
//  On calcule les ticks 
  CalcTicks();

  unsigned int flags = axesFlags;

  if (flags & kStdAxes) {

    // Les axes
    
    g->DrawLine(xMin, 0, xMax, 0);
    g->DrawLine(0, yMin, 0, yMax);

    // La grille en pointilles

    if (flags & kGridOn) DrawGrid(g);
  
    // Les ticks majeurs
  
    if (flags & kMajTicks) {
        DrawHTicks(g, 0, xMajTickLen, xMajTickLen, xFirstMajTick, xMajTickStep);
        DrawVTicks(g, 0, yMajTickLen, yMajTickLen, yFirstMajTick, yMajTickStep);
    }
    
    // Les ticks mineurs
  
    if (flags & kMinTicks) {
        DrawHTicks(g, 0, xMinTickLen, xMinTickLen, xFirstMinTick, xMinTickStep);
        DrawVTicks(g, 0, yMinTickLen, yMinTickLen, yFirstMinTick, yMinTickStep);
    }
    
    // Les labels
    
    if (flags & kLabels) {
      if (!aYdir)
        DrawHLabels(g, -xMajTickLen*8, xFirstMajTick, xMajTickStep,0);
      else 
        DrawHLabels(g, xMajTickLen*8, xFirstMajTick, xMajTickStep,0);
      if (!aXdir)
        DrawVLabels(g, -yMajTickLen*2, yFirstMajTick, yMajTickStep,1);
      else 
        DrawVLabels(g, yMajTickLen*2, yFirstMajTick, yMajTickStep,1);
    }
    
  }
  
  if (flags & kBoxAxes) {
  
    // La boite
    
    g->DrawLine(xMin, yMin, xMax, yMin);
    g->DrawLine(xMax, yMin, xMax, yMax);
    g->DrawLine(xMax, yMax, xMin, yMax);
    g->DrawLine(xMin, yMax, xMin, yMin);

    // Longueur des ticks
    
    double extXMajTickLen = flags&kExtTicks ? xMajTickLen : 0;
    double intXMajTickLen = flags&kIntTicks ? xMajTickLen : 0;
    double extXMinTickLen = flags&kExtTicks ? xMinTickLen : 0;
    double intXMinTickLen = flags&kIntTicks ? xMinTickLen : 0;
    double extYMajTickLen = flags&kExtTicks ? yMajTickLen : 0;
    double intYMajTickLen = flags&kIntTicks ? yMajTickLen : 0;
    double extYMinTickLen = flags&kExtTicks ? yMinTickLen : 0;
    double intYMinTickLen = flags&kIntTicks ? yMinTickLen : 0;

    // La grille en pointilles

    if (flags & kGridOn) DrawGrid(g);

    // Les ticks majeurs
  
    if (flags & kMajTicks) {
        DrawHTicks(g, yMin, intXMajTickLen, extXMajTickLen, xFirstMajTick, xMajTickStep);
        DrawHTicks(g, yMax, extXMajTickLen, intXMajTickLen, xFirstMajTick, xMajTickStep);
        DrawVTicks(g, xMin, extYMajTickLen, intYMajTickLen, yFirstMajTick, yMajTickStep);
        DrawVTicks(g, xMax, intYMajTickLen, extYMajTickLen, yFirstMajTick, yMajTickStep);
    }
    
    // Les ticks mineurs
  
    if (flags & kMinTicks) {
        DrawHTicks(g, yMin, intXMinTickLen, extXMinTickLen, xFirstMinTick, xMinTickStep);
        DrawHTicks(g, yMax, extXMinTickLen, intXMinTickLen, xFirstMinTick, xMinTickStep);
        DrawVTicks(g, xMin, extYMinTickLen, intYMinTickLen, yFirstMinTick, yMinTickStep);
        DrawVTicks(g, xMax, intYMinTickLen, extYMinTickLen, yFirstMinTick, yMinTickStep);
    }


    // Les labels
    
    if (flags & kLabels) {
      if (!aYdir) {
        DrawHLabels(g, yMin-xMajTickLen*8, xFirstMajTick, xMajTickStep,0);
        DrawHLabels(g, yMax+xMajTickLen*2, xFirstMajTick, xMajTickStep,0);
      }
      else {
        DrawHLabels(g, yMin-xMajTickLen*2, xFirstMajTick, xMajTickStep,0);
        DrawHLabels(g, yMax+xMajTickLen*8, xFirstMajTick, xMajTickStep,0);
      }
      if (!aXdir) {
        DrawVLabels(g, xMin-yMajTickLen*2, yFirstMajTick, yMajTickStep,1);
        DrawVLabels(g, xMax+yMajTickLen*2, yFirstMajTick, yMajTickStep,-1);
      }
      else {
        DrawVLabels(g, xMin-yMajTickLen*2, yFirstMajTick, yMajTickStep,-1);
        DrawVLabels(g, xMax+yMajTickLen*2, yFirstMajTick, yMajTickStep,1);
      }
    }
  }
  g->Clip();
}

  

void
PIDrawer::Draw(PIGraphicUC* g, double /*xmin*/, double /*ymin*/, double /*xmax*/, double /*ymax*/)
{
}


void
PIDrawer::DrawHTicks(PIGraphicUC* g, double y, double tickUp, double tickDown, double xBeg, double xStep)
{
  for (double x=xBeg; x<=xMax; x += xStep)
    g->DrawLine(x, y-tickDown, x, y+tickUp);
}

void
PIDrawer::DrawVTicks(PIGraphicUC* g, double x, double tickLeft, double tickRight, double yBeg, double yStep)
{
  for (double y=yBeg; y<=yMax; y += yStep)
    g->DrawLine(x-tickLeft, y, x+tickRight, y);
}

void
PIDrawer::DrawHLabels(PIGraphicUC* g, double y, double xBeg, double xStep, int just)
{
  double xOffset = 0;
  int kk;
  g->SelFontSz(xMajTickLen*4.);
  for (double x=xBeg; x<=xMax; x += xStep) {
      char label[20]; sprintf(label, "%-6g", x);
      for(kk=0; kk<20; kk++)
        if (label[kk] == ' ')   { label[kk] = '\0' ; break; }
      double largeur = g->CalcStringWidth(label);
      if (just == 1)
        xOffset = -largeur;
      else if (just == 0)
        xOffset = -largeur/2;
      else
        xOffset = 0;
      if (aXdir)  xOffset = -xOffset;
      g->DrawString(x+xOffset, y, label);
  }
}

void
PIDrawer::DrawVLabels(PIGraphicUC* g, double x, double yBeg, double yStep, int just)
{
  double xOffset = 0;
  g->SelFontSz(xMajTickLen*4.);
  for (double y=yBeg; y<=yMax; y += yStep) {
      char label[20]; sprintf(label, "%-6g", y);
      double largeur = g->CalcStringWidth(label);
      if (just == 1)
        xOffset = -largeur;
      else if (just == 0)
        xOffset = -largeur/2;
      else
        xOffset = 0;
      if (aXdir)  xOffset = -xOffset;
      g->DrawString(x+xOffset, y, label);
  }
}

void
PIDrawer::DrawGrid(PIGraphicUC* g)
{
  PILineAtt savlineatt = g->GetLineAtt();
//  g->SelLine(PI_ThinDashedLine);
  g->SelLine(PI_ThinDottedLine);

  for (double x=xFirstMajTick; x<=xMax; x += xMajTickStep)
    g->DrawLine(x, yMin, x, yMax);

  for (double y=yFirstMajTick; y<=yMax; y += yMajTickStep)
    g->DrawLine(xMin, y, xMax, y);

  g->SelLine(savlineatt);
}


/*  --------------------------------------------------------------  */
/*  ------------------- Classe PIElDrawer ------------------------  */
/*  --------------------------------------------------------------  */

/* --Methode-- */
PIElDrawer::PIElDrawer()
{
mEn = 0;
titleT = titleB = "";
}

/* --Methode-- */
PIElDrawer::~PIElDrawer()
{
}

/* --Methode-- */
void PIElDrawer::SetTitles(const char* tt, const char* tb)
{
if (tt != NULL)  titleT = tt;
if (tb != NULL)  titleB = tb;
}

/* --Methode-- */
void PIElDrawer::SetTitles(string const & tt, string const & tb)
{
titleT = tt;  titleB = tb;
}

/* --Methode-- */
void  PIElDrawer::Draw(PIGraphicUC* g, double /*xmin*/, double /*ymin*/, double /*xmax*/, double /*ymax*/)
{
DrawAxes(g);   // Trace des axes 

SelGraAtt(g);   // DrawAxes change certains attributs graphiques
if (mFSz == PI_NotDefFontSize) g->SelFont(PI_NormalSizeFont);
// Trace des titres 
if ( (titleT.length() > 0) || (titleB.length() > 0) ) {
  aXdir = false;  // Vrai si Axe X de Droite vers Gauche 
  aYdir = false;  // Vrai si Axe Y de Haut vers Bas
  PIGrCoord gas, gds;
  double fx, fy, fh;
//  fh = g->GetFontHeightUC(gas, gds);
  fh = 0.075*(YMax()-YMin());
  if ( titleT.length() > 0) {
      fx = g->CalcStringWidth(titleT.c_str());
      if(aXdir) fx = XMax()-(XMax()-XMin()-fx)/2.;
      else fx = XMin()+(XMax()-XMin()-fx)/2.;
      if (aYdir) fy = YMin()-fh;
      else fy = YMax()+fh;
      g->DrawString(fx, fy, titleT.c_str());
//      printf(" PIElDrawer::Draw()/DBG (%g , %g) %s  - %g \n", fx, fy, titleT.c_str(), fh);
    }
  if ( titleB.length() > 0) {
      fx = g->CalcStringWidth(titleB.c_str());
      if(aXdir) fx = XMax()-(XMax()-XMin()-fx)/2.;
      else fx = XMin()+(XMax()-XMin()-fx)/2.;
      if (aYdir) fy =  YMax()+fh*1.5;
      else fy = YMin()-fh*1.5;
      g->DrawString(fx, fy, titleB.c_str());
//      printf(" PIElDrawer::Draw()/DBG (%g , %g) %s  - %g \n", fx, fy, titleT.c_str(), fh);
    }
  }
DrwElList::iterator it;
PIColors cc = g->GetForeground();
for (it = mElist.begin(); it != mElist.end(); it++)
  {
  if ((*it).col != PI_NotDefColor)  g->SelForeground((*it).col);
  switch ( (*it).etyp )
    {
    case PIDEL_Line :
      g->DrawLine((*it).ex, (*it).ey, (*it).edx, (*it).edy ); 
      break;
    case PIDEL_Text :
      g->DrawString((*it).ex, (*it).ey, (char*) ((*it).es.c_str()) ); 
      break;
    case PIDEL_Rect :
      g->DrawBox((*it).ex, (*it).ey, (*it).edx, (*it).edy ); 
      break;
    case PIDEL_FRect :
      g->DrawFBox((*it).ex, (*it).ey, (*it).edx, (*it).edy ); 
      break;
    case PIDEL_Circ :
      g->DrawCircle((*it).ex, (*it).ey, (*it).edx ); 
      break;
    case PIDEL_FCirc :
      g->DrawFCircle((*it).ex, (*it).ey, (*it).edx ); 
      break;
    default :
      break;
    }
  g->SelForeground(cc);
  }
}


/* --Methode-- */
int PIElDrawer::ElAdd(int typ, PIGrCoord x, PIGrCoord  y, PIGrCoord dx, PIGrCoord dy, const char* s, PIColors c)
{
DrwEl dre;
mEn++;
dre.eid = mEn;  dre.etyp = typ;
dre.ex = x;   dre.ey = y;
dre.edx = dx;  dre.edy = dy;
dre.col = c;
if (s)  dre.es = s;
mElist.push_back(dre);
return(mEn);
}

/* --Methode-- */
void  PIElDrawer::ElDel(int id)
{
DrwElList::iterator it;
for (it = mElist.begin(); it != mElist.end(); it++)
  if ( (*it).eid == id) { mElist.erase(it);   break; }
return;
}

/* --Methode-- */
void  PIElDrawer::ElDelAll()
{
mElist.erase(mElist.begin(), mElist.end());
return;
}

