// Module PI : Peida Interactive     PIScDrawWdg
// Gestionnaire Drawer-2D  E.Aubourg, R. Ansari  96-98
// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA

#include "piscdrawwdg.h"
#include "pidrwtools.h"
#include "parradapter.h"

//++
// Class	PIScDrawWdg
// Lib		PI
// include	piscdrawwdg.h
//
//   	Classe pour la l'affichage et la manipulation d'un ensemble
//	de *Drawer* avec la gestion de zoom (agrandissement).
//	* Bouton-1 : Envoie le message "PIMsg_Active" et affiche
//	les coordonnées courantes (User-Coordinates)
//	* Bouton-2 : Permet de définir un rectangle pour le zoom.
//
//	Gestion du clavier :
//	* <Alt>O : Active/Affiche les  contrôles de paramètres spécifiques 
//	(Par défaut, action identique à <Alt>G)
//	* <Alt>G : Affichage de la fenêtre de changement des attributs graphiques
//	* <Alt>R : Rafraîchit l'affichage.
//	* <Alt>M : Active/Désactive l'affichage d'une réticule de mesure (Bouton-1)
//	* <Alt>V : Copier/*Coller* Ajout du texte du buffer copier/coller
//	sur l'objet à la position courante.
//	* <Alt>Z : Supprime les textes et signes ajoutés au dessin.
//--
//++
// Links	Parents
// PIBaseWdg
//--
//++
// Links	Voir aussi
// PIDrawer
//--

static inline void dbl_swap(double& a, double& b) { double tmp = a; a = b; b = tmp;  } 

//++
// Titre	Constructeurs, méthodes
//--
//++
//  PIScDrawWdg(PIContainerGen *par, char *nom, int sx=100, int sy=100, int px=0, int py=0)
//	Constructeur. Contient uniquement le traceur de base, responsable du tracé
//	des axes et des éléments ajoutés (textes, ...)
// long  kind()
//	Renvoie le type ("=PIScDrawWdg::ClassId")
// void SetLimits(double xmin, double xmax, double ymin, double ymax, -
//           int axrl=kAxeDirSame, int ayud=kAxeDirSame, bool tmp=false)
//	Définit les limites de coordonnées.
// void	 UpdateLimits()
//	Recalcule les limites de coordonnées à partir du premier Drawer.
// void  SetAxesFlags(unsigned int flags=kAxesDflt)
//	Définit les attributs des axes.
// int  AddScDrawer(PIDrawer*, bool ad=false)
//	Ajoute un Drawer.
//--


PIScDrawWdg::PIScDrawWdg(PIContainerGen *par, char *nom,
                         int sx, int sy, int px, int py)
:PIBaseWdg(par, nom, sx, sy, px, py)
{
  mFgReticule = false;
  mCPosX = mCPosY = 0;
  mPPos[0] = mPPos[1] = 0.;

  mBDrw = new PIElDrawer;
  SetLimits(-1.,1.,-1.,1.,kAxeDirLtoR,kAxeDirDownUp);
  SetAxesFlags(kBoxAxes | kExtTicks | kLabels);  
  FreeLimits();
  xEndDrag = yEndDrag = xBegDrag = yBegDrag = 0;
  SetDefaultDrawRectangle(0.12, 0.12, 0.88, 0.88, true);
//  SetDefaultDrawRectangle(0.12, 0.08, 0.88, 0.92, true);
  AddDrawer(mBDrw, true, false, false);
  
  mTrlb = mTxw = NULL;
  mTrW = NULL;
  SetTextWin(NULL);

  ActivateKeyboard();
  ActivateButton(1);    // Pour pouvoir activer la fenetre et coordonnees
  ActivateMove(1);      //    "         "         "        "
  ActivateButton(2);
  ActivateMove(2);

}

PIScDrawWdg::~PIScDrawWdg()
{
  delete mBDrw;
  if (mTrW)  delete mTrW;
}

void 
PIScDrawWdg::Keyboard(int key, PIKeyModifier kmod)
{
if (kmod == PIKM_Alt) {
  if      (key == 'R' || key == 'r')  Refresh(); // rafraichir l'affichage
  else if (key == 'M' || key == 'm') mFgReticule = !mFgReticule; // reticule de mesure 
  else if (key == 'V' || key == 'v') RequestSelection();  // Pour coller (copier/coller)  
  else if (key == 'Z' || key == 'z') { mBDrw->ElDelAll();  Refresh(); } // Pour supprimer tous les signes 
  else if (key == 'O' || key == 'o') ActivateSpecializedControls();
  else if (key == 'G' || key == 'g') { 
    PIDrwTools::SetCurrentBaseWdg(this);
    PIDrwTools::ShowPIDrwTools();   // Fentre axes et options de trace
    }
  }
}
void 
PIScDrawWdg::PasteSelection(unsigned int typ, void *pdata, unsigned int l)
{
if (typ != PICP_string) return;
int ll = (l<256) ? l+1 : 256;
char pc[256];
strncpy(pc, (char*)pdata, ll-1);  pc[ll-1] = '\0';
mBDrw->ElAddText(mPPos[0], mPPos[1], pc );
mBDrw->Refresh();
}

