
/***************************************************************************
 *   Copyright (C) 2004 by Armando Navarro-Vazquez                         *
 *   qoajnv@usc.es                                                         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


#include <kmessagebox.h>
#include <kinputdialog.h>
#include <kdebug.h>

#include <sstream>
#include <iomanip>
#include <algorithm>

#include <qclipboard.h>
#include <qapplication.h>
#include <q3popupmenu.h>
#include <qtimer.h>
//Added by qt3to4:
#include <QMouseEvent>
#include <QWheelEvent>

#include "kryovisor.h"
#include "qtimeropenmenu.h"
#include "aceswriter.h"
#include "animation.h"
#include "gl2ps.h"
#include "tokenizer.h"
#include "xyzwriter.h"
#include "cpmdwriter.h"
#include "qrenumberoptionsdialog.h"
#include "nmrhandler.h"

KryoVisor::KryoVisor ( QWidget* parent) : CGlVisorBase ( parent )
{
   kDebug() << "KryoVisor" << endl;
  m_selectionmode=eNone;
  m_bshowdipole=false;
  m_bshowcell=false;
  m_bshowdistances=true;
  m_bshowespcharges=false;

  Q3PopupMenu* menu = GetMenu();
  m_exportmenu= new Q3PopupMenu ( this,"exportmenu" );


  int nitems=menu->count();
  menu->insertItem ( "Show Dipole Moment",this,SLOT ( OnShowDipole() ),0,nitems++ );
  menu->insertItem ( "Show Cell",this,SLOT ( OnShowCell() ),0,nitems++ );
  m_distancemenu= new Q3PopupMenu ( this );
  menu->insertItem ( "Distances",m_distancemenu,0,nitems++ );
  m_distancemenu->insertItem ( "Show Distances",this,SLOT ( OnShowDistances() ),0,0 );
  m_distancemenu->insertItem ( "Clear Distances",this,SLOT ( OnClearDistances() ),0,1 );
  m_chargemenu = new Q3PopupMenu ( this );
  menu->insertItem ( "Charges", m_chargemenu,0,nitems++ );
  m_chargemenu->insertItem ( "Show ESP Charges",this,SLOT ( OnShowESPCharges() ),0,0 );

  menu->insertItem ( "Export",m_exportmenu,0,nitems++ );
  m_exportmenu->insertItem ( "XYZ",this,SLOT ( OnExportXYZ() ),0,0 );
  m_exportmenu->insertItem ( "Gaussian Input",this,SLOT ( OnExportGaussian() ),0,1 );
  m_exportmenu->insertItem ( "ACES input",this,SLOT ( OnExportACES() ),0,2 );
  m_exportmenu->insertItem ( "CPMD input",this,SLOT ( OnExportCPMD() ), 0,3 );
  Q3PopupMenu* toolsmenu = new Q3PopupMenu ( this );
  menu->insertItem ( "Tools",toolsmenu,0,6 );

  toolsmenu->insertItem ( "Enantiomerize",this,SLOT ( OnEnantiomerize() ),0,0 );
  toolsmenu->insertItem ( "Recalculate Connectivity",this,SLOT ( OnRecalculateConnectivity() ),0,1 );
  toolsmenu->insertItem ( "Move to Centroid",this,SLOT ( OnMoveToCentroid() ),0,2 );
  toolsmenu->insertItem ( "Add Methyl",this,SLOT ( OnAddGroup() ), 0,3 );

  //m_optionsmenu->setItemChecked(0,m_bshownumbers);
  menu->setItemChecked ( menu->count(),m_bshowdipole );
  menu->setItemChecked ( menu->count() +1,m_bshowdistances );

  menu->setCheckable ( true );
  m_framespeed=100;
  m_screencontrols.resize ( 6 );

}


KryoVisor::~KryoVisor()
{}

void KryoVisor::SetSelectionMode ( eSelectionMode mode )
{
  m_selectionmode=mode;
}

void KryoVisor::OnEnantiomerize()
{
  std::vector<CAtom>::iterator it;
  for ( it=m_molecule.m_atom.begin();it!=m_molecule.m_atom.end();++it )
  {
    it->c*=-1;
  }
  size_t i,j;
  for ( i=0;i<m_molecule.GetForces().size();++i )
  {
    m_molecule.GetForces() ( i ) *=-1;
  }
  for ( i=0;i<m_molecule.GetHessian().rows();++i )
    for ( j=0;j<m_molecule.GetHessian().columns();++j )
      m_molecule.GetHessian() ( i,j ) *=-1;

  update();
}

void KryoVisor::OnRecalculateConnectivity()
{
  m_molecule.SetConexions();
  update();
}

void KryoVisor::OnMoveToCentroid()
{
  m_molecule.CalculateMassCenter();
  update();
}
void KryoVisor::OnSelection ( QMouseEvent* e )
{
  if ( m_molecule.m_atom.size() && m_selectionmode != eNone )
  {

    //selection code

    static GLuint selectBuff[64];
    GLint hits, viewport[4];
    glSelectBuffer ( 64,selectBuff );

    glGetIntegerv ( GL_VIEWPORT,viewport );

    glMatrixMode ( GL_PROJECTION );
    glPushMatrix();

    glRenderMode ( GL_SELECT );


    glLoadIdentity();




    gluPickMatrix ( e->x(),viewport[3]-e->y(),5,5,viewport );


    SetupPerspective();

    //gluPerspective(45.0,m_aspect,1.0,60.0);

    //glTranslatef(0.0,0.0,-5.0f);

    //We have to multiply here by the rotation acumulated matrix
    glMultMatrixf ( ( GLfloat * ) m_transform );

    RenderScene ( GL_SELECT );
    hits=glRenderMode ( GL_RENDER );

    if ( hits!=0 )
    {

      unsigned int i;
      GLuint names, *ptr, minZ,*ptrNames, numberOfNames;

      ptr = ( GLuint * ) selectBuff;
      minZ = 0xffffffff;
      for ( i = 0; i < hits; i++ )
      {
        names = *ptr;
        ptr++;
        if ( *ptr < minZ )
        {
          numberOfNames = names;
          minZ = *ptr;
          ptrNames = ptr+2;
        }

        ptr += names+2;
      }

      ptr = ptrNames;

      int natom=*ptr;
      std::cerr << "Selected atom=" << natom + 1 << std::endl;
      bool b=m_molecule.m_atom[natom].IsSelected();
      m_molecule.m_atom[natom].Select ( !b );

      std::vector<int>::iterator it=std::find ( m_selectedatoms.begin(), m_selectedatoms.end(), natom );
      if ( it!= m_selectedatoms.end() )
      {
        m_selectedatoms.erase ( it );

      }
      else
      {
        if ( m_selectionmode != eRenumberHeavy || m_molecule.m_atom[natom].name != "H" )
          m_selectedatoms.push_back ( natom );
        else
          m_molecule.m_atom[natom].Select ( false );
      }

      std::string name=m_molecule.m_atom[natom].name;

      switch ( m_selectionmode )
      {
        case eDistance:
          if ( m_selectedatoms.size() == 2 )
          {
            QString str;
            str.sprintf ( "%.3f",m_molecule.GetDistance ( m_selectedatoms[0],m_selectedatoms[1] ) );
            m_rpairs.push_back ( CMolecule::pair ( m_selectedatoms[0],m_selectedatoms[1],str.latin1() ) );
            emit text ( str );
            OnResetSelection ( false );
          }
          break;
        case eAngle:
          if ( m_selectedatoms.size() == 3 )
          {
            QString str;
            str.sprintf ( "Angle(%s%d-%s%d-%s%d)=%.2f",
                          m_molecule.m_atom[m_selectedatoms[0]].name.c_str(),
                          m_selectedatoms[0]+1,
                          m_molecule.m_atom[m_selectedatoms[1]].name.c_str(),
                          m_selectedatoms[1]+1,
                          m_molecule.m_atom[m_selectedatoms[2]].name.c_str(),
                          m_selectedatoms[2]+1,
                          m_molecule.GetAngle ( m_selectedatoms[0],m_selectedatoms[1],m_selectedatoms[2],true ) );

            m_angletriads.push_back ( CMolecule::triad ( m_selectedatoms[0],m_selectedatoms[1],m_selectedatoms[2],str.latin1() ) );
            //str= "Angle=" +str;
            emit text ( str );
            OnResetSelection ( false );
          }
          break;
        case eDihedral:
          if ( m_selectedatoms.size() == 4 )
          {
            QString str;
            str.sprintf ( "Angle(%s%d-%s%d-%s%d-%s%d)=%.2f",
                          m_molecule.m_atom[m_selectedatoms[0]].name.c_str(),
                          m_selectedatoms[0]+1,
                          m_molecule.m_atom[m_selectedatoms[1]].name.c_str(),
                          m_selectedatoms[1]+1,
                          m_molecule.m_atom[m_selectedatoms[2]].name.c_str(),
                          m_selectedatoms[2]+1,
                          m_molecule.m_atom[m_selectedatoms[3]].name.c_str(),
                          m_selectedatoms[3]+1,
                          m_molecule.GetDihedral ( m_selectedatoms[0],m_selectedatoms[1],m_selectedatoms[2],m_selectedatoms[3],true ) );

            m_dihedralquads.push_back ( CMolecule::quad ( m_selectedatoms[0],m_selectedatoms[1],m_selectedatoms[2],
                                        m_selectedatoms[3],str.latin1() ) );

            emit text ( str );
            OnResetSelection ( false );
          }
          break;
        case eSetDihedral:
          if ( m_selectedatoms.size() == 4 )
          {
            double angle=KInputDialog::getDouble ( "", "Set Dihedral angle (degrees)", 0, -360.0, 360.0,0.1,1 );
            m_molecule.SetDihedral ( m_selectedatoms[0],m_selectedatoms[1],m_selectedatoms[2],m_selectedatoms[3],angle );
            OnResetSelection ( false );
            updateGL();
          }
          break;
        case eGromacs3dout:
          if ( m_selectedatoms.size() == 4 )
          {
            Gromacs3dout ( m_selectedatoms[0],m_selectedatoms[1],m_selectedatoms[2],m_selectedatoms[3] );
          }
          break;
        case eAnglePlanes:
          if ( m_selectedatoms.size() == 6 )
          {
            QString str;
            str.sprintf ( "Angle[%s%d-%s%d-%s%d,%s%d-%s%d-%s%d]=%.1f",
                          m_molecule.m_atom[m_selectedatoms[0]].name.c_str(),
                          m_selectedatoms[0]+1,
                          m_molecule.m_atom[m_selectedatoms[1]].name.c_str(),
                          m_selectedatoms[1]+1,
                          m_molecule.m_atom[m_selectedatoms[2]].name.c_str(),
                          m_selectedatoms[2]+1,
                          m_molecule.m_atom[m_selectedatoms[3]].name.c_str(),
                          m_selectedatoms[3]+1,
                          m_molecule.m_atom[m_selectedatoms[4]].name.c_str(),
                          m_selectedatoms[4]+1,
                          m_molecule.m_atom[m_selectedatoms[5]].name.c_str(),
                          m_selectedatoms[5]+1,
                          m_molecule.GetAnglePlanes ( m_selectedatoms,true ) );

            emit text ( str );

            OnResetSelection ( false );
          }
          break;
        case eRotateBond:
          if ( m_selectedatoms.size() >2 )
          {
            size_t lastatom=m_selectedatoms.back();
            OnResetSelection ( false );
            m_selectedatoms.push_back ( lastatom );
            m_molecule.m_atom[lastatom].Select ( true );
            update();
          }
          break;
        case eDeleteBond:
          if ( m_selectedatoms.size() == 2 )
          {
            m_molecule.DeleteBond ( m_selectedatoms[0],m_selectedatoms[1] );
            OnResetSelection ( false );
          }
          break;
        case eCreateBond:

          if ( m_selectedatoms.size() == 2 )
          {
            m_molecule.CreateBond ( m_selectedatoms[0],m_selectedatoms[1] );
            OnResetSelection ( false );
          }
          break;
        case eAddGroup:
          if ( m_molecule.m_atom[m_selectedatoms[0]].name=="H" )
          {
            ChangeHByMethyl ( m_selectedatoms[0] );
            OnResetSelection ( false );
          }
          break;
        case eRenumberAll:
        {
          if ( m_selectedatoms.size() == m_molecule.m_atom.size() )
          {
            RenumberAtoms();
            SetSelectionMode ( eNone );
            OnResetSelection ( false );
          }
        }
        break;
        case eRenumberHeavy:
        {
          if ( m_selectedatoms.size() == m_nheavyatoms )
          {
            RenumberAtoms();
            SetSelectionMode ( eNone );
            OnResetSelection ( false );
          }
        }
        break;
        case eJ:

          if ( m_selectedatoms.size() == 2 )
          {
            std::vector<CMolecule::Coupling>::const_iterator it;

            for ( it=m_molecule.GetCouplings().begin();it!=m_molecule.GetCouplings().end();++it )
            {
              if ( ( m_selectedatoms.at ( 0 ) == it->I() && m_selectedatoms.at ( 1 ) == it->J() ) ||
                      ( m_selectedatoms.at ( 1 ) == it->I() && m_selectedatoms.at ( 0 ) == it->J() ) )
              {
                m_jpairs.push_back ( it );
                QString str;
                str.sprintf ( "%.2f",it->Value() );
                str= "J="+str;
                emit text ( str );
              }


            }
            OnResetSelection ( false );
          }
          break;
        default:
          break;

      }

    }

    glMatrixMode ( GL_PROJECTION );
    glPopMatrix();

    glMatrixMode ( GL_MODELVIEW );

    if ( hits )
      updateGL();
  }
}

void KryoVisor::OnResetSelection ( bool repaint /*=true*/ )
{
  m_selectedatoms.clear();
  m_molecule.ResetSelection();
  if ( repaint )
    updateGL();
}



