// Module PI : Peida Interactive PIDrawer3D PIDraw3DWdg
// DrawWidget 3D                R. Ansari 06/98
// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA

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

#include "pi3dwdg.h"
#include "pidrwtools.h"

// ......................................................................
// .......................  Classe PIDraw3DWdg ..........................
// ......................................................................

//++
// Class	PIDraw3DWdg
// Lib		PI
// include	pi3dwdg.h
//
//   	Classe pour la l'affichage et la manipulation d'un ensemble
//	de *Drawer3D* avec la gestion de zoom, rotation, déplacement
//	d'objet.
//	* Bouton-1 : Envoie le message "PIMsg_Active".
//	* Bouton-2 : Permet de faire tourner l'observateur (caméra) 
//	autour de l'objet.
//	* Shift-Bouton-2 : Permet de tourner l'objet en maintenant 
//	la caméra fixe. Cette fonction peut-être réalisée sans le shift
//	à partir du centre de l'objet, en l'ayant préalablement activée
//	par <Alt>S.
//	* Bouton-3 : Gestion de zoom (On approche/ éloigne la caméra, ou 
//	on change l'angle d'ouverture) 
//
//	Gestion du clavier :
//	* <Alt>G : Affichage d'une fenêtre de changement des attributs graphiques
//	* <Alt>O : Action identique a <Alt>G
//	* <Alt>V : Copier/*Coller* Ajout du texte du buffer copier/coller
//	sur l'objet à la position courante de la souris
//	* <Alt>Z : Supprime les élément graphiques ajoutés au dessin.
//	* <Alt>E : Supprime le dernier élément graphique ajouté.
//	* <Alt>A : Active/desactive l'affichage des axes
//	* <Alt>R : Reinitialise la vue 3D (vue par défaut) et rafraichit le dessin.
//	* <Alt>S : Active/desactive le mode de rotation d'objet sans Shift
//
//--
//++
// Links	Parents
// PIBaseWdg
//--
//++
// Links	Voir aussi
// PIDrawer3D
//--

//++
// Titre	Constructeur, méthodes
//--
//++
// PIDraw3DWdg(PIContainerGen *par, const char *nom, int sx=100, int sy=100, int px=0, int py=0)
//	Constructeur
// long	 kind()
//	Renvoie le type de l'objet ("=PIDraw3DWdg::ClassId")
// int AddDrawer3D(PIDrawer3D*, bool ad=false)
//	Ajoute un Drawer3D
//--

/* --Methode-- */
PIDraw3DWdg::PIDraw3DWdg(PIContainerGen *par, const char *nom, int sx, int sy, int px, int py)
        : PIBaseWdg(par, nom, sx, sy, px, py)
{  
  vfixed = false;
  // On cree l'objet PIGraphic3D pour la gestion des vues 3D
  mGr3d = new PIGraphic3D(mWGrC, 0, 0, sx, sy);

//  SetDefaultDrawRectangle(0.12, 0.12, 0.88, 0.88, true);
  SetDefaultDrawRectangle(0., 0., 1., 1., true);
  mBDrw = new PIElDrawer3D;
  //  mBDrw->SetLimits(0.12,0.88, 0.12,0.88, kAxeDirLtoR,kAxeDirDownUp);
  //  AddDrawer(mBDrw, 0.12, 0.12, 0.88, 0.88, true, false, false);
  mBDrw->SetLimits(0.,1.,0.,1.,kAxeDirLtoR,kAxeDirDownUp);
  AddDrawer3D(mBDrw, false);
  mBDrw->SetAxesFlags(kStdAxes);
  vfixed = false; // Il faut laisser le premier Drawer3D fixer les limites et la vue 3D

  mPx = mPy = 0;

  lPx = lPy = 0;
  lCol = PI_Black;
  cTeta = cPhi = 0.;  cUlen = 1.;

  ActivateKeyboard();
  ActivateButton(2);    //  Pour pouvoir tourner la camera
  ActivateMove(2);      
  ActivateButton(3);    //  Pour deplacer la camera le long de son axe
  ActivateMove(3);
  ActivateButton(1);    

  rOfg = false;
  axfg = true;
  skcfg = false;
}