void
PIScDrawWdg::SetLimits(double xmin, double xmax, double ymin, double ymax,
                       int axrl, int ayud, bool tmp)
{
  if (xmax <= xmin || ymax <= ymin)
    return; // $CHECK$ exception ?
  SetDefaultDrawerLimits(xmin, xmax, ymin, ymax, axrl, ayud);
  mBDrw->SetLimits(xmin, xmax, ymin, ymax, axrl, ayud);
  mPPos[0] = 0.5*(XMin()+XMax());
  mPPos[1] = 0.5*(YMin()+YMax());
  if (!tmp) {
    xMinS = xmin;
    xMaxS = xmax;
    yMinS = ymin;
    yMaxS = ymax;
  }
}
  
void
PIScDrawWdg::UpdateLimits()
{
  if (NbDrawers() < 2)  return;
  PIDrawer* d = GetDrawer(1);
  if (d == NULL) return;
  d->UpdateLimits();
  SetLimits(d->XMin(), d->XMax(), d->YMin(), d->YMax()); 
  Refresh();
  return;  
}

int 
PIScDrawWdg::AddScDrawer(PIDrawer* d, bool ad)
{
  if (d == NULL)  return(-1);
  if (! LimitsFixed() ) {  //  On met a jour les limites 
    d->UpdateLimits();
    SetLimits(d->XMin(), d->XMax(), d->YMin(), d->YMax());
  }
  return(AddDrawer(d, true, ad));
}


void
PIScDrawWdg::SetTextWin(PILabel * tw, bool trw, int tx, int ty)
{
mTxw = tw;
if (trw)
  {
  if (mTrW)  mTrW->SetSize(tx, ty);
  else
    {
    mTrW = new PIWindow(this, "Point", PIWK_transient, tx, ty, 0, 0);
    mTrW->SetAutoDelChilds(true);
    mTrlb = new PILabel(mTrW, "PointCoord", tx, ty, 0, 0);
    mTrlb->SetBinding(PIBK_fixed,PIBK_fixed, PIBK_fixed,PIBK_fixed);
    }
  }
else
  if (mTrW) { delete mTrW; mTrW = NULL;  mTrlb = NULL; }

return;
}



// Gestion affichage coordonnees du point 

string   
PIScDrawWdg::GetClickText(double x, double y) 
// Donne le texte a afficher pour position x,y  
{
  char buff[128];
  sprintf(buff, "X= %g  Y= %g",  x, y);
  //  sprintf(buff, "%.3f,%.3f ",  x, y);
  return((string)buff);
}

void
PIScDrawWdg::ActivateSpecializedControls()
// Par défaut affiche la fenetres des attributs graphiques et axes
{
  PIDrwTools::SetCurrentBaseWdg(this);
  PIDrwTools::ShowPIDrwTools();   // Fentre axes et options de trace
}

void
PIScDrawWdg::But1Press(int x, int y)
{
  UpdateText(x, y);
  if (mTrW)
    { 
    int tpx, tpy;
    GetScreenPos(tpx, tpy);       
    tpy -= (mTrW->YSize()+5);
    if (tpy < 0)  tpy = 0;
    mTrW->SetPos(tpx, tpy);
    mTrW->Show();
    }
  if (mFgReticule) { // On trace une reticule
    cForCol = mWGrC->GetForeground();
    cGOmod  = mWGrC->GetGOMode();
    cLatt = mWGrC->GetLineAtt();
    mWGrC->SelForeground(PI_Magenta);
    mWGrC->SelGOMode(PI_GOXOR);
    mWGrC->SelLine(PI_ThinLine);
    mWGrC->DrawLine(x, 0, x, YSize());
    mWGrC->DrawLine(0, y, XSize(), y);
    mCPosX = x;   mCPosY = y;
    char buff[64];
    sprintf(buff,"%g , %g", mPPos[0], mPPos[1]);
    mWGrC->DrawString(x+10, y-10, buff); 
    }
  else SelPointerShape(PI_CrossPointer);

  Send(Msg(), PIMsg_Active);
  PIDrwTools::SetCurrentBaseWdg(this);
}