// void KryoVisor::RenderButtons()
// {
//   glDisable ( GL_LIGHTING );   // so lighting models are used
//   // glDisable(GL_LIGHT0);     // we'll use LIGHT0
//   // glDisable(GL_LIGHT1);
//   // glEnable(GL_LIGHT2);
//   glMatrixMode ( GL_PROJECTION );
//   glPushMatrix();
//   glLoadIdentity ();
//   gluOrtho2D ( 0, this->rect().width(), 0, this->rect().height() );
//   int left=this->rect().left();
//   int bottom=this->rect().bottom();
//   int height=this->rect().height();
//   int top=this->rect().top();
//
//   glDisable ( GL_DEPTH_TEST );
//   glMatrixMode ( GL_MODELVIEW );
//   glPushMatrix();
//   glLoadIdentity();
//
//   glColor3f ( 1,0,0 );
//   glBegin ( GL_TRIANGLES );
//   glVertex2f ( left+10,height-15 );
//   glVertex2f ( left+20,height-10 );
//   glVertex2f ( left+20,height-20 );
//   m_screencontrols[0].setCoords ( left+10,top+10,left+20,top+20 );
//   glEnd();
//
//
//   glColor3f ( 0,1,0 );
//   glBegin ( GL_TRIANGLES );
//   glVertex2f ( left+40,height-15 );
//   glVertex2f ( left+50,height-10 );
//   glVertex2f ( left+50,height-20 );
//   m_screencontrols[1].setCoords ( left+40,top+10,left+50,top+20 );
//   glEnd();
//
//   glColor3f ( 1,0,0 );
//   glBegin ( GL_QUADS );
//   glVertex2f ( left+70,height-10 );
//   glVertex2f ( left+70,height-20 );
//   glVertex2f ( left+80,height-20 );
//   glVertex2f ( left+80,height-10 );
//   m_screencontrols[2].setCoords ( left+70,top+10,left+80,top+20 );
//   glEnd();
//
//   glColor3f ( 0,1,0 );
//   glBegin ( GL_QUADS );
//   glVertex2f ( left+100,height-10 );
//   glVertex2f ( left+100,height-20 );
//   glVertex2f ( left+110,height-20 );
//   glVertex2f ( left+110,height-10 );
//   m_screencontrols[3].setCoords ( left+100,top+10,left+110,top+20 );
//   glEnd();
//
//   glColor3f ( 0,1,0 );
//   glBegin ( GL_TRIANGLES );
//   glVertex2f ( left+130,height-20 );
//   glVertex2f ( left+130,height-10 );
//   glVertex2f ( left+140,height-15 );
//   m_screencontrols[4].setCoords ( left+130,top+10,left+140,top+20 );
//   glEnd();
//
//   glColor3f ( 1,0,0 );
//   glBegin ( GL_TRIANGLES );
//   glVertex2f ( left+160,height-20 );
//   glVertex2f ( left+160,height-10 );
//   glVertex2f ( left+170,height-15 );
//   m_screencontrols[5].setCoords ( left+160,top+10,left+170,top+20 );
//   glEnd();
//
//   RenderScreenText();
//
//
//   glMatrixMode ( GL_PROJECTION );
//   glPopMatrix();
//   glMatrixMode ( GL_MODELVIEW );
//   glPopMatrix();
//   glEnable ( GL_DEPTH_TEST );
//   glEnable ( GL_LIGHTING );
// }