/* --Methode-- */
PIDraw3DWdg::~PIDraw3DWdg()
{
  delete mBDrw;
  delete mGr3d;
}

/* --Methode-- */
void PIDraw3DWdg::Set3DView_Obs(double xo, double yo, double zo, double teta, double phi, double psi, 
                                double dax, double day, double co, double dco)
{
  mGr3d->Set3DCS_Obs(xo, yo, zo, teta, phi, psi, dax, day, co, dco);
  list<int>::iterator it;
  for(it = m3DrIl.begin(); it != m3DrIl.end(); it++)   
#if defined(CC_HAS_RTTI_SUPPORT)
    (dynamic_cast<PIDrawer3D*>(GetDrawerId(*it)))->Set3DView_Obs(xo, yo, zo, teta, phi, psi, dax, day, co, dco);
#else
    ((PIDrawer3D*)(GetDrawerId(*it)))->Set3DView_Obs(xo, yo, zo, teta, phi, psi, dax, day, co, dco);
#endif
  vfixed = true;
}

/* --Methode-- */
void PIDraw3DWdg::Set3DView(double xc, double yc, double zc, double xo, double yo, double zo, 
                            double dax, double day, double dco, double psi)
{
  mGr3d->Set3DCS(xc, yc, zc, xo, yo, zo, dax, day, dco, psi);
  list<int>::iterator it;
  for(it = m3DrIl.begin(); it != m3DrIl.end(); it++)   
#if defined(CC_HAS_RTTI_SUPPORT)
    (dynamic_cast<PIDrawer3D*>(GetDrawerId(*it)))->Set3DView(xc, yc, zc, xo, yo, zo, dax, day, dco, psi);
#else
    ((PIDrawer3D*)(GetDrawerId(*it)))->Set3DView(xc, yc, zc, xo, yo, zo, dax, day, dco, psi);
#endif
  vfixed = true;
}

/* --Methode-- */
bool PIDraw3DWdg::Get3DView(double& xc, double& yc, double& zc, double& xo, double& yo, double& zo, 
                            double& teta, double& phi, double& psi, double& dax, double& day, double& co, double& dco)
{
  return(mGr3d->Get3DCS(xc, yc, zc, xo, yo, zo, teta, phi, psi, dax, day, co, dco));
}


/* --Methode-- */
void PIDraw3DWdg::Update3DView()
{
  list<int>::iterator it = m3DrIl.begin();
#if defined(CC_HAS_RTTI_SUPPORT)
  while ( (it != m3DrIl.end()) && 
	  (dynamic_cast<PIDrawer3D*>(GetDrawerId(*it)) != mBDrw) ) it++; 
  if (it == m3DrIl.end())  return;
  PIDrawer3D* dr3 = dynamic_cast<PIDrawer3D*>(GetDrawerId(*it));
#else
  while ( (it != m3DrIl.end()) && 
	  ( (PIDrawer3D*)(GetDrawerId(*it)) != mBDrw) ) it++; 
  PIDrawer3D* dr3 = (PIDrawer3D*)(GetDrawerId(*it));
#endif
  if (dr3 == NULL)  return;
  dr3->UpdateLimits();
  double xo, yo, zo, xc, yc, zc, teta, phi, psi, dax, day, co, dco;
  bool vobs;
  vobs = dr3->Get3DView(xc, yc, zc, xo, yo, zo, teta, phi, psi, dax, day, co, dco);
  if (vobs) Set3DView_Obs(xo, yo, zo, teta, phi, psi, dax, day, co, dco);
  else Set3DView(xc, yc, zc, xo, yo, zo, dax, day, dco, psi);
  mBDrw->Copy3DBox(*dr3);
  Refresh();
}

/* --Methode-- */
int PIDraw3DWdg::DecodeOptionString(vector<string> & opt, bool rmdecopt)
{
  // AFAIRE - Reza 01/03/2002 
  return(0);
}


