

#include <iostream>
#include <stdlib.h>
#include <sstream>
#include <iomanip>

#include "glvisorbase.h"
#include "qapplication.h"
#include <qmessagebox.h>
#include <q3popupmenu.h>
#include <qcursor.h>
#include <qimage.h>
#include <qclipboard.h>
//Added by qt3to4:
#include <QKeyEvent>
#include <QPixmap>
#include <QMouseEvent>
#include <QWheelEvent>
//#include <GL/glut.h>

#ifdef _USE_KDE_
#include <kprinter.h>
#include <kcolordialog.h>
#else
#include <qprinter.h>
#include <qcolordialog.h>
#endif
#include <qpainter.h>
#include <q3paintdevicemetrics.h>
#include <qtimer.h>
#include "kdebug.h"

#include "gl2ps.h"
#include "qtimeropenmenu.h"

const int spheres=8;

const float rotationpass=5.0f;
static QFont glFont ( "Helvetica" );
GLfloat transformmatrix[4][4] =
    {
        {1.0, 0.0, 0.0, 0.0},
        {0.0, 1.0, 0.0, 0.0},
        {0.0, 0.0, 1.0, 0.0},
        {0.0, 0.0, 0.0, 1.0}
    };


CGlVisorBase::CGlVisorBase ( const QGLFormat& format, QWidget* parent) : QGLWidget ( format, parent)
{
  m_parent=parent;
  Init();
}
CGlVisorBase::CGlVisorBase ( QGLContext* context, QWidget* parent ) : QGLWidget ( context, parent )
{
  m_parent=parent;
  Init();
}
CGlVisorBase::CGlVisorBase ( QWidget * parent ) :
    QGLWidget (  parent )
{

  m_parent=parent;
  Init();

}

void CGlVisorBase::Init()
{
  m_lgrabbed=false;
  m_rgrabbed=false;
  m_rotate.x=m_rotate.y=m_rotate.z=0;
  m_scale=1;
  m_range=5.0f;

  //Setup background
  m_bkcolor=QColor ( 0,0,0 );

  //setFocusPolicy ( Qt::ClickFocus );

  m_graphmode=STICKS;
  m_bshowtypes=m_bisfull=false;
  m_bshownumbers=m_bshowtypes=false;

  m_optionsmenu= new QTimerOpenMenu ( this );
  m_optionsmenu->insertItem ( "Show atom numbers",this,SLOT ( OnShowNumbers() ),0,0 );
  m_optionsmenu->insertItem ( "Show atom types",this,SLOT ( OnShowTypes() ),0,1 );
  m_optionsmenu->insertItem ( "Change background color",this,SLOT ( OnChangeBackground() ),0,2 );
  connect(m_optionsmenu,SIGNAL(stopped()),this,SLOT(OnMenuHidden()));
  m_rendermenu= new Q3PopupMenu ( this );
  m_optionsmenu->insertItem ( "Rendering mode",m_rendermenu,0,3 );
  m_rendermenu->insertItem ( "Wireframe",0,0 );
  //rendermenu->setItemParameter(0,0);
  m_rendermenu->insertItem ( "Sticks",1,1 );
  //rendermenu->setItemParameter(1,1);
  m_rendermenu->insertItem ( "CPK",2,2 );
  //rendermenu->setItemParameter(2,2);
  m_rendermenu->setCheckable ( true );
  OnChangeGraphMode ( static_cast<int> ( m_graphmode ) );
  connect ( m_rendermenu,SIGNAL ( activated ( int ) ),this,SLOT ( OnChangeGraphMode ( int ) ) );


  /*define the transformation matrix
  {
                            {1.0, 0.0, 0.0, 0.0},
                            {0.0, 1.0, 0.0, 0.0},
                            {0.0, 0.0, 1.0, 0.0},
                            {0.0, 0.0, 0.0, 1.0}
                          };*/
  m_transform = new GLfloat[16];
  for ( int i=0;i<16;i++ )
    m_transform[i]=0.0;
  m_transform[0]=m_transform[5]=m_transform[10]=m_transform[15]=1.0f;


  //no molecules initiallu
  m_current=-1;
  //glFont.setStyleStrategy ( QFont::OpenGLCompatible );
  m_glfont.setStyleStrategy ( QFont::OpenGLCompatible );
  m_maxdepth=20;
}
CGlVisorBase::~CGlVisorBase()
{
  delete [] m_transform;
  //gluDeleteQuadric(m_quadric);
}
void CGlVisorBase::initializeGL()
{

  glEnable ( GL_NORMALIZE );
  GLfloat light_position[]={ 0.8,0.7,1.0,0.0 };
  GLfloat ambient_color[]={ 0.20,0.20,0.20,1.0 };
  GLfloat specular_color[]= { 1.0,1.0,1.0,1.0 };
  GLfloat difusse_color[]= { 1.0,1.0,1.0,1.0 };


  qglClearColor ( m_bkcolor );

  glLightfv ( GL_LIGHT0, GL_POSITION, light_position );
  glLightfv ( GL_LIGHT0, GL_AMBIENT, ambient_color );
  glLightfv ( GL_LIGHT0, GL_SPECULAR, specular_color );
  glLightfv ( GL_LIGHT0, GL_DIFFUSE, difusse_color );

  glEnable ( GL_LIGHTING );   // so lighting models are used
  glEnable ( GL_LIGHT0 );  // we'll use LIGHT0

  glEnable ( GL_DEPTH_TEST ); // allow z-buffer display
  glEnable ( GL_COLOR_MATERIAL );
  glEnable ( GL_BLEND );   // enable alpha-channel blending
  glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

  kDebug() << "GLVisorBase::InitializeGL" << endl;

}