void KryoVisor::RenderScreenText()
{
  std::stringstream label;
  label << "#" << m_current+1 << " of " << m_molecules.size();
  QColor col ( QColor ( 255,255,255 ) );
  qglColor ( col );
  renderText ( rect().left() +30,rect().bottom() - 30,QString ( label.str().c_str() ),Font() );
}

void KryoVisorOpt::RenderScreenText()
{
  KryoVisor::RenderScreenText();
}

void KryoVisorFreq::RenderScreenText()
{
  std::stringstream label;
  label << "Current active vibrational mode: #" << m_animation->GetMode() +1;
  QString str ( label.str().c_str() );
  QColor col ( QColor ( 255,255,255 ) );
  qglColor ( col );
  renderText ( rect().left() +30,rect().bottom() - 30,str,Font() );
}


KryoVisorFreq::~KryoVisorFreq()
{
  m_animation->Stop();
}

void KryoVisor::OnFirstFrame()
{
  m_molecule=m_molecules.front();
  m_current=0;
  emit selectedPoint ( m_current );
  RefreshDistances();
}

void KryoVisor::OnLastFrame()
{
  m_molecule=m_molecules.back();
  m_current=m_molecules.size()-1;
  emit selectedPoint ( m_current );
  RefreshDistances();
}

void KryoVisor::OnPreviousFrame()
{
  m_current--;
  if ( m_current < 0 )
    m_current=0;
  m_molecule=m_molecules[m_current];
  emit selectedPoint ( m_current );
  RefreshDistances();

}

void KryoVisor::OnNextFrame()
{
  m_current++;
  if ( m_current > static_cast<int> ( m_molecules.size()-1 ) )
    m_current=m_molecules.size()-1;
  m_molecule=m_molecules[m_current];
  emit selectedPoint ( m_current );
  RefreshDistances();
}
// void KryoVisor::selectButton ( const QPoint& pos )
// {
//   std::vector<QRect>::iterator q;
//
//   int i;
//   for ( q=m_screencontrols.begin(),i=0;q!=m_screencontrols.end();q++,i++ )
//   {
//     if ( q->contains ( pos ) )
//       break;
//   }
//
//   if ( i > 5 )
//     return;
//
//   switch ( i )
//   {
//     case 0:
//       m_molecule=m_molecules.front();
//       m_current=0;
//       emit selectedPoint ( m_current );
//       break;
//     case 4:
//       m_current++;
//       if ( m_current > static_cast<int> ( m_molecules.size()-1 ) )
//         m_current=m_molecules.size()-1;
//       m_molecule=m_molecules[m_current];
//       emit selectedPoint ( m_current );
//       break;
//     case 1:
//       m_current--;
//       if ( m_current < 0 )
//         m_current=0;
//       m_molecule=m_molecules[m_current];
//       emit selectedPoint ( m_current );
//       break;
//     case 5:
//       m_molecule=m_molecules.back();
//       m_current=m_molecules.size()-1;
//       emit selectedPoint ( m_current );
//       break;
//     case 2:
//       OnStopMovie();
//       break;
//     case 3:
//       OnStartMovie();
//       break;
//     default:
//       break;
//   }
//
//
// }

void KryoVisor::RefreshDistances()
{
  std::vector<CMolecule::pair>::iterator it;

  for ( it=m_rpairs.begin();it!=m_rpairs.end();it++ )
  {
    QString str;
    str.sprintf ( "%.3f",m_molecule.GetDistance ( it->i,it->j ) );
    it->value=str.latin1();
  }

  update();
}

void KryoVisor::mousePressEvent ( QMouseEvent* e )
{
  CGlVisorBase::mousePressEvent ( e );
  switch ( e->button() )
  {
    case Qt::LeftButton:
      OnSelection ( e );
      break;
    case Qt::RightButton:
      break;
    default:
      break;
  }
}


void KryoVisor::wheelEvent ( QWheelEvent* e )
{
  if ( m_selectionmode==eRotateBond && m_selectedatoms.size() ==2 )
  {
    m_molecule.RotateBond ( m_selectedatoms[0],m_selectedatoms[1],-e->delta() );
    updateGL();
  }
  else
  {
    CGlVisorBase::wheelEvent ( e );
  }

}

void KryoVisor::mouseMoveEvent ( QMouseEvent* e )
{
  if ( m_selectionmode==eRotateBond && ( e->state() & Qt::ControlButton ) )
  {

    int  delta= ( m_oldpoint.y()-e->pos().y() ) *10;
    m_molecule.RotateBond ( m_selectedatoms[0],m_selectedatoms[1],delta );
    updateGL();
    m_oldpoint=e->pos();
  }
  else
  {
    CGlVisorBase::mouseMoveEvent ( e );
  }
}
void KryoVisor::OnMeasureDistances ( bool b )
{

  if ( b )
    SetSelectionMode ( eDistance );
  else
    SetSelectionMode ( eNone );
  OnResetSelection();

}

void KryoVisor::OnAddGroup()
{


  SetSelectionMode ( eAddGroup );

}

void KryoVisor::OnRenumbering ( bool b )
{
  if ( b )
  {
    QRenumberOptionsDialog rdlg;
    if ( rdlg.exec() == QDialog::Accepted )
    {

      switch ( rdlg.Mode() )
      {
        case QRenumberOptionsDialog::AllAtoms:
          SetSelectionMode ( eRenumberAll );
          std::cout << "Renumber all atoms" << std::endl;
          break;
        case QRenumberOptionsDialog::HeavyAtoms:
          SetSelectionMode ( eRenumberHeavy );
          m_nheavyatoms=m_molecule.CountHeavyAtoms();
          std::cout << "Renumbering " << m_nheavyatoms << std::endl;
          break;
      }
    }
  }
  else
    SetSelectionMode ( eNone );

  OnResetSelection();
}

void KryoVisor::OnSetDihedral ( bool b )
{
  if ( b )
  {
    SetSelectionMode ( eSetDihedral );
  }
  else
    SetSelectionMode ( eNone );

  OnResetSelection();
}

void KryoVisor::Gromacs3dout ( int i, int j, int k, int l )
{
  coordinate c1=m_molecule.m_atom[i].c;
  coordinate c2=m_molecule.m_atom[j].c;
  coordinate c3=m_molecule.m_atom[k].c;
  coordinate c4=m_molecule.m_atom[l].c;

  coordinate rij=c2-c1;
  coordinate rjk=c3-c2;
  coordinate ril=c4-c1;
  coordinate rop=c2^c3;
  //output in nanometers
  float a=0.1*ril*rij/rij.norm();
  float b=0.1*ril*rjk/rjk.norm();
  float c=0.1*ril*rop/rop.norm();
  std::stringstream ss;
  ss << i+1 << " " << j+1 << " " << k+1 << " " << l+1 << " " << a << " " << b << " " <<  c << std::endl;
  QString s ( ss.str().c_str() );
  KMessageBox::information ( this, s );

}
void KryoVisor::OnGromacs3dout ( bool b )
{
  if ( b )
  {
    SetSelectionMode ( eGromacs3dout );
  }
  else
    SetSelectionMode ( eNone );

  OnResetSelection();
}

void KryoVisor::OnMeasureAngles ( bool b )
{
  if ( b )
    SetSelectionMode ( eAngle );
  else
    SetSelectionMode ( eNone );

  OnResetSelection();
}

void KryoVisor::OnMeasureDihedrals ( bool b )
{
  if ( b )
    SetSelectionMode ( eDihedral );
  else
    SetSelectionMode ( eNone );
  OnResetSelection();

}

void KryoVisor::OnMeasureAngleBetweenPlanes ( bool b )
{
  if ( b )
    SetSelectionMode ( eAnglePlanes );
  else
    SetSelectionMode ( eNone );
  OnResetSelection();
}
void KryoVisor::OnRotateBond ( bool b )
{
  if ( b )
    SetSelectionMode ( eRotateBond );
  else
    SetSelectionMode ( eNone );
  OnResetSelection();
}

void KryoVisor::OnDeleteBond ( bool b )
{
  if ( b )
    SetSelectionMode ( eDeleteBond );
  else
    SetSelectionMode ( eNone );
  OnResetSelection();
}