/* --Methode-- */
void PIDraw3DWdg::SetDrawers3DView()
{
  double xc, yc, zc, xo, yo, zo;
  double teta, phi, psi;
  double dax, day, co, dco;
  bool vo;
  vo = mGr3d->Get3DCS( xc, yc, zc, xo, yo, zo, teta, phi, psi, dax, day, co, dco);
  list<int>::iterator it;

#if defined(CC_HAS_RTTI_SUPPORT)
  if (vo)   for(it = m3DrIl.begin(); it != m3DrIl.end(); it++)   
    (dynamic_cast<PIDrawer3D*>(GetDrawerId(*it)))->Set3DView_Obs(xo, yo, zo, teta, phi, psi, dax, day, co, dco);
  else   for(it = m3DrIl.begin(); it != m3DrIl.end(); it++)   
    (dynamic_cast<PIDrawer3D*>(GetDrawerId(*it)))->Set3DView(xc, yc, zc, xo, yo, zo, dax, day, dco, psi);    
#else
  if (vo)   for(it = m3DrIl.begin(); it != m3DrIl.end(); it++)   
    ((PIDrawer3D*)(GetDrawerId(*it)))->Set3DView_Obs(xo, yo, zo, teta, phi, psi, dax, day, co, dco);
  else   for(it = m3DrIl.begin(); it != m3DrIl.end(); it++)   
    ((PIDrawer3D*)(GetDrawerId(*it)))->Set3DView(xc, yc, zc, xo, yo, zo, dax, day, dco, psi);    
#endif

}

/* --Methode-- */
int PIDraw3DWdg::AddDrawer3D(PIDrawer3D* dr3, bool ad)
{
if ((!vfixed) && (dr3 != mBDrw))  { 
  dr3->UpdateLimits();
  double xo, yo, zo, xc, yc, zc, teta, phi, psi, dax, day, co, dco;
  bool vobs;
  vobs = dr3->Get3DView(xc, yc, zc, xo, yo, zo, teta, phi, psi, dax, day, co, dco);
  if (vobs) Set3DView_Obs(xo, yo, zo, teta, phi, psi, dax, day, co, dco);
  else Set3DView(xc, yc, zc, xo, yo, zo, dax, day, dco, psi);
  mBDrw->Copy3DBox(*dr3);
  }
else { 
  double xc, yc, zc, xo, yo, zo, teta, phi, psi, dax, day, co, dco;
  if ( Get3DView(xc, yc, zc, xo, yo, zo, teta, phi, psi, dax, day, co, dco) ) 
      dr3->Set3DView_Obs(xo, yo, zo, teta, phi, psi, dax, day, co, dco);
  else  dr3->Set3DView(xc, yc, zc, xo, yo, zo, dax, day, co, psi);
  }
int id = AddDrawer(dr3, false, false, ad);
if (dr3 != mBDrw) dr3->SetAxesFlags(kAxesNone);
m3DrIl.push_back(id);
return(id);
}

/* --Methode-- */
void PIDraw3DWdg::RemoveDrawer(int id)
{
  list<int>::iterator it;
  for(it = m3DrIl.begin(); it != m3DrIl.end(); it++)   
    if ( (*it) == id )  { m3DrIl.erase(it);  break; }
  PIBaseWdg::RemoveDrawer(id);
}

/* --Methode-- */
void PIDraw3DWdg::DeleteDrawers()
{
  m3DrIl.erase(m3DrIl.begin(),  m3DrIl.end());
  PIBaseWdg::DeleteDrawers();
}

/* --Methode-- */
void PIDraw3DWdg::Keyboard(int key, PIKeyModifier kmod)
{
if (kmod == PIKM_Alt) {
  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 == 'E' || key == 'e') { mBDrw->ElDelLast();  Refresh(); } // Pour supprimer le dernier element ajoute
  else if (key == 'O' || key == 'o') { // fenetre des options de controle du drawer actif
    PIDrawer* actdrw = GetActiveDrawer();
    if (actdrw != NULL) actdrw->ShowControlWindow(this);
  }
  else if (key == 'G' || key == 'g') // Fenetre des options graphiques de trace
    PIDrwTools::ShowPIDrwTools(this);   

  else if (key == 'A' || key == 'a')  {    // On affiche ou on enleve les axes
    axfg = !axfg;
    list<int>::iterator it;
    if (!axfg) mBDrw->SetAxesFlags(kAxesNone);
    else mBDrw->SetAxesFlags(kStdAxes);
    Refresh(); 
    }
//  <Alt>S active la sensibilite de la zone centrale pour tourner l'objet
  else if (key == 'S' || key == 's')  skcfg = !skcfg;  
  else if (key == 'R' || key == 'r')  Update3DView();
  }
}