void CGlVisorBase::resizeGL ( int w, int h )
{

  GLsizei width,height;

  width = w;
  height = h;

  if ( h==0 )
    m_aspect = ( GLdouble ) width;
  else
    m_aspect = ( GLdouble ) width/ ( GLdouble ) height;


  glViewport ( 0,0,width,height );
  glMatrixMode ( GL_PROJECTION );
  glLoadIdentity();
  SetupPerspective();
  glMatrixMode ( GL_MODELVIEW );

  // glLoadIdentity();

}

void CGlVisorBase::SetupPerspective()
{
  gluPerspective ( 45,m_aspect,1.0,m_maxdepth );
  glTranslatef ( 0.0,0.0,-m_maxdepth/2 );

}

void CGlVisorBase::paintGL()
{
  RenderScene();
}

void CGlVisorBase::RenderText ( const QString& str,int x1, int y1,int z1 )
{
  /*#ifdef HAVE_GLUT
    glRasterPos3f(x,y,z);
    const char* c;
    const char* string=str.ascii();
    for (c=string; *c != '\0'; c++)
    {
      glutBitmapCharacter(GLUT_BITMAP_HELVETICA_10, *c);
    }
  #else*/
  //glPushMatrix();
  //glTranslatef(x1,y1,z1);
  renderText ( 0,0,0,str,glFont );
  //7glPopMatrix();
  //#endif
}

void CGlVisorBase::RenderText ( const std::string& str,int x, int y, int z )
{
#ifdef HAVE_GLUT
  glRasterPos3f ( x,y,z );
  const char* c;
  const char* string=str.c_str();
  for ( c=string; *c != '\0'; c++ )
  {
    glutBitmapCharacter ( GLUT_BITMAP_HELVETICA_10, *c );
  }
#else
  //glPushMatrix();
  //glTranslatef(x,y,z);
  renderText ( 0,0,0,QString ( str.c_str() ),glFont );
  //glPopMatrix();
#endif
}



void CGlVisorBase::RenderScene ( GLenum mode )
{
  glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

  if ( mode != GL_SELECT )
  {
    glPushMatrix();
    glEnable ( GL_COLOR_MATERIAL );
    RenderButtons();
    RenderScreenText();
    glDisable ( GL_COLOR_MATERIAL );
    glPopMatrix();
  }



  glPushMatrix();
  glScalef ( m_scale,m_scale,m_scale );

  if ( !m_molecule.m_atom.empty() )
  {
    RenderMolecule ( mode );

  }

  //  glDisable(GL_COLOR_MATERIAL);
  glPopMatrix();

}