void KryoVisor::ChangeHByMethyl ( int atom )
{
  CAtom& replaced=m_molecule.m_atom.at ( atom );
  CAtom& connected=m_molecule.m_atom.at ( replaced.conexion[0].index );
  CAtom carbon, h1, h2, h3;
  carbon.c=coordinate ( 0,0,1.400 ) + connected.c;

  h1.c=coordinate ( 0,1.034,1.776 ) + connected.c;
  h2.c=coordinate ( 0.895,-0.517,1.776 ) + connected.c;
  h3.c=coordinate ( -0.895,-0.517,1.776 ) + connected.c;

  carbon.name="C";
  h1.name="H";
  h2.name="H";
  h3.name="H";


  if ( replaced.conexion.size() != 1 )
  {
    std::cerr << "You cannot replace this atom";
    return;
  }

  coordinate vec=replaced.c-connected.c;
  //OK do rotation
  coordinate rotaxis= ( carbon.c-connected.c ) ^vec;
  //ok get angle
  double angle=coordinate::GetAngleVector ( carbon.c-connected.c,vec );
  carbon.c=coordinate::RotAroundAxis ( connected.c,rotaxis+connected.c,carbon.c,angle );
  h1.c=coordinate::RotAroundAxis ( connected.c,rotaxis+connected.c,h1.c,angle );
  h2.c=coordinate::RotAroundAxis ( connected.c,rotaxis+connected.c,h2.c,angle );
  h3.c=coordinate::RotAroundAxis ( connected.c,rotaxis+connected.c,h3.c,angle );

  std::vector<CAtom>::iterator it=m_molecule.m_atom.begin();
  for ( size_t i=0;i<m_molecule.m_atom.size();++i,++it )
  {
    if ( i== atom ) break;
  }

  m_molecule.m_atom.erase ( it );
  m_molecule.m_atom.push_back ( carbon );
  m_molecule.m_atom.push_back ( h1 );
  m_molecule.m_atom.push_back ( h2 );
  m_molecule.m_atom.push_back ( h3 );

  m_molecule.SetConexions();
  update();
}

void KryoVisor::OnCreateBond ( bool b )
{
  if ( b )
    SetSelectionMode ( eCreateBond );
  else
    SetSelectionMode ( eNone );
  OnResetSelection();
}


void KryoVisor::OnExportACES()
{

  QClipboard* c=QApplication::clipboard();

  AcesWriter awr ( m_molecule );
  std::stringstream s;

  s << awr;



  c->setText ( s.str().c_str() );

}

void KryoVisor::OnExportXYZ()
{
  QClipboard* c=QApplication::clipboard();
  XYZWriter w ( m_molecule );
  std::stringstream s;
  s << w;
  c->setText ( s.str().c_str() );

}

void KryoVisor::OnExportCPMD()
{
  QClipboard* c=QApplication::clipboard();
  CPMDWriter w ( m_molecule );
  std::stringstream s;
  s << w;
  c->setText ( s.str().c_str() );
}
void KryoVisor::OnExportGaussian ( bool withhessian )
{
  QClipboard* c=QApplication::clipboard();
  if ( c == NULL )
  {
    std::cerr << "Could not open clipboard";
  }

  std::stringstream s;
  s << 0 <<"," << 1 << std::endl;
  if ( !withhessian )
  {
    std::cout << "geometry exported to clipboard" << std::endl;
    s << m_molecule << std::endl;
    s << std::endl;
    c->setText ( s.str().c_str() );
    return;
  }

  std::vector<CAtom>::iterator it;
  std::vector<coordinate>::const_iterator ct=m_molecule.InputOrientation().begin();
  for ( it=m_molecule.m_atom.begin();it!=m_molecule.m_atom.end();++it,++ct )
  {

    s << std::setiosflags ( std::ios::fixed ) << std::setw ( 3 ) << setiosflags ( std::ios::left ) << it->name
    << std::setw ( 10 )  << std::resetiosflags ( std::ios::left )
    << std::setiosflags ( std::ios::right ) << std::setiosflags ( std::ios::fixed ) << std::setprecision ( 6 )
    << ct->x <<
    std::setw ( 10 ) <<ct->y
    << std::setw ( 10 ) << ct->z
    << std::resetiosflags ( std::ios::right ) << std::endl;
  }
  s << std::endl << std::endl;
  s << std::resetiosflags ( std::ios::fixed );

  s << std::setiosflags ( std::ios::fixed );
  s << std::setiosflags ( std::ios::uppercase ) << std::setprecision ( 8 );


  IMArray<double>& forces=m_molecule.GetForces();
  IBArray<double>& hessian=m_molecule.GetHessian();

  if ( forces.size() )
  {
    size_t i=0;
    int j;
    while ( i < forces.size() )
    {
      for ( j=0;j<6;++j )
      {
        s << std::setw ( 12 )  << forces ( i++ );
        if ( i == forces.size() )
          break;
      }
      s << std::endl;

    }

  }

  if ( hessian.size() )
  {
    size_t i,j;
    short k=0;
    for ( i=0;i<hessian.rows();i++ )
      for ( j=0;j<=i;j++ )
      {
        s << std::setw ( 12 ) << hessian ( i,j );
        if ( ++k > 5 )
        {
          s << std::endl;
          k=0;
        }
      }

  }
  s << std::endl;
  c->setText ( s.str().c_str() );

}

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

  if ( m_bshowdistances )
  {
    std::vector<CMolecule::pair>::iterator it;
    glLineWidth ( 1 );
    glEnable ( GL_LINE_STIPPLE );
    glLineStipple ( 1,0xCCCC );
    glDisable ( GL_LIGHTING );
    for ( it=m_rpairs.begin();it!=m_rpairs.end();it++ )
    {
      //draw negative values in blue
      if ( it->value.at ( 0 ) == '-' )
        glColor3f ( 0.5,0.5,0.9 );
      else
        glColor3f ( 0.9,0.5,0.5 );

      glBegin ( GL_LINES );
      glVertex3f ( m_molecule.m_atom[it->i].c.x,
                   m_molecule.m_atom[it->i].c.y,
                   m_molecule.m_atom[it->i].c.z );

      glVertex3f ( m_molecule.m_atom[it->j].c.x,
                   m_molecule.m_atom[it->j].c.y,
                   m_molecule.m_atom[it->j].c.z );

      coordinate middle=coordinate::GetMiddle ( m_molecule.m_atom[it->i].c,m_molecule.m_atom[it->j].c );
      glEnd();

      //write two spaces to leave some separation between
      //number and atom
      QString str=it->value.c_str();
      QString number="  " +str;

      //m_visor->renderText ( middle.x,middle.y, middle.z, number,glFont);
      /*   if(1)
         {
           std::stringstream st;
           st.flags(std::ios::fixed);
           st.precision(3);
           st << " (" << m_molecule.GetDistance(it->i,it->j) << ")";
           QString str1(st.str().c_str());
           str+=str1;
         }*/
      glPushMatrix();
      glTranslatef ( middle.x,middle.y-0.25,middle.z );
      RenderText ( str,0,0,0 );
      glPopMatrix();

    }
    glDisable ( GL_LINE_STIPPLE );
    glEnable ( GL_LIGHTING );
  }
  const int spheres=16;
  if ( m_bshowdipole )
  {
    /*static float icolor[4];
    icolor[0]=0.9f;
    icolor[1]=0.3f;
    icolor[2]=0.3f;
    icolor[3]=0.5f;*/

    if ( m_molecule.m_dipole.norm() > 0.1 )
    {
      GLUquadricObj* quadric= gluNewQuadric();
      float color[4];
      color[0]=0.4;
      color[1]=0.4;
      color[2]=0.9;
      color[3]=0.5;
      glMaterialfv ( GL_FRONT_AND_BACK, GL_AMBIENT, color );
      glMaterialfv ( GL_FRONT_AND_BACK, GL_DIFFUSE, color );
      glPushMatrix();
      {
        coordinate zaxis ( 0,0,1 );
        coordinate normal=zaxis^m_molecule.m_dipole;
        float angle=coordinate::GetAngleVector ( zaxis,m_molecule.m_dipole );
        glRotatef ( angle*180/M_PI,normal.x,normal.y,normal.z );
        float height=m_molecule.m_dipole.norm() *0.3;
        gluCylinder ( quadric,0.05,0.05,height,spheres,spheres );
        glPushMatrix();
        {
          glTranslatef ( 0,0,height );
          //glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, icolor);
          //glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, icolor);

          gluCylinder ( quadric,0.09,0.001,0.2,spheres,spheres );

        }
        glPopMatrix();

        glPushMatrix();
        {
          glTranslatef ( 0,0,height/2 );
          glDisable ( GL_LIGHTING );

          QColor col ( QColor ( 255,255,255 ) );
          qglColor ( col );
          //glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
          //glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color);
          //glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color);

          //write two spaces to leave some separation between
          //number and atom
          QString dipole;
          dipole.sprintf ( "  %.3f debyes",m_molecule.m_dipole.norm() );


          RenderText ( dipole,0,0, 0 );
          glEnable ( GL_LIGHTING );
        }
        glPopMatrix();
      }
      glPopMatrix();

      gluDeleteQuadric ( quadric );
    }
  }

  if ( m_bshowcell )
  {
    if ( !m_molecule.Cell().empty() )
    {
      if ( mode == GL_RENDER )
      {
        glPushMatrix();
        coordinate c=m_molecule.Cell().at ( 0 ) +m_molecule.Cell().at ( 1 ) +m_molecule.Cell().at ( 2 );
        c=-0.5*c;
        glTranslatef ( c.x,c.y,c.z );
        glDisable ( GL_LIGHTING );
        std::cout << "show cell " << c << std::endl;
        std::vector<coordinate> cube=CalculateCellCube();
        glColor4f ( 0.7,0.7,0.95,0.15 );
        glBegin ( GL_QUADS );
        std::cout << cube[0] << "," << cube[1] << "," << cube[2] << std::endl;
        glVertexC ( cube[0] );
        glVertexC ( cube[1] );
        glVertexC ( cube[2] );
        glVertexC ( cube[3] );

        glVertexC ( cube[7] );
        glVertexC ( cube[6] );
        glVertexC ( cube[5] );
        glVertexC ( cube[4] );

        glVertexC ( cube[1] );
        glVertexC ( cube[2] );
        glVertexC ( cube[6] );
        glVertexC ( cube[5] );

        glVertexC ( cube[0] );
        glVertexC ( cube[3] );
        glVertexC ( cube[7] );
        glVertexC ( cube[4] );

        glVertexC ( cube[2] );
        glVertexC ( cube[3] );
        glVertexC ( cube[7] );
        glVertexC ( cube[6] );

        glVertexC ( cube[0] );
        glVertexC ( cube[1] );
        glVertexC ( cube[5] );
        glVertexC ( cube[4] );


        glEnd();
        glEnable ( GL_LIGHTING );
        glPopMatrix();
      }
    }
  }
  if ( m_selectionmode == eRenumberAll || m_selectionmode == eRenumberHeavy )
  {

    if ( mode == GL_RENDER )
    {

      glDisable ( GL_LIGHTING );

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

      static QFont glFont ( "Helvetica" );


      std::vector<int>::const_iterator it;
      unsigned int i=1;
      for ( it=m_selectedatoms.begin();it!=m_selectedatoms.end();++it,++i )
      {
        QString number="   ";
        glPushMatrix();
        CAtom& atom=m_molecule.m_atom[*it];
        glTranslatef ( atom.c.x,atom.c.y,atom.c.z );
        QString str;
        str.sprintf ( "%d",i );
        number+=str;
        renderText ( 0,0, 0, number,glFont );
        glPopMatrix();
      }

      glEnable ( GL_LIGHTING );
    }

  }

  if ( m_bshowespcharges )
  {
    QFont font ( "Helvetica" );
    const std::vector<double>* charges=m_molecule.Charges ( CMolecule::ESP );
    if ( charges )
    {
      std::vector<double>::const_iterator it;
      for ( size_t i=0;i<charges->size();++i )
      {
        glPushMatrix();
        CAtom& atom=m_molecule.m_atom[i];
        glTranslatef ( atom.c.x,atom.c.y,atom.c.z );
        QString str;
        str.sprintf ( "%.3f", ( *charges ) [i] );
        renderText ( 0,0,0,str,font );
        glPopMatrix();
      }
    }
  }
}