void
PIScDrawWdg::Ptr1Move(int x, int y)
{
  char buff[64];
  if (mFgReticule) { // On trace une reticule
    mWGrC->DrawLine(mCPosX, 0, mCPosX, YSize());
    mWGrC->DrawLine(0, mCPosY, XSize(), mCPosY);
    sprintf(buff,"%g , %g", mPPos[0], mPPos[1]);
    mWGrC->DrawString(mCPosX+10, mCPosY-10, buff); 
    mWGrC->DrawLine(x, 0, x, YSize());
    mWGrC->DrawLine(0, y, XSize(), y);
    mCPosX = x;   mCPosY = y;
    }  
  UpdateText(x, y);  // Met a jour mPPos
  if (mFgReticule) {
    sprintf(buff,"%g , %g", mPPos[0], mPPos[1]);
    mWGrC->DrawString(x+10, y-10, buff); 
    }
}

void
PIScDrawWdg::But1Release(int x, int y)
{
  UpdateText(x, y);
  if (mFgReticule) { // On efface la reticule
    mWGrC->DrawLine(mCPosX, 0, mCPosX, YSize());
    mWGrC->DrawLine(0, mCPosY, XSize(), mCPosY);
    char buff[64];
    sprintf(buff,"%g , %g", mPPos[0], mPPos[1]);
    mWGrC->DrawString(mCPosX+10, mCPosY-10, buff); 
    mWGrC->SelForeground(cForCol);
    mWGrC->SelGOMode(cGOmod);
    mWGrC->SelLine(cLatt); 
    mCPosX = mCPosY = 0;
    }
  else SelPointerShape(PI_ArrowPointer);
  if (mTrW)  mTrW->Hide();
}

void
PIScDrawWdg::UpdateText(int xp, int yp)
{

  double dx, dy;
  mBDrw->GetGraphicUC()->GrC2UC(xp, yp, dx, dy); 
  mPPos[0] = dx;  mPPos[1] = dy; 
  
  string ctxt =  GetClickText(dx, dy);
//DEBUG  char buff[32];    sprintf(buff, " %d:%d",xp,yp); ctxt += buff;
  if (mTxw)
    mTxw->SetLabel(ctxt);
  if (mTrlb)
    mTrlb->SetLabel(ctxt);
  return;

}

// Gestion du zoom
void
PIScDrawWdg::But2Press(int x, int y)
{
  xEndDrag = xBegDrag = x;
  yEndDrag = yBegDrag = y;
  cForCol = mWGrC->GetForeground();
  cGOmod  = mWGrC->GetGOMode();
  cPointer = GetPointerShape();
  cLatt = mWGrC->GetLineAtt();
  SelPointerShape(PI_CrossPointer);
  mWGrC->SelForeground(PI_Magenta);
  mWGrC->SelGOMode(PI_GOXOR);
  mWGrC->SelLine(PI_ThinLine);
  Send(Msg(), PIMsg_Active);
  PIDrwTools::SetCurrentBaseWdg(this);
}

void
PIScDrawWdg::But2Release(int /*x*/, int /*y*/)
{

  mWGrC->DrawBox(xBegDrag, yBegDrag, xEndDrag-xBegDrag, yEndDrag-yBegDrag);

  mWGrC->SelForeground(cForCol);
  mWGrC->SelGOMode(cGOmod);
  SelPointerShape(cPointer); 
  mWGrC->SelLine(cLatt); 
  
  if (xBegDrag == xEndDrag || yBegDrag == yEndDrag)
    SetLimits(xMinS, xMaxS, yMinS, yMaxS, kAxeDirSame, kAxeDirSame );
  else {
    double xl,yl,xh,yh;
    mBDrw->GetGraphicUC()->GrC2UC(xBegDrag, yBegDrag, xl, yl);
    mBDrw->GetGraphicUC()->GrC2UC(xEndDrag, yEndDrag, xh, yh);
    if (xl > xh) dbl_swap(xl, xh);
    if (yl > yh) dbl_swap(yl, yh);
    SetLimits(xl,xh,yl,yh, kAxeDirSame, kAxeDirSame, true);
  }
  
  Refresh();
}

void
PIScDrawWdg::Ptr2Move(int x, int y)
{
  mWGrC->DrawBox(xBegDrag, yBegDrag, xEndDrag-xBegDrag, yEndDrag-yBegDrag);
  xEndDrag = x;
  yEndDrag = y;
  mWGrC->DrawBox(xBegDrag, yBegDrag, xEndDrag-xBegDrag, yEndDrag-yBegDrag);
}


// ----------------------------------------------------------
//  Classe de trace de points/fonctions Y=f(X)
//  Trace Y=ay->Value() = f(X= ax->Value()) si ax et ay != NULL
//  Trace Y=ax->Value() = f(X= ax->X())  si ay == NULL
// ----------------------------------------------------------