/* --Methode-- */
void PIDraw3DWdg::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';
// printf("PIDraw3DWdg::PasteSelection() / Debug  %s - %d %d -> %g %g \n", pc, 
//        mPx, mPy, (double)mPx/(double)XSize(), 1.-(double)mPy/(double)YSize());
mBDrw->ElAddText((double)mPx/(double)XSize(), 1.-(double)mPy/(double)YSize(), pc );
mBDrw->Refresh();
}

/* --Methode-- */
void PIDraw3DWdg::But2Press(int x, int y)
{
  mGr3d->SetBaseGraphic(mWGrC, 0, 0, XSize(), YSize());
  double xo, yo, zo;
  double teta, phi, psi;
  double dax, day, co, dco;
  mGr3d->Get3DCS(cXc, cYc, cZc, xo, yo, zo, teta, phi, psi, dax, day, co, dco);
//   puts("\n PIDraw3DWdg::But2Press() ");
//   mGr3d->PrintCS();
  lPx = x;  lPy = y;

// Pour savoir si on se trouve ds la zone centrale   
  int czx = XSize()/10;
  int czy = YSize()/10;
  PIKeyModifier kmod;
  unsigned long tm;
  GetLastEventInfo(kmod, tm);
  rOfg = false;
  if (kmod ==  PIKM_Shift)  rOfg = true;
  else if (skcfg) {
    int dmix = x-XSize()/2;
    int dmiy = y-YSize()/2;
    if (dmix < 0) dmix = -dmix;
    if (dmiy < 0) dmiy = -dmiy;
    if ((dmix < czx) && (dmiy < czy))  rOfg = true;
    }

  cUlen = 0.5*(dax+day)*co*0.3;    cDax = dax;   cDay = day;  clCO = co; 
  cTeta = teta;   cPhi = phi;
  SelPointerShape(PI_TDLRArrowPointer);
  lCol = mWGrC->GetForeground();
  //  mWGrC->SelForeground(GetBackgroundColor());
  mWGrC->SelForeground(PI_Magenta);
/*
  {
  char buff[256];
  sprintf(buff, "But2Press() C=%g %g %g  T,P=%g %g   ", xc, yc, zc, cTeta, cPhi); 
  mWGrC->DrawOpaqueString(0,20,buff);
  }
*/
  DrawXYZAxes();
  if (rOfg)  { czx /= 2;   czy /= 2;  mWGrC->DrawBox(XSize()/2-czx, YSize()/2-czy, czx*2, czy*2); }
 
  Send(Msg(), PIMsg_Active);
  PIDrwTools::SetCurrentBaseWdg(this);
}

/* --Methode-- */
void PIDraw3DWdg::Ptr2Move(int x, int y)
{
  DrawXYZAxes();
  double teta, phi;

  if (rOfg) { 
    phi = cPhi + (M_PI*0.25*(double)(x-lPx)/XSize());
    teta = cTeta - (M_PI*0.25*(double)(y-lPy)/YSize());
    mGr3d->RotateObject(teta, phi, false);
    }
  else {
    phi = cPhi - (M_PI*0.5*(double)(x-lPx)/XSize());
    teta = cTeta + (M_PI*0.5*(double)(y-lPy)/YSize());
    mGr3d->RotateObserver(teta, phi, 0., false);
    }
/*  {
  char buff[256];
  sprintf(buff, "Ptr2Move() T,P=%g %g -> %g %g  ", cTeta, cPhi, teta, phi); 
  mWGrC->DrawOpaqueString(0,20,buff);
  }
*/
  DrawXYZAxes();
}