/*      6           7
  2           3
      4           5
   0---------1
*/
std::vector<coordinate> KryoVisor::CalculateCellCube()
{
  std::vector<coordinate> cube;
  cube.resize ( 8 );
  cube[0]=coordinate ( 0,0,0 );
  cube[1]=m_molecule.Cell().at ( 0 );
  cube[2]=m_molecule.Cell().at ( 0 ) +m_molecule.Cell().at ( 1 );
  cube[3]=m_molecule.Cell().at ( 1 );
  cube[4]=m_molecule.Cell().at ( 2 );
  cube[5]=m_molecule.Cell().at ( 0 ) +m_molecule.Cell().at ( 2 );
  cube[6]=m_molecule.Cell().at ( 0 ) +m_molecule.Cell().at ( 1 ) +m_molecule.Cell().at ( 2 );
  cube[7]=m_molecule.Cell().at ( 1 ) +m_molecule.Cell().at ( 2 );
  return cube;
}
void KryoVisor::OnShowDistances()
{

  m_bshowdistances=!m_distancemenu->isItemChecked ( 0 );
  m_distancemenu->setItemChecked ( 0,m_bshowdistances );
  update();
}

void KryoVisor::OnShowESPCharges()
{

  m_bshowespcharges=!m_chargemenu->isItemChecked ( 0 );
  m_chargemenu->setItemChecked ( 0,m_bshowespcharges );
  update();
}

void KryoVisor::OnClearDistances()
{
  m_rpairs.clear();
  update();
}

void KryoVisor::Export ( const QString& file, const QString& mode )
{
  std::cout << "Exporting in " << mode.ascii() << " format to file " << file.ascii() << std::endl;
  char *oldlocale = setlocale ( LC_NUMERIC, "C" );
  FILE* fp=fopen ( file.ascii(),"wb" );
  if ( !fp )
    return;
  GLint buffsize=0, state =GL2PS_OVERFLOW;
  GLint viewport[4];
  viewport[0]=0;
  viewport[1]=0;
  viewport[2]=640;
  viewport[3]=480;
  //glGetIntegerv(GL_VIEWPORT, viewport);

  StringTokenizer path ( std::string(file.toLatin1()),"." );
  StringTokenizer::toupper ( path.back() );
  int gmode=GL2PS_PS;

  if ( mode == "EPS" )
    gmode=GL2PS_EPS;
  if ( mode =="PS" )
    gmode =GL2PS_PS;
  if ( mode == "SVG" )
    gmode=GL2PS_SVG;

  while ( state == GL2PS_OVERFLOW )
  {

    buffsize+=1024*1024;
    gl2psBeginPage ( file.ascii(), "KryoMol", viewport,
                     gmode , GL2PS_BSP_SORT, GL2PS_DRAW_BACKGROUND | GL2PS_SILENT | GL2PS_BEST_ROOT ,
                     GL_RGBA, 0, NULL, 0, 0, 0, buffsize,
                     fp,file.ascii() );
    RenderScene();
    state=gl2psEndPage();

  }
  fclose ( fp );
  setlocale ( LC_NUMERIC, oldlocale );

  std::cout << "Exporting finished" << std::endl;
}

void KryoVisor::OnShowDipole()
{
  m_bshowdipole=!GetMenu()->isItemChecked ( 4 );
  GetMenu()->setItemChecked ( 4,m_bshowdipole );
  update();
}

void KryoVisor::OnShowCell()
{
  m_bshowcell=!GetMenu()->isItemChecked ( 5 );
  GetMenu()->setItemChecked ( 5,m_bshowcell );
  update();
}


KryoVisorOpt::KryoVisorOpt ( QWidget* parent ) : KryoVisor ( parent )
{
  m_forcescale=1;

  m_bshowforces=false;
  Q3PopupMenu* menu = GetMenu();
  int nitems=menu->count();
  menu->insertSeparator ( nitems++ );

  m_bshowforces=false;
  menu->insertItem ( "Show Forces",this,SLOT ( OnShowForces() ),0,nitems++ );
  menu->setItemChecked ( nitems,m_bshowforces );
}

void KryoVisor::Initialize()
{
  CGlVisorBase::Initialize();
  m_timer= new QTimer ( this );
  connect ( m_timer,SIGNAL ( timeout() ),this,SLOT ( OnChangeFrame() ) );

}

void KryoVisorOpt::Initialize()
{
  KryoVisor::Initialize();

  CMolecule& firstmolecule=m_molecules.front();
  float maxforce=0.000001;
  std::vector<CAtom>::const_iterator it;
  for ( it=firstmolecule.m_atom.begin();it!=firstmolecule.m_atom.end();++it )
  {
    float force=it->Gradient().norm();
    if ( force > maxforce )
      maxforce=force;
  }
  //Maximum cylinder should be about 3 A
  m_forcemagnitude=3/maxforce;
}

void KryoVisorOpt::OnShowForces()
{
  m_bshowforces=!m_bshowforces;
  Q3PopupMenu* menu = GetMenu();
  menu->setItemChecked ( 8,m_bshowforces );
  update();
}