/* --Methode-- */
PIYfXDrawer::PIYfXDrawer(P1DArrayAdapter* ax, P1DArrayAdapter* ay, bool ad)
    : PIDrawer()
{
  mAdDO = ad;
  mAx = ax;
  mAy = ay;
  if ((mAx == NULL))  mSz = 0;
  else {
    if (!mAy)  mSz = mAx->Size();
    else   mSz = (mAx->Size() < mAy->Size()) ? mAx->Size() :  mAy->Size();
    }
} 

/* --Methode-- */
PIYfXDrawer::~PIYfXDrawer()
{
  if (mAdDO && mAx)  delete mAx;
  if (mAdDO && mAy)  delete mAy;
}

/* --Methode-- */
void PIYfXDrawer::UpdateLimits()
{
  if (mSz <= 0)  return;
  double xmin, xmax, ymin, ymax;
  int i=0;
  double cv;
  if (mAy) {
    xmin = xmax = mAx->Value(0);
    ymin = ymax = mAy->Value(0);
    for(i=1; i<mSz; i++) {
      cv = mAx->Value(i); 
      if (cv < xmin)  xmin = cv;
      if (cv > xmax)  xmax = cv;
      cv = mAy->Value(i); 
      if (cv < ymin)  ymin = cv;
      if (cv > ymax)  ymax = cv;
      }
    }
  else {
    xmin = xmax = mAx->X(0);
    ymin = ymax = mAx->Value(0);
    for(i=1; i<mSz; i++) {
      cv = mAx->X(i); 
      if (cv < xmin)  xmin = cv;
      if (cv > xmax)  xmax = cv;
      cv = mAx->Value(i); 
      if (cv < ymin)  ymin = cv;
      if (cv > ymax)  ymax = cv;
      }
    }

  double dx, dy;
  dx = 0.02*(xmax-xmin);
  dy = 0.02*(ymax-ymin);

  SetLimits(xmin-dx, xmax+dx, ymin-dy, ymax+dy);
  SetAxesFlags(kBoxAxes | kExtTicks | kLabels);
}


/* --Methode-- */
void PIYfXDrawer::Draw(PIGraphicUC* g, double xmin, double ymin, double xmax, double ymax)
{
  double xp,yp;
  int i;

  if (mSz <= 0)  return;

//  Trace des markers 
  if ( (mMrk != PI_NotDefMarker) ||  (mLAtt == PI_NotDefLineAtt) ) {
    for(i=0; i<mSz; i++) {
      if (mAy) { xp = mAx->Value(i); yp = mAy->Value(i); }
      else { xp = mAx->X(i);  yp = mAx->Value(i); }
      if ( (xp < xmin) || (xp > xmax) || (yp < ymin) || (yp > ymax) )  continue;
      g->DrawMarker(xp, yp);
    }
  }

  if (mLAtt == PI_NotDefLineAtt)    return;

// Trace des lignes 
  double xp2, yp2;
  if (mAy) { xp2 = mAx->Value(0); yp2 = mAy->Value(0); }
  else { xp2 = mAx->X(0);  yp2 = mAx->Value(0); }
  for(i=1; i<mSz; i++) {
    xp = xp2; yp = yp2;
    if (mAy) { xp2 = mAx->Value(i); yp2 = mAy->Value(i); }
    else { xp2 = mAx->X(i);  yp2 = mAx->Value(i); }
    if ( ((xp < xmin) || (xp > xmax) || (yp < ymin) || (yp > ymax)) &&
         ((xp2 < xmin) || (xp2 > xmax) || (yp2 < ymin) || (yp2 > ymax)) )  continue;
    g->DrawLine(xp, yp, xp2, yp2);
  }
}


//================================================================
// PIFuncDrawer
// Classe de traceur de fonction  double = f(fouble)
//================================================================

/* --Methode-- */
PIFuncDrawer::PIFuncDrawer(PIFuncDrawer::FUNC f)
: mFunc(f)
{}

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

/* --Methode-- */
void
PIFuncDrawer::Draw(PIGraphicUC* g, double /*xmin*/, double/*ymin*/, double/*xmax*/, double/*ymax*/)
{
  PIGrCoord x1, x2, y1, y2;
  g->GetGrSpace(x1, x2, y1, y2);
  double xMax = x2;
  double xMin = x1;
  double yMax = y2;
  double yMin = y1;
  double xStep = (xMax - xMin)/100;
  double xOld = xMin;
  double yOld = mFunc(xMin);
  for (double x = xMin+xStep; x<xMax; x+=xStep) {
    double y = mFunc(x);
    if (y>yMin && yOld>yMin && 
        y<yMax && yOld<yMax) 
           g->DrawLine(xOld, yOld, x, y);
    xOld = x;
    yOld = y;
  }
}