void CGlVisorBase::RenderMolecule ( GLenum mode/*=GL_RENDER*/ )
{

  m_quadric=gluNewQuadric();
  gluQuadricDrawStyle ( m_quadric, ( GLenum ) GLU_FILL );

  glInitNames();
  glPushName ( 0 );
  std::vector<CAtom>::iterator mit;

  int i=0;
  for ( mit=m_molecule.m_atom.begin();mit!=m_molecule.m_atom.end();mit++,i++ )
  {

    glPushMatrix();

    glTranslatef ( mit->c.x,mit->c.y,mit->c.z );
    glLoadName ( i );

    GetMaterialColor ( *mit );
    if ( mode == GL_RENDER )
    {
      if ( m_bshownumbers || m_bshowtypes )
      {
        glDisable ( GL_LIGHTING );

        QColor col ( QColor ( 255,255,255 ) );
        qglColor ( col );

        //static QFont glFont("Helvetica");
        //load here a bitmap font, other fonts seems not to work with GLWidgets
        //glFont.setStyleStrategy(QFont::PreferBitmap);
        //write two spaces to leave some separation between
        //number and atom
        QString number="   ";

        if ( m_bshowtypes )
          number+=mit->name.c_str();
        if ( m_bshownumbers )
        {
          QString str;
          str.sprintf ( "%d",i+1 );
          number+=str;
        }
        renderText ( 0,0, 0, number,glFont );
        //RenderText(number,0,0,0);
        glEnable ( GL_LIGHTING );
      }


    }
    if ( m_graphmode == CPK )
      gluSphere ( m_quadric,mit->GetVdW() /*0.05*/,spheres*3,spheres*3 );
    if ( m_graphmode == STICKS )
      gluSphere ( m_quadric,0.05,spheres,spheres );
    if ( m_graphmode == WIREFRAME )
    {
      glBegin ( GL_POINTS );
      glVertex3f ( 0,0,0 );
      glEnd();
      if ( mit->IsSelected() )
        gluSphere ( m_quadric,0.1,spheres,spheres );

    }
    glPopMatrix();

  }


  if ( mode == GL_RENDER )
  {
    glLineWidth ( 1.0*m_scale );
    for ( mit=m_molecule.m_atom.begin();mit!=m_molecule.m_atom.end();++mit )
    {
      //now draw the bonds


      if ( m_graphmode == WIREFRAME )
      {
        glDisable ( GL_LIGHTING );
        glBegin ( GL_LINES );

        std::vector<bond>::iterator cit;
        for ( cit=mit->conexion.begin();cit!=mit->conexion.end();cit++ )
        {

          coordinate middle=coordinate::GetMiddle ( mit->c,m_molecule.m_atom[cit->index].c );
          GetColor ( *mit );
          glVertex3f ( mit->c.x,mit->c.y,mit->c.z );
          glVertex3f ( middle.x,middle.y,middle.z );

          /*  GetAtomColor(m_molecule.m_atom[cit->index]);

            glVertex3f(middle.x,middle.y,middle.z);
            glVertex3f(m_molecule.m_atom[cit->index].c.x,
                       m_molecule.m_atom[cit->index].c.y,
                       m_molecule.m_atom[cit->index].c.z);*/


        }
        glEnd();
        glEnable ( GL_LIGHTING );
      }

      if ( m_graphmode == STICKS )
      {
        GetMaterialColor ( *mit );
        std::vector<bond>::iterator cit;
        for ( cit=mit->conexion.begin();cit!=mit->conexion.end();cit++ )
        {

          glPushMatrix();
          glTranslatef ( mit->c.x,mit->c.y,mit->c.z );
          coordinate bond=m_molecule.m_atom[cit->index].c-mit->c;
          coordinate zaxis ( 0,0,1 );
          coordinate normal=zaxis^bond;
          float angle=coordinate::GetAngleVector ( zaxis,bond );
          if ( fabs ( angle ) < 1e-6  || fabs ( M_PI-angle ) < 1e-6 )
            normal=coordinate ( 0,1,0 );
          glRotatef ( angle*180/M_PI,normal.x,normal.y,normal.z );
          gluCylinder ( m_quadric,0.05,0.05, bond.norm() /2 , spheres,spheres );
          glPopMatrix();


        }
      }
    }
  }
  gluDeleteQuadric ( m_quadric );

  return;

}

void CGlVisorBase::GetColor ( CAtom& a )
{
  GLfloat color[3]={0.5,0.2,0.1};
  a.GetColor ( &color[0],&color[1],&color[2] );
  glColor3f ( color[0],color[1],color[2] );
  return;
}
void CGlVisorBase::GetMaterialColor ( CAtom &a )
{

//  std::string& s=a.name;
  // std::cerr << "atom" << a.label << ",name=" << s << std::endl;


  GLfloat color[4]={0.5,0.2,0.1,1.0};

  if ( a.IsSelected() )
  {
    //glColor3f(1.0f,0.5f,0.5f);
    color[0]=0.80f;
    color[1]=0.1f;
    color[2]=0.1f;
    color[3]=0.5f;
    glMaterialfv ( GL_FRONT_AND_BACK, GL_AMBIENT, color );
    glMaterialfv ( GL_FRONT_AND_BACK, GL_DIFFUSE, color );
    return;
  }


  a.GetColor ( &color[0],&color[1],&color[2] );
  glMaterialfv ( GL_FRONT_AND_BACK, GL_AMBIENT, color );
  glMaterialfv ( GL_FRONT_AND_BACK, GL_DIFFUSE, color );
  return;


}