/* --Methode-- */
void PIDraw3DWdg::But2Release(int x, int y)
{
  DrawXYZAxes();
  if (rOfg) {
    int czx = XSize()/20;
    int czy = YSize()/20;
    mWGrC->DrawBox(XSize()/2-czx, YSize()/2-czy, czx*2, czy*2);
    }

//  puts("\n PIDraw3DWdg::But2Release()");
//  mGr3d->PrintCS();
  SelPointerShape(PI_ArrowPointer);
  mWGrC->SelForeground(lCol);
  SetDrawers3DView();
  Refresh();
}


/* --Methode-- */
void PIDraw3DWdg::But3Press(int x, int y)
{
  double xo, yo, zo;
  double teta, phi, psi;
  double dax, day, co, dco;
  mGr3d->Get3DCS(cXc, cYc, cZc, xo, yo, zo, teta, phi, psi, dax, day, co, dco);
  mGr3d->SetBaseGraphic(mWGrC, 0, 0, XSize(), YSize());
  lPx = x;  lPy = y;  
  cUlen = 0.5*(dax+day)*co*0.3;    cDax = dax;   cDay = day;  clCO = co;
  cTeta = teta;   cPhi = phi;
  SelPointerShape(PI_TDLRArrowPointer);
  lCol = mWGrC->GetForeground();
  //  mWGrC->SelForeground(GetBackgroundColor());
  mWGrC->SelForeground(PI_Magenta);
/*
  {
  char buff[256];
  sprintf(buff, "But3Press() C=%g %g %g  O= %g %g %g  L= %g   ", xc, yc, zc, xo, yo, zo, co); 
  mWGrC->DrawOpaqueString(0,20,buff);
  }
*/
  DrawXYZAxes();
  Send(Msg(), PIMsg_Active);
  PIDrwTools::SetCurrentBaseWdg(this);
}

/* --Methode-- */
void PIDraw3DWdg::Ptr3Move(int x, int y)
{
  DrawXYZAxes();
  double rapx = (double)(lPx-x)/XSize();
  if (rapx > 0.) rapx = rapx*2.+1;
  else rapx += 1.;
  if (rapx < 0.3)  rapx = 0.3;
  if (rapx > 3.)  rapx = 3.;
  double rapy = (double)(y-lPy)/YSize();
  if (rapy > 0.) rapy = rapy*2.+1;
  else rapy += 1.;
  if (rapy < 0.3)  rapy = 0.3;
  if (rapy > 3.)  rapy = 3.;
/*
  {
  char buff[256];
  sprintf(buff, "Ptr3Move() Rap=%g %g -> %g %g %g     ", rapx, rapy, rapy*clCO, rapx*cDax, rapx*cDay);
  mWGrC->DrawOpaqueString(0,20,buff);
  }
*/
  mGr3d->ZoomInOut(rapy*clCO, rapx*cDax, rapx*cDay, false);
  DrawXYZAxes();
}

/* --Methode-- */
void PIDraw3DWdg::But3Release(int x, int y)
{
  DrawXYZAxes();
  SelPointerShape(PI_ArrowPointer);
  mWGrC->SelForeground(lCol);
  SetDrawers3DView();
  Refresh();
}


/* --Methode-- */
void PIDraw3DWdg::DrawXYZAxes()
{
  mWGrC->SelGOMode(PI_GOXOR);
  PIGrCoord x1,y1,z1,x2,y2,z2;
  x1 = cXc;  y1 = cYc;  z1 = cZc;
  x2 = cXc+cUlen;   y2 = cYc;   z2 = cZc; 
  mGr3d->DrawLine3D(x1, y1, z1, x2, y2, z2);
  x2 = cXc+cUlen*1.1;
  mGr3d->DrawString3D(x2, y2, z2, "X");
  x2 = cXc;   y2 = cYc+cUlen;   z2 = cZc; 
  mGr3d->DrawLine3D(x1, y1, z1, x2, y2, z2);
  y2 = cYc+cUlen*1.1;
  mGr3d->DrawString3D(x2, y2, z2, "Y");   
  x2 = cXc;   y2 = cYc;   z2 = cZc+cUlen; 
  mGr3d->DrawLine3D(x1, y1, z1, x2, y2, z2);
  z2 = cZc+cUlen*1.1;
  mGr3d->DrawString3D(x2, y2, z2, "Z");   
//   mGr3d->DrawCircle(0., 0., daxO*0.30);
  char buff[256];
  double xc,yc,zc,xo,yo,zo,teta,phi,psi,dax,day,co,dco;
  mGr3d->Get3DCS(xc,yc,zc,xo,yo,zo,teta,phi,psi,dax,day,co,dco);
  sprintf(buff, " C: %g %g %g -> O: %g %g %g  (T/P0= %g %g)     ", xc,yc,zc, xo, yo, zo, cTeta, cPhi);
  mWGrC->DrawString(0,20,buff);
  sprintf(buff, " T,P= %g %g %g lCO= %g  dax= %g %g UL=%g    ", teta,phi,psi,co,dax,day,cUlen); 
  mWGrC->DrawString(0,40,buff);
  mWGrC->SelGOMode(PI_GOCopy);  

}