void KryoVisor::OnSelectPoint ( int current )
{
  if ( current < 0 || current >= m_molecules.size() )
    return;
  m_molecule=m_molecules[current];
  m_current=current;
  std::vector<CMolecule::pair>::iterator it;

  RefreshDistances();

}
void KryoVisorOpt::RenderMolecule ( GLenum mode )
{
  KryoVisor::RenderMolecule ( mode );
  if ( mode != GL_RENDER || !m_bshowforces )
    return;
  std::vector<CAtom>::const_iterator it;
  GLUquadricObj* quadric= gluNewQuadric();
  static float color[4]={0.8,0.8,0.8,0.6};

  for ( it=m_molecule.m_atom.begin();it!=m_molecule.m_atom.end();++it )
  {

    glMaterialfv ( GL_FRONT_AND_BACK, GL_AMBIENT, color );
    glMaterialfv ( GL_FRONT_AND_BACK, GL_DIFFUSE, color );
    glPushMatrix();
    {
      coordinate zaxis ( 0,0,1 );
      coordinate normal=zaxis^it->Gradient();
      float angle=coordinate::GetAngleVector ( zaxis,it->Gradient() );
      glTranslatef ( it->c.x,it->c.y,it->c.z );

      glPushMatrix();
      glRotatef ( angle*180/M_PI,normal.x,normal.y,normal.z );

      const int spheres=40;
      coordinate cyl=it->Gradient() *m_forcemagnitude*m_forcescale;
      //coordinate top=it->c+cyl;
      gluCylinder ( quadric,0.05,0.05,cyl.norm(),spheres,spheres );
      glPopMatrix();
      glPushMatrix();
      {
        glTranslatef ( cyl.x,cyl.y,cyl.z );
        //glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, icolor);
        //glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, icolor);

        glRotatef ( angle*180/M_PI,normal.x,normal.y,normal.z );

        gluCylinder ( quadric,0.09,0.001,0.2,spheres,spheres );

      }
      glPopMatrix();
    }
    glPopMatrix();

  }
  gluDeleteQuadric ( quadric );
}

void KryoVisor::RenumberAtoms()
{
  std::vector<int>::const_iterator it;
  if ( m_selectionmode == eRenumberHeavy )
  {
    std::vector<int>  hatoms;
    int counter=m_nheavyatoms;

    {
      std::vector<bond>::const_iterator bt;
      for ( it=m_selectedatoms.begin();it!=m_selectedatoms.end();++it )
      {
        bt=m_molecule.m_atom[*it].conexion.begin();
        for ( bt=m_molecule.m_atom[*it].conexion.begin();bt!=m_molecule.m_atom[*it].conexion.end();++bt )
        {
          if ( m_molecule.m_atom[bt->index].name == "H" )
          {
            hatoms.push_back ( bt->index );
          }
        }
      }
    }
    std::vector<int>::const_iterator at;
    for ( at=hatoms.begin();at!=hatoms.end();++at )
    {
      m_selectedatoms.push_back ( *at );
    }


  }
  unsigned int counter=0;
  std::vector<CAtom> renumberedatoms ( m_selectedatoms.size() );
  for ( it=m_selectedatoms.begin();it!=m_selectedatoms.end();++it,counter++ )
  {
//
    renumberedatoms[counter]=m_molecule.m_atom[*it];
  }
  m_molecule.m_atom=renumberedatoms;
  //Setup labels not necessary on next version

  m_molecule.SetConexions();
}
void KryoVisor::OnStartAnimation()
{
  if ( m_current == ( m_molecules.size() -1 ) )
  {
    m_current=0;
    emit selectedPoint ( m_current );
  }
  m_timer->start ( m_framespeed );
  emit playing ( true );
}

void KryoVisor::OnStopAnimation()
{
  m_timer->stop();
  emit playing ( false );
}

void KryoVisorOpt::OnChangeFrame()
{
  if ( GetCurrentPoint() == m_molecules.size()-1 )
  {
    m_timer->stop();
    emit playing ( false );
    return;
  }
  //We are doing deep copy, this should be changed on next version
  OnNextFrame();
#ifdef __GNUC__
#warning performance issue here
#endif

}

// void KryoVisorOpt::RenderButtons()
// {
//   KryoVisor::RenderButtons();
// }


// void KryoVisorFreq::RenderButtons()
// {
//   glDisable ( GL_LIGHTING );   // so lighting models are used
//   // glDisable(GL_LIGHT0);     // we'll use LIGHT0
//   // glDisable(GL_LIGHT1);
//   // glEnable(GL_LIGHT2);
//   glMatrixMode ( GL_PROJECTION );
//   glPushMatrix();
//   glLoadIdentity ();
//   gluOrtho2D ( 0, this->rect().width(), 0, this->rect().height() );
//   int left=this->rect().left();
//   //int bottom=this->rect().bottom();
//   int height=this->rect().height();
//   int top=this->rect().top();
//
//   glDisable ( GL_DEPTH_TEST );
//   glMatrixMode ( GL_MODELVIEW );
//   glPushMatrix();
//   glLoadIdentity();
//
//   glColor3f ( 0,1,0 );
//   glBegin ( GL_QUADS );
//   glVertex2f ( left+70,height-20 );
//   glVertex2f ( left+70,height-10 );
//   glVertex2f ( left+80,height-10 );
//   glVertex2f ( left+80,height-20 );
//   m_screencontrols[0].setCoords ( left+70,top+10,left+80,top+20 );
//   glEnd();
//
//   glColor3f ( 1,0,0 );
//   glBegin ( GL_TRIANGLES );
//   glVertex2f ( left+100,height-20 );
//   glVertex2f ( left+100,height-10 );
//   glVertex2f ( left+110,height-15 );
//   m_screencontrols[1].setCoords ( left+100,top+10,left+110,top+20 );
//   glEnd();
//
//
//   RenderScreenText();
//
//
//   glMatrixMode ( GL_PROJECTION );
//   glPopMatrix();
//   glMatrixMode ( GL_MODELVIEW );
//   glPopMatrix();
//   glEnable ( GL_DEPTH_TEST );
//   glEnable ( GL_LIGHTING );
// }

KryoVisorFreq::KryoVisorFreq ( QWidget* parent ) : KryoVisor ( parent )
{
  m_screencontrols.resize ( 2 );
  m_animation= new Animation ( m_molecule );

  //m_movie = new QPNGMovie ( file,4 );

  connect ( m_animation,SIGNAL ( shot() ),this,SLOT ( update() ) );
  //connect ( m_movie,SIGNAL ( shot() ),this,SLOT ( OnPushImage() ) );

}

void KryoVisorFreq::Initialize()
{
  CGlVisorBase::Initialize();
  m_animation->BackupMolecule();

  if ( m_molecule.GetHessian().size() > 0 )
  {
    m_exportmenu->removeItemAt ( 0 );
    Q3PopupMenu* gaussianmenu =new Q3PopupMenu ( this );
    m_exportmenu->insertItem ( "Gaussian Input",gaussianmenu,0,0 );
    gaussianmenu->insertItem ( "Geometry",this,SLOT ( OnExportGaussian() ),0,0 );
    gaussianmenu->insertItem ( "Geometry && Forces",this,SLOT ( OnExportGaussianWithHessian() ),0,1 );
  }
}

void KryoVisorFreq::OnPushImage()
{
  /* const int w=1024;
   const int h=600;
   QImage img;
   GetImage ( img,w,h );
   m_movie->PushImage ( img );*/
}

void KryoVisorFreq::OnDistortion ( int v )
{
  m_animation->SetFrame ( v );
  updateGL();
}

void KryoVisorFreq::OnStartAnimation()
{
  std::cout << "animation freq started" << std::endl;
  m_animation->Start();
  emit playing ( true );
}
bool KryoVisorFreq::IsPlaying() const
{ 
return m_animation->IsActive(); 
}

void KryoVisorFreq::OnStopAnimation()
{
std::cout << "OnStopAnimation" << std::endl;
  m_animation->Stop();
  update();
  emit playing ( false );
}
void KryoVisorFreq::RecordMovie ( bool b )
{
  /*  QPixmap p=renderPixmap(400,200,true);
    if ( b )
      m_movie->Start();
    else
      m_movie->Stop();*/
  KMessageBox::error ( this,"Not yet implemented","KryoMol" );
}
// void KryoVisorOpt::selectButton ( const QPoint& pos )
// {
//   KryoVisor::selectButton ( pos );
// }