void CGlVisorBase::mousePressEvent ( QMouseEvent* e )
{
  m_oldpoint=e->pos();
  switch ( e->button() )
  {
    case Qt::LeftButton:
      m_lgrabbed=true;
      m_rgrabbed=false;
      selectButton ( e->pos() );
      break;
    case Qt::MidButton:
      break;
    case Qt::RightButton:
      m_optionsmenu->start ( 200 );
      m_lgrabbed=false;
      m_rgrabbed=true;
      break;
    default:
      break;
  }

}


void CGlVisorBase::mouseReleaseEvent ( QMouseEvent* e )
{
  switch ( e->button() )
  {
    case Qt::LeftButton:
      m_lgrabbed=false;
      break;
    case Qt::RightButton:
      m_rgrabbed=false;
     // std::cout << "m_rgrabbed=false" << std::endl;
      /*if(e->pos() == m_oldpoint)
      m_optionsmenu->exec(m_oldpoint);*/
      break;
    default:
      break;
  }

}

void CGlVisorBase::mouseMoveEvent ( QMouseEvent* e )
{

  int x=m_oldpoint.x()-e->pos().x();
  int y=m_oldpoint.y()-e->pos().y();

  if ( abs ( x ) <2 && abs ( y ) <2 )
    return;

  if ( m_lgrabbed )
  {

    coordinate c ( x,-y,0 );
    //Get a perpendicular vector
    m_rotationvector=c^coordinate ( 0,0,1 );


    //glGetFloatv( GL_MODELVIEW_MATRIX, (GLfloat *) transform );
    glLoadIdentity();
    glRotatef ( rotationpass, m_rotationvector.x, m_rotationvector.y, m_rotationvector.z );
    glMultMatrixf ( ( GLfloat * ) m_transform );
    glGetFloatv ( GL_MODELVIEW_MATRIX, ( GLfloat * ) m_transform );


    updateGL();

  }
  if ( m_rgrabbed )
  {
    if ( m_oldpoint.y() < e->pos().y() )
      m_scale*=1.3;
    if ( m_oldpoint.y() > e->pos().y() )
      m_scale/=1.3;
    updateGL();
  }
  m_oldpoint.setX ( e->pos().x() );
  m_oldpoint.setY ( e->pos().y() );
}


void CGlVisorBase::mouseDoubleClickEvent ( QMouseEvent* e )
{}


void CGlVisorBase::wheelEvent ( QWheelEvent* e )
{


  if ( e->delta() <0 )
    m_scale*=1.3;
  if ( e->delta() >0 )
    m_scale/=1.3;

  updateGL();

}

/** show atom numbering */
void CGlVisorBase::OnShowNumbers()
{

  m_bshownumbers=!m_optionsmenu->isItemChecked ( 0 );
  m_optionsmenu->setItemChecked ( 0,m_bshownumbers );
  updateGL();
  //Export("fdf");
}
/** Print the GL contents rendering a pixmap */
void CGlVisorBase::Print ( QPrinter* pP )
{


  QPainter p ( pP );
  Q3PaintDeviceMetrics Metric=Q3PaintDeviceMetrics ( pP );
  // This seems not to work at least with linux nvidia drivers
  QPixmap pix=renderPixmap ( Metric.width(),Metric.height() );
  p.drawPixmap ( 0, 0, pix );
}

#include <fstream>
#include <stdio.h>
#include "tokenizer.h"
#include <locale.h>