/* --Methode-- */
void PIDraw3DWdg::But1Press(int x, int y)
{
  Send(Msg(), PIMsg_Active);
  PIDrwTools::SetCurrentBaseWdg(this);
  mPx = x;  mPy = y;
  if ( (x > 50) || ( y > 50) ) return; 
  Update3DView();
}

/*   Pour debugger sur bouton-3 
static int isw = 0;
void PIDraw3DWdg::But3Press(int x, int y)
x -= XSize()/2;
y -= YSize()/2;

double fx = (double)x/XSize(); 
double fy = (double)y/YSize(); 
if ( (fx<0.2) && (fx>-0.2) && (fy<0.2) && (fy>-0.2) ) {
  
  if ((isw%3) == 0) { 
    Set3DView(0., 0., 0., 0., 0., -3. , 0.3, 0.3, 2); 
    puts("\n Set3DView(0., 0., 0., 0., 0., -3. , 0.3, 0.3, 2)");
  }
  else if ((isw%3) == 1) { 
    Set3DView(0., 0., 0., 2., 1., 3. , 0.3, 0.3, 2);
    puts("\n Set3DView(0., 0., 0., 2., 1., 3. , 0.3, 0.3, 2)");
  }
  else if ((isw%3) == 2) { 
    Set3DView_Obs(0.,4.,0.,3.14159/2.,-3.14159/2.,0.,0.25,0.25,4.,2.);
    puts("\n Set3DView_Obs(0.,4.,0.,3.14159/2.,3.14159/2.,0.,0.25,0.25,4.,2.)");
  }
  isw++;
  mGr3d->PrintCS();
  double xc,yc,zc,xo,yo,zo,teta,phi,psi,dax,day,co,dco;
  mGr3d->Get3DCS(xc,yc,zc,xo,yo,zo,teta,phi,psi,dax,day,co,dco);
  mGr3d->Set3DCS(xc,yc,zc,xo,yo,zo,dax,day,dco,psi);
  mGr3d->PrintCS();
  mGr3d->Set3DCS_Obs(xo,yo,zo,teta,phi,psi,dax,day,co,dco);
  mGr3d->PrintCS();
  puts(" --------------------- \n");
  Refresh();
  return;
}
if ((x > 0) && (y > 0) )  { 
  puts(" But3Press()Set3DView(0., 0., 0., 5., 0., 0. , 0.3, 0.3, 2)");
  Set3DView(0., 0., 0., 5., 0., 0. , 0.3, 0.3, 2);
}
else if ((x < 0) && (y > 0) )  { 
  puts(" But3Press()Set3DView(0., 0., 0., 0., 5., 1. , 0.3, 0.3, 2) ");
  Set3DView(0., 0., 0., 0., 5., 1. , 0.3, 0.3, 2);
}
else  if ((x < 0) && (y < 0) ) {
  puts(" But3Press()Set3DView(0., 0., 0., 1., 1., 5. , 0.3, 0.3, 2)");
  Set3DView(0., 0., 0., 1., 1., 5. , 0.3, 0.3, 2);
}
else {
  puts(" But3Press()Set3DView(0., 0., 0., -3., -3., 3. , 0.3, 0.3, 2)");
  Set3DView(0., 0., 0., -3., -3., 3. , 0.3, 0.3, 2);
}

Refresh();
*/