// void KryoVisorFreq::OnStartAnimation()
// {
//   m_animation->Start();
//   emit playing(true);
// }
//
// void KryoVisorFreq::OnStopAnimation()
// {
//   m_animation->Stop();
//   emit playing(false);
// }

// void KryoVisorFreq::selectButton ( const QPoint& pos )
// {
// //   std::vector<QRect>::iterator q;
// //
// //   int i;
// //   for ( q=m_screencontrols.begin(),i=0;q!=m_screencontrols.end();q++,i++ )
// //   {
// //     if ( q->contains ( pos ) )
// //       break;
// //   }
// //
// //   if ( i > 1 )
// //     return;
// //
// //   switch ( i )
// //   {
// //     case 0:
// //       m_animation->Stop();
// //       emit playing ( false );
// //       break;
// //     case 1:
// //       m_animation->Start();
// //       emit playing ( true );
// //       break;
// //
// //     default:
// //       break;
// //   }
// //
// //   updateGL();
// }

void KryoVisorFreq::SetMode ( int mode,int frame )
{

  m_animation->SetMode ( mode,frame );
  updateGL();
  std::cout << "Set mode to " << mode << std::endl;


}

// void KryoVisorFreq::StopAnimation()
// {
//   m_animation->Stop();
//   updateGL();
// }

KryoVisorDens::KryoVisorDens ( QWidget* parent ) : KryoVisor ( parent )
{
  kDebug() << "kryovisordens" << endl;
  m_bshowattractors=m_bshowbondpoints=m_bshowringpoints=true;
  m_bshowaimcharges=false;
  Q3PopupMenu* menu = GetMenu();
  int nitems=menu->count();
  menu->insertSeparator ( ++nitems );
  m_aimmenu= new Q3PopupMenu ( this );
  m_cpmenu= new Q3PopupMenu ( this );
  menu->insertItem ( "AIM",m_aimmenu,0,++nitems );
  m_aimmenu->insertItem ( "Show Critical Points",m_cpmenu,0,0 );
  m_cpmenu->insertItem ( "Show Attractors",this,SLOT ( OnShowAttractors() ),0,0 );
  m_cpmenu->insertItem ( "Show Bond Points",this,SLOT ( OnShowBondPoints() ),0,1 );
  m_cpmenu->insertItem ( "Show Ring Points",this,SLOT ( OnShowRingPoints() ),0,2 );
  m_aimmenu->insertItem ( "Show AIM Charges",this,SLOT ( OnShowAIMCharges() ),0,1 );
  m_aimmenu->setItemChecked ( 1,m_bshowaimcharges );

  m_cpmenu->setItemChecked ( 0,m_bshowattractors );
  m_cpmenu->setItemChecked ( 1,m_bshowbondpoints );
  m_cpmenu->setItemChecked ( 2,m_bshowringpoints );
  m_aimmenu->insertItem ( "Recalculate Connectivity",this,SLOT ( OnRecalculateConnectivity() ),0,2 );
  m_cpmenu->setCheckable ( true );
  m_aimmenu->setCheckable ( true );

}

void KryoVisorDens::OnShowAttractors()
{
  m_bshowattractors=!m_cpmenu->isItemChecked ( 0 );
  m_cpmenu->setItemChecked ( 0,m_bshowattractors );
  updateGL();
}

void KryoVisorDens::OnShowBondPoints()
{
  m_bshowbondpoints=!m_cpmenu->isItemChecked ( 1 );
  m_cpmenu->setItemChecked ( 1,m_bshowbondpoints );
  updateGL();
}

void KryoVisorDens::OnShowRingPoints()
{
  m_bshowringpoints=!m_cpmenu->isItemChecked ( 2 );
  m_cpmenu->setItemChecked ( 2,m_bshowringpoints );
  updateGL();
}

void KryoVisorDens::OnShowAIMCharges()
{
  m_bshowaimcharges=!m_aimmenu->isItemChecked ( 1 );
  m_aimmenu->setItemChecked ( 1,m_bshowaimcharges );
  updateGL();
}

KryoVisorDens::~ KryoVisorDens()
{}