// void CGlVisorBase::Export()
// {
//   RenderScene();
// }
// void CGlVisorBase::Export(const QString& file,const QString& mode)
// {
//
//     //state=gl2psEndPage();
//   char *oldlocale = setlocale(LC_NUMERIC, "C");
//   FILE* fp=fopen(file.ascii(),"wb");
//   if (!fp) return;
//   GLint buffsize=0, state =GL2PS_OVERFLOW;
//   GLint viewport[4];
//   glGetIntegerv(GL_VIEWPORT, viewport);
//
//   StringTokenizer path(file,".");
//   StringTokenizer::toupper(path.back());
//   int gmode=GL2PS_PS;
//   std::cout << "Exportint in mode " << mode.latin1() << "to file " << file.latin1() << std::endl;
//   if (mode == "EPS" ) gmode=GL2PS_EPS;
//   if (mode =="PS") gmode =GL2PS_PS;
//   if (mode == "SVG" ) gmode=GL2PS_SVG;
//   while ( state == GL2PS_OVERFLOW )
//   {
//
//     buffsize+=2048*2048;
//     gl2psBeginPage ( file.ascii(), "KryoMol", viewport,
//                      GL2PS_PS , GL2PS_BSP_SORT, GL2PS_SILENT | GL2PS_BEST_ROOT ,
//                      GL_RGBA, 0, NULL, 0, 0, 0, buffsize,
//                      fp,file.ascii());
//     std::cout << "begin drawing" << std::endl;
//     CGlVisorBase::RenderScene();
//     std::cout << "finish drawing" << std::endl;
//     state=gl2psEndPage();
//
//   }
//   fclose(fp);
//   setlocale(LC_NUMERIC, oldlocale);
// //
// }
/** Get a snapshot of the GL Widget */
void CGlVisorBase::SaveSnapshot ( const QString& file, const QString& format )
{

  QString Format=format.upper();
  QImage img= this->grabFrameBuffer();
  img.save ( file,Format.toLatin1() );
}
/** change the background color */
void CGlVisorBase::OnChangeBackground()
{

#ifdef _USE_KDE_
  KColorDialog::getColor ( m_bkcolor,m_bkcolor );
#else
  m_bkcolor=QColorDialog::getColor ( m_bkcolor );
#endif

  qglClearColor ( m_bkcolor );
}

/** show atom types */
void CGlVisorBase::OnShowTypes()
{

  m_bshowtypes=!m_optionsmenu->isItemChecked ( 1 );
  m_optionsmenu->setItemChecked ( 1,m_bshowtypes );
  updateGL();

}

/** No descriptions */
void CGlVisorBase::OnFullScreen()
{
#warning not implemented
/*  QWidget* w=NULL;

  if ( !m_bisfull )
  {
    w=QApplication::desktop();
    this->reparent ( w,Qt::WStyle_Customize | Qt::WStyle_NoBorder,QPoint ( 0,0 ) );
    this->resize ( w->size() );
    this->show();
    m_bisfull=true;

  }
  else
  {
    this->reparent ( m_parent,0,QPoint ( 0,0 ) );
    this->resize ( m_parent->size() );
    this->show();
    m_bisfull=false;
  }*/
}

void CGlVisorBase::keyPressEvent ( QKeyEvent* e )
{

  if ( e->key() ==Qt::Key_F || e->key() ==Qt::Key_Escape )
  {
    OnFullScreen();
  }
}

void CGlVisorBase::GetImage ( QImage& img, int w, int h )
{
  img=renderPixmap ( w,h,false ).convertToImage();

  //img=grabFrameBuffer();



}

void CGlVisorBase::Initialize()
{

  m_molecule=m_molecules.back();
  if ( m_molecules.size() )
    m_current=m_molecules.size()-1;
  else
    m_current=0;

  coordinate c1,c2;
  m_molecules.back().GetBox ( c1,c2 );

  coordinate c=c1-c2;
  float s[3];
  s[0]=1.5*fabs ( c.x );
  s[1]=1.5*fabs ( c.y );
  s[2]=1.5*fabs ( c.z );

  float max=0;
  for ( int i=0;i<3;++i )
  {
    if ( s[i] > max ) max=s[i];
  }

  if ( max > m_maxdepth )
    m_scale= m_maxdepth / max;


}

void CGlVisorBase::OnChangeGraphMode ( int mode )
{
  std::cout << "graph mode is " << mode <<  std::endl;
  m_graphmode=static_cast<GraphMode> ( mode );
  for ( int i=0;i<m_rendermenu->count();++i )
  {
    m_rendermenu->setItemChecked ( i,false );
  }
  m_rendermenu->setItemChecked ( mode,true );
  update();
}



Q3PopupMenu* CGlVisorBase::GetMenu() { return static_cast<Q3PopupMenu*> ( m_optionsmenu ); }