void KryoVisorDens::RenderMolecule ( GLenum mode )
{
  KryoVisor::RenderMolecule ( mode );
  if ( mode != GL_RENDER )
    return;

  //draw bonds with non nuclear attractors
  if ( 1 && mode == GL_RENDER )
  {
    std::vector<AIM::CriticalPoint>::iterator cp;
    if ( GetGraphMode()  == CGlVisorBase::WIREFRAME )
    {
      glDisable ( GL_LIGHTING );
      glBegin ( GL_LINES );
      for ( cp=m_molecule.GetBondPoints().begin();cp!=m_molecule.GetBondPoints().end();++cp )
      {
        if ( cp->attractors[0] >= m_molecule.m_atom.size()  || cp->attractors[1] >= m_molecule.m_atom.size() )
        {

          coordinate middle=coordinate::GetMiddle ( m_molecule.GetAttractors().at ( cp->attractors[0] ).c,
                            m_molecule.GetAttractors().at ( cp->attractors[1] ).c );
          for ( int i=0;i<2;++i )
          {
            if ( cp->attractors[i] < m_molecule.m_atom.size() )
              GetColor ( m_molecule.m_atom[cp->attractors[i]] );
            else
            {
              GLfloat color[3]={0.5,0.2,0.1};
              glColor3f ( color[0],color[1],color[2] );
            }
            glVertex3f ( m_molecule.GetAttractors().at ( cp->attractors[i] ).c.x,
                         m_molecule.GetAttractors().at ( cp->attractors[i] ).c.y,
                         m_molecule.GetAttractors().at ( cp->attractors[i] ).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 ( GetGraphMode()  == CGlVisorBase::STICKS )
    {


      GLUquadricObj* quadric=gluNewQuadric();
      for ( cp=m_molecule.GetBondPoints().begin();cp!=m_molecule.GetBondPoints().end();++cp )
      {
        coordinate middle=coordinate::GetMiddle ( m_molecule.GetAttractors().at ( cp->attractors[0] ).c,
                          m_molecule.GetAttractors().at ( cp->attractors[1] ).c );
        if ( cp->attractors[0] >= m_molecule.m_atom.size()  || cp->attractors[1] >= m_molecule.m_atom.size() )
        {

          for ( int i=0;i<2;++i )
          {
            if ( cp->attractors[i] < m_molecule.m_atom.size() )
              GetMaterialColor ( m_molecule.m_atom[cp->attractors[i]] );
            else
            {
              float color[4];
              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 );;
            }
            glPushMatrix();
            coordinate& ac =m_molecule.GetAttractors() [cp->attractors[i]].c;
            glTranslatef ( ac.x,ac.y,ac.z );
            coordinate bond=middle-ac;
            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 ( quadric,0.05,0.05, bond.norm()  , 12,12 );
            glPopMatrix();
          }

          /*  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);*/


        }

      }
      gluDeleteQuadric ( quadric );
    }

  }

  //Ok draw the attractors

  if ( m_bshowattractors || m_bshowbondpoints || m_bshowringpoints )
  {
    float color[4];
    const int spheres=20;
    GLUquadricObj* quadric= gluNewQuadric();
    if ( m_bshowattractors )
    {
      std::vector<AIM::Attractor>::iterator it;

      color[0]=1.0;
      color[1]=0.5;
      color[2]=0.0;
      color[3]=0.5;
      glMaterialfv ( GL_FRONT_AND_BACK, GL_AMBIENT, color );
      glMaterialfv ( GL_FRONT_AND_BACK, GL_DIFFUSE, color );



      for ( it=m_molecule.GetAttractors().begin();it!=m_molecule.GetAttractors().end();it++ )
      {
        glPushMatrix();
        glTranslatef ( it->c.x,it->c.y,it->c.z );
        gluSphere ( quadric,0.20,spheres,spheres );
        glPopMatrix();
      }
    }
    if ( m_bshowbondpoints )
    {
      color[0]=0.0;
      color[1]=1.0;
      color[2]=0.0;
      color[3]=0.5;
      glMaterialfv ( GL_FRONT_AND_BACK, GL_AMBIENT, color );
      glMaterialfv ( GL_FRONT_AND_BACK, GL_DIFFUSE, color );


      std::vector<AIM::CriticalPoint>::iterator cp;
      for ( cp=m_molecule.GetBondPoints().begin();cp!=m_molecule.GetBondPoints().end();cp++ )
      {
        glPushMatrix();
        glTranslatef ( cp->c.x,cp->c.y,cp->c.z );
        gluSphere ( quadric,0.20,spheres,spheres );
        glPopMatrix();
      }
      //ring points
    }

    if ( m_bshowringpoints )
    {
      color[0]=0.0;
      color[1]=0.0;
      color[2]=1.0;
      color[3]=0.5;
      glMaterialfv ( GL_FRONT_AND_BACK, GL_AMBIENT, color );
      glMaterialfv ( GL_FRONT_AND_BACK, GL_DIFFUSE, color );
      std::vector<AIM::CriticalPoint>::iterator cp;
      for ( cp=m_molecule.GetRingPoints().begin();cp!=m_molecule.GetRingPoints().end();cp++ )
      {
        glPushMatrix();
        glTranslatef ( cp->c.x,cp->c.y,cp->c.z );
        gluSphere ( quadric,0.20,spheres,spheres );
        glPopMatrix();
      }
    }

    gluDeleteQuadric ( quadric );
  }

  if ( m_bshowaimcharges )
  {
    std::vector<AIM::Attractor>::iterator it;
    glDisable ( GL_LIGHTING );
    for ( it=m_molecule.GetAttractors().begin();it!=m_molecule.GetAttractors().end();it++ )
    {


      QString s;
      s.sprintf ( "  %.3f",it->q );

      //m_visor->renderText ( middle.x,middle.y, middle.z, number,glFont);
      /*   if(1)
         {
           std::stringstream st;
           st.flags(std::ios::fixed);
           st.precision(3);
           st << " (" << m_molecule.GetDistance(it->i,it->j) << ")";
           QString str1(st.str().c_str());
           str+=str1;
         }*/
      glPushMatrix();
      glTranslatef ( it->c.x+0.25,it->c.y,it->c.z );
      RenderText ( s,it->c.x+0.25,it->c.y,it->c.z );
      glPopMatrix();

    }
    glEnable ( GL_LIGHTING );
  }

}

// void KryoVisorDens::RenderButtons()
// {
//   if ( m_molecules.size() > 1 )
//     KryoVisor::RenderButtons();
// }

void KryoVisorDens::RenderScreenText()
{
  if ( m_molecules.size() > 1 )
    KryoVisor::RenderScreenText();
}

void KryoVisorDens::OnRecalculateConnectivity()
{

  std::vector<AIM::CriticalPoint>::iterator cp;
  std::vector<CAtom>::iterator it;
  for ( it=m_molecule.m_atom.begin();it!=m_molecule.m_atom.end();++it )
  {
    it->conexion.clear();
  }
  for ( cp=m_molecule.GetBondPoints().begin();cp!=m_molecule.GetBondPoints().end();++cp )
  {
    //only nuclear attractors
    if ( cp->attractors[0] < m_molecule.m_atom.size() && cp->attractors[1] < m_molecule.m_atom.size() )
    {
      m_molecule.m_atom[cp->attractors[0]].conexion.push_back ( bond ( cp->attractors[1] ) );
      m_molecule.m_atom[cp->attractors[1]].conexion.push_back ( bond ( cp->attractors[0] ) );
    }
  }
  update();
}


KryoVisorNMR::KryoVisorNMR ( QWidget* parent ) : KryoVisor ( parent )
{
  m_bshowchemicalshifts=true;
  m_bgetjs=false;
  Q3PopupMenu* menu = GetMenu();
  int nitems=menu->count();
  menu->insertSeparator ( nitems++ );
  m_nmrmenu= new Q3PopupMenu ( this );
  menu->insertItem ( "NMR",m_nmrmenu,0,nitems );

  m_nmrmenu->insertItem ( "Show Chemical Shifts",this,SLOT ( OnShowChemicalShifts() ),0,0 );
  m_nmrmenu->setItemChecked ( 0,m_bshowchemicalshifts );
  m_nmrmenu->insertItem ( "Get Js",this,SLOT ( OnGetJs() ),0,1 );
  m_nmrmenu->setItemChecked ( 1,m_bgetjs );
}

KryoVisorNMR::~ KryoVisorNMR()
{}


void KryoVisorNMR::OnShowChemicalShifts()
{
  m_bshowchemicalshifts=!m_bshowchemicalshifts;
  m_nmrmenu->setItemChecked ( 0,m_bshowchemicalshifts );
  update();

}


void KryoVisorNMR::Initialize()
{
  KryoVisor::Initialize();
  std::vector<CAtom>::const_iterator it;
  for ( it=m_molecule.m_atom.begin();it!=m_molecule.m_atom.end();++it )
  {
    m_shieldings.push_back ( it->CShift() );
  }
}
void KryoVisorNMR::OnGetJs()
{
  m_bgetjs=!m_bgetjs;
  m_nmrmenu->setItemChecked ( 1,m_bgetjs );
  if ( ! m_bgetjs )
  {
    m_selectionmode=eNone;
    m_jpairs.clear();
  }
  else
    m_selectionmode=eJ;

  update();

}

void KryoVisorNMR::RenderMolecule ( GLenum mode )
{
  CGlVisorBase::RenderMolecule ( mode );
  if ( mode != GL_RENDER ) return;
  if ( m_bshowchemicalshifts )
  {
    std::vector<CAtom>::const_iterator it;
    std::vector<float>::const_iterator ft=m_shieldings.begin();
    glDisable ( GL_LIGHTING );

    QColor col ( QColor ( 255,255,255 ) );
    qglColor ( col );
    for ( it=m_molecule.m_atom.begin();it!=m_molecule.m_atom.end();++it,++ft )
    {
      glPushMatrix();
      glTranslatef ( it->c.x,it->c.y,it->c.z );
      QString number="  ";
      number.sprintf ( "   %.2f",*ft );
      RenderText ( number,0,0,0 );
      glPopMatrix();

    }
  }
  std::vector< CMolecule::Coupling >::const_iterator ct;

  if ( m_bgetjs )
  {
    std::vector< std::vector<CMolecule::Coupling>::const_iterator >::const_iterator it;
    glLineWidth ( 1 );
    glEnable ( GL_LINE_STIPPLE );
    glLineStipple ( 1,0xCCCC );

    for ( it=m_jpairs.begin();it!=m_jpairs.end();++it )
    {

      glBegin ( GL_LINES );
      glColor3f ( 0.9,0.9,0.9 );


      glVertexC ( m_molecule.m_atom.at ( ( *it )->I() ).c );

      glVertexC ( m_molecule.m_atom.at ( ( *it )->J() ).c );

      coordinate middle=coordinate::GetMiddle ( m_molecule.m_atom.at ( ( *it )->I() ).c,m_molecule.m_atom.at ( ( *it )->J() ).c );
      glEnd();
      QFont glFont;
      glFont.setStyleHint ( QFont::Helvetica,QFont::PreferBitmap );

      //write two spaces to leave some separation between
      //number and atom
      QString str;
      str.sprintf ( "   %.2f", ( *it )->Value() );
      //QString number="  " +str;
      renderText ( middle.x,middle.y-0.25,middle.z,str,glFont );
      glEnd();

    }

    glEnable ( GL_LIGHTING );
    glDisable ( GL_LINE_STIPPLE );

  }

  glEnable ( GL_LIGHTING );
}

void KryoVisorNMR::OnSetReferences ( const NMRHandler* h )
{
  std::vector<CAtom>::const_iterator it;
  std::vector<float>::iterator ft=m_shieldings.begin();
  for ( it=m_molecule.m_atom.begin();it!=m_molecule.m_atom.end();++it,++ft )
  {
    std::map<std::string,float>::const_iterator mp;
    for ( mp=h->References().begin();mp!=h->References().end();++mp )
    {
      if ( it->name == mp->first )
        ( *ft ) = mp->second -it->CShift();
    }
  }
  if ( 1 /* h->PromediateMethyls() */ )
  {
    std::vector<size_t> met=m_molecule.Methyls();
    std::vector<size_t>::const_iterator it;
    for ( it=met.begin();it!=met.end();++it )
    {
      std::vector<bond>::const_iterator bt;
      float shielding=0;
      for ( bt=m_molecule.m_atom.at ( *it ).conexion.begin();bt!=m_molecule.m_atom.at ( *it ).conexion.end();++bt )
      {
        if ( m_molecule.m_atom.at ( bt->index ).name == "H" )
          shielding+=m_molecule.m_atom.at ( bt->index ).CShift();
      }
      shielding/=3;
      for ( bt=m_molecule.m_atom.at ( *it ).conexion.begin();bt!=m_molecule.m_atom.at ( *it ).conexion.end();++bt )
      {
      //note that checked at acces for maps is not standard
        if ( m_molecule.m_atom.at ( bt->index ).name == "H" )
          m_shieldings.at ( bt->index ) =h->References().find("H" )->second-shielding;
      }
    }

  }
  update();
}
