/***************************************************************************
 *   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 <iostream>

#include <qlayout.h>
#include <qslider.h>

#include <QLabel>
#include <QLayout>
#include <qwt_plot.h>

#include "qconvwidget.h"
//#include "qryoplot.h"
#include "quantumplot.h"
#include "qdoubleeditbox.h"


Convergence::Convergence ( size_t size )
{
  m_energies= new double[size];
  m_S2= new double[size];
  m_rmsforces= new double[size];
  m_maximumforces= new double[size];
  m_rmsdisplacements= new double[size];
  m_maximumdisplacements= new double[size];
  m_size=size;
}

Convergence::~Convergence()
{
  delete [] m_energies;
  delete [] m_S2;
  delete [] m_rmsforces;
  delete [] m_maximumforces;
  delete [] m_rmsdisplacements;
  delete [] m_maximumdisplacements;

}

double Convergence::MaxEnergy()
{
  return GetMaxArray ( m_energies );

}

double Convergence::MinEnergy()
{
  return GetMinArray ( m_energies );
}

double Convergence::GetMaxArray ( double* array )
{
  double max=-1e25;
  for ( size_t i=0;i< m_size;i++ )
  {
    if ( array[i] > max )
      max=array[i];
  }

  return max;
}

double Convergence::GetMinArray ( double* array )
{
  double min=1e25;
  for ( size_t i=0;i< m_size;i++ )
  {
    if ( array[i] < min )
      min=array[i];
  }
  return min;
}

std::ostream& operator << ( std::ostream& s, Convergence& conv )
{
  s << "Energy" << "\t" << " RMS Force" << "\t" << "Max.Force" << "\t" << "RMS Disp." << "\t" << "MaxDisp" << std::endl;

  double* energies=conv.Energies();
  double* rmsforces=conv.RMSForces();
  double* maxforces=conv.MaximumForces();
  double* rmsdisp=conv.RMSDisplacements();
  double* maxdisp=conv.MaximumDisplacements();

  for ( size_t i=0;i < conv.size();i++ )
  {
    s << energies[i] << "\t" << maxforces[i] << "\t" << rmsforces[i] <<"\t"
    << maxdisp[i] <<"\t" << rmsdisp[i] << std::endl;

  }
  s << std::endl;
  s << energies[0] << std::endl;
  return s;
}

QConvWidget::QConvWidget ( QWidget *parent, const char *name )
    : QWidget ( parent, name )
{
  //setResizePolicy(QScrollView::AutoOneFit);

 
  QVBoxLayout* lay = new QVBoxLayout ( this );

  /*Q3GroupBox* groupBox1 = new Q3GroupBox ( Form2Layout, "groupBox1" );
  groupBox1->setColumnLayout ( 0, Qt::Vertical );
  groupBox1->layout()->setSpacing ( 6 );
  groupBox1->layout()->setMargin ( 11 );
  Q3GridLayout* groupBox1Layout = new Q3GridLayout ( groupBox1->layout() );
  //groupBox1Layout->setAlignment( Qt::AlignTop );*/

  m_energy = new QuantumPlot ( this );
  lay->addWidget ( m_energy );

  m_gradient = new QuantumPlot ( this );

  lay->addWidget ( m_gradient );


  m_displacement = new QuantumPlot ( this );

  lay->addWidget ( m_displacement  );


  m_limitsbox = new QDoubleEditBox ( 0,0,this );
  connect ( m_limitsbox,SIGNAL ( limits ( int,int ) ),this,SLOT ( OnChangeLimits ( int,int ) ) );

  lay->addWidget ( m_limitsbox, 0, 0 );

  m_scaleslider = new QSlider ( -6,6,1,0,Qt::Horizontal,this );
  m_scaleslider->setTickmarks ( QSlider::Below );
  lay->addWidget ( m_scaleslider );
  QLabel* slidertext= new QLabel ( this );
  slidertext->setText ( "Forces Scale" );
  lay->addWidget ( slidertext );
  connect ( m_scaleslider,SIGNAL ( valueChanged ( int ) ),this,SLOT ( OnForceSliderChanged ( int ) ) );


  m_xaxis=NULL;
  m_convdata=NULL;

  connect ( m_energy,SIGNAL ( selectedPoint ( int ) ),this,SIGNAL ( selectedPoint ( int ) ) );
  connect ( m_energy,SIGNAL ( selectedPoint ( int ) ),m_gradient,SLOT ( OnSelectedPoint ( int ) ) );
  connect ( m_energy,SIGNAL ( selectedPoint ( int ) ),m_displacement,SLOT ( OnSelectedPoint ( int ) ) );

  connect ( m_gradient,SIGNAL ( selectedPoint ( int ) ),this,SIGNAL ( selectedPoint ( int ) ) );
  connect ( m_gradient,SIGNAL ( selectedPoint ( int ) ),m_energy,SLOT ( OnSelectedPoint ( int ) ) );
  connect ( m_gradient,SIGNAL ( selectedPoint ( int ) ),m_displacement,SLOT ( OnSelectedPoint ( int ) ) );

  connect ( m_displacement,SIGNAL ( selectedPoint ( int ) ),this,SIGNAL ( selectedPoint ( int ) ) );
  connect ( m_displacement,SIGNAL ( selectedPoint ( int ) ),m_energy,SLOT ( OnSelectedPoint ( int ) ) );
  connect ( m_displacement,SIGNAL ( selectedPoint ( int ) ),m_gradient,SLOT ( OnSelectedPoint ( int ) ) );


}


QConvWidget::~QConvWidget()
{

  if ( m_xaxis ) delete [] m_xaxis;
  if ( m_convdata ) delete m_convdata;
}

void QConvWidget::SetNData ( size_t size )
{

  m_xaxis=new double[size];
  m_ndata=size;

  for ( size_t  i=0;i< size;i++ )
  {
    m_xaxis[i]=i+1;
  }

  m_convdata= new Convergence ( m_ndata );
  m_limitsbox->SetLimits ( 1,m_ndata );


}

// void QConvWidget::SetupCurves()
// {
//   //Take into account incomplete minimizations
//   double* forces=m_convdata->RMSForces();
//   int data=m_ndata;
//   if (forces[m_ndata-1] < 0 )
//   {
//     data=m_ndata-1;
//   }
//   EnergyCurve* envc= new EnergyCurve(m_energy,"energies");
//
//   long int  energycurve=m_energy->insertCurve(envc);
//   m_energy->setCurveRawData(energycurve,m_xaxis,m_convdata->Energies(),data);
//   envc->setExtraData(m_convdata->S2());
//
//   envc->setExtraLabel(m_energylevel);
//   m_energy->setCurvePen(energycurve,QPen(QPen::red));
//   m_energy->setAxisTitle(QwtPlot::yLeft,"Energy");
//   m_energy->setAxisLabelFormat(QwtPlot::yLeft,'f',5,6);
//   m_energy->setupStepMarkers();
//   m_energy->replot();
//
//   QuantumCurve* mxfcv= new QuantumCurve(m_gradient,"maxforces");
//   long int maxforcecurve=m_gradient->insertCurve(mxfcv);
//   QuantumCurve* rmsfcv= new QuantumCurve(m_gradient,"rmsforces");
//   long int rmsforcecurve=m_gradient->insertCurve(rmsfcv);
//
//
//   m_gradient->setCurveRawData(rmsforcecurve,m_xaxis,m_convdata->RMSForces(),data);
//   m_gradient->setCurveRawData(maxforcecurve,m_xaxis,m_convdata->MaximumForces(),data);
//   m_gradient->setCurvePen(rmsforcecurve,QPen::red);
//   m_gradient->setCurvePen(maxforcecurve,QPen::blue);
//   m_gradient->setAxisTitle(QwtPlot::yLeft,"Forces");
//   m_gradient->setAxisLabelFormat(QwtPlot::yLeft,'f',6,6);
//   long int maxfmarker=m_gradient->insertLineMarker(QString::null,QwtPlot::yLeft);
//   m_gradient->setMarkerYPos(maxfmarker,m_threshold.maxforce);
//   m_gradient->setMarkerPen(maxfmarker,QPen(Qt::green,0,Qt::DotLine));
//   long int rmsfmarker=m_gradient->insertLineMarker(QString::null,QwtPlot::yLeft);
//   m_gradient->setMarkerYPos(rmsfmarker,m_threshold.rmsforce);
//   m_gradient->setMarkerPen(rmsfmarker,QPen(Qt::yellow,0,Qt::DotLine));
//   m_gradient->setupStepMarkers();
//   m_gradient->replot();
//
//   QuantumCurve* rmsdisp = new QuantumCurve(m_displacement,"rmsdisplacements");
//   QuantumCurve* maxdisp = new QuantumCurve(m_displacement,"maxdisplacements");
//   long int maxdispcurve=m_displacement->insertCurve(maxdisp);
//   long int rmsdispcurve=m_displacement->insertCurve(rmsdisp);
//
//   m_displacement->setCurveRawData(rmsdispcurve,m_xaxis,m_convdata->RMSDisplacements(),data);
//   m_displacement->setCurveRawData(maxdispcurve,m_xaxis,m_convdata->MaximumDisplacements(),data);
//   m_displacement->setCurvePen(rmsdispcurve,QPen::red);
//   m_displacement->setCurvePen(maxdispcurve,QPen::blue);
//   m_displacement->setAxisTitle(QwtPlot::yLeft,"Displacements");
//   m_displacement->setAxisLabelFormat(QwtPlot::yLeft,'f',6,6);
//   long int maxdmarker=m_displacement->insertLineMarker(QString::null,QwtPlot::yLeft);
//   m_displacement->setMarkerYPos(maxdmarker,m_threshold.maxdisplacement);
//   m_displacement->setMarkerPen(maxdmarker,QPen(Qt::green,0,Qt::DotLine));
//   long int rmsdmarker=m_displacement->insertLineMarker(QString::null,QwtPlot::yLeft);
//   m_displacement->setMarkerYPos(rmsdmarker,m_threshold.rmsdisplacement);
//   m_displacement->setMarkerPen(rmsdmarker,QPen(Qt::yellow,0,Qt::DotLine));
//   m_displacement->setupStepMarkers();
//   m_displacement->replot();
//
//   /*std::cerr << *m_convdata;
//   std::cerr << "thr is " << m_threshold.maxforce << " "
//   << m_threshold.rmsforce << m_threshold.maxdisplacement << " " << m_threshold.rmsdisplacement << std::endl;*/
//
//
// }

void QConvWidget::SetupCurves()
{
  //Take into account incomplete minimizations
  double* forces=m_convdata->RMSForces();
  int data=m_ndata;
  if ( forces[m_ndata-1] < 0 )
  {
    data=m_ndata-1;
  }
  EnergyCurve* envc= new EnergyCurve ( m_energy,"energies" );
  envc->setExtraLabel("E");
//  long int  energycurve=m_energy->insertCurve(envc);
  envc->setRawData ( m_xaxis,m_convdata->Energies(),data );
  envc->setExtraData ( m_convdata->S2() );
  envc->setPen ( QPen ( QColor ( 255,0,0 ) ) );
  envc->attach ( m_energy );
  m_energy->setAxisTitle ( QwtPlot::yLeft,"Energy" );
//  m_energy->setAxisLabelFormat(QwtPlot::yLeft,'f',5,6);
  m_energy->setupStepMarkers();
  m_energy->replot();

  QuantumCurve* mxfcv= new QuantumCurve ( m_gradient,"maxforces" );
  mxfcv->setExtraLabel("Max. Force");
  //long int maxforcecurve=m_gradient->insertCurve(mxfcv);
  QuantumCurve* rmsfcv= new QuantumCurve ( m_gradient,"rmsforces" );
  rmsfcv->setExtraLabel("RMS Force");
  //long int rmsforcecurve=m_gradient->insertCurve(rmsfcv);


  rmsfcv->setRawData ( m_xaxis,m_convdata->RMSForces(),data );
  mxfcv->setRawData ( m_xaxis,m_convdata->MaximumForces(),data );
  rmsfcv->setPen ( QPen ( QColor ( 255,0,0 ) ) );
  mxfcv->setPen ( QPen ( QColor ( 0,0,255 ) ) );
  rmsfcv->attach ( m_gradient );
  mxfcv->attach ( m_gradient );
  m_gradient->setAxisTitle ( QwtPlot::yLeft,"Forces" );
  //m_gradient->setAxisLabelFormat(QwtPlot::yLeft,'f',6,6);
  /*long int maxfmarker=m_gradient->insertLineMarker(QString::null,QwtPlot::yLeft);*/
  QwtPlotMarker* maxfmarker= new QwtPlotMarker();
  maxfmarker->setLineStyle ( QwtPlotMarker::HLine );
  maxfmarker->setYValue ( m_threshold.maxforce );
  maxfmarker->setLinePen ( QPen ( Qt::green,0,Qt::DotLine ) );
  QwtPlotMarker* rmsfmarker=new QwtPlotMarker();
  rmsfmarker->setLineStyle ( QwtPlotMarker::HLine );
  rmsfmarker->setYValue ( m_threshold.rmsforce );
  rmsfmarker->setLinePen ( QPen ( Qt::yellow,0,Qt::DotLine ) );
  maxfmarker->attach ( m_gradient );
  rmsfmarker->attach ( m_gradient );
  m_gradient->setupStepMarkers();
  m_gradient->replot();

  QuantumCurve* rmsdisp = new QuantumCurve ( m_displacement,"rmsdisplacements" );
  rmsdisp->setExtraLabel("RMS Disp.");
  QuantumCurve* maxdisp = new QuantumCurve ( m_displacement,"maxdisplacements" );
  maxdisp->setExtraLabel("Max. Disp.");

  rmsdisp->setRawData ( m_xaxis,m_convdata->RMSDisplacements(),data );
  maxdisp->setRawData ( m_xaxis,m_convdata->MaximumDisplacements(),data );
  rmsdisp->setPen ( QPen ( QColor ( 255,0,0 ) ) );
  maxdisp->setPen ( QPen ( QColor ( 0,0,255 ) ) );

  rmsdisp->attach ( m_displacement );
  maxdisp->attach ( m_displacement );

  m_displacement->setAxisTitle ( QwtPlot::yLeft,"Displacements" );
// m_displacement->setAxisLabelFormat(QwtPlot::yLeft,'f',6,6);
  QwtPlotMarker* maxdmarker=new QwtPlotMarker();;
  maxdmarker->setLineStyle ( QwtPlotMarker::HLine );
  maxdmarker->setYValue ( m_threshold.maxdisplacement );
  maxdmarker->setLinePen ( QPen ( Qt::green,0,Qt::DotLine ) );
  QwtPlotMarker* rmsdmarker=new QwtPlotMarker();
  rmsdmarker->setLineStyle ( QwtPlotMarker::HLine );
  rmsdmarker->setYValue ( m_threshold.rmsdisplacement );
  rmsdmarker->setLinePen ( QPen ( Qt::yellow,0,Qt::DotLine ) );
  maxdmarker->attach ( m_displacement );
  rmsdmarker->attach ( m_displacement );
  m_displacement->setupStepMarkers();
  m_displacement->replot();

}


void QConvWidget::OnChangeLimits ( int first, int last )
{

  double max=-1e30;
  double min=1e30;
  for ( int i=first-1;i<last;++i )
  {
    if ( m_convdata->Energies() [i] > max ) max= ( m_convdata->Energies() [i] );
    if ( m_convdata->Energies() [i] < min ) min= ( m_convdata->Energies() [i] );
  }
  m_energy->ChangeLimits ( first,last,min,max );
  max=-1e30;
  min=1e30;
  for ( int i=first-1;i<last;++i )
  {
    if ( m_convdata->MaximumForces() [i] > max ) max= ( m_convdata->MaximumForces() [i] );
    if ( m_convdata->RMSForces() [i] > max ) max= ( m_convdata->RMSForces() [i] );
    if ( m_convdata->MaximumForces() [i] < min ) min= ( m_convdata->MaximumForces() [i] );
    if ( m_convdata->RMSForces() [i] < min ) min= ( m_convdata->RMSForces() [i] );

  }
  m_gradient->ChangeLimits(first,last,min,max);
  
    for ( int i=first-1;i<last;++i )
  {
    if ( m_convdata->MaximumDisplacements() [i] > max ) max= ( m_convdata->MaximumDisplacements() [i] );
    if ( m_convdata->RMSDisplacements() [i] > max ) max= ( m_convdata->RMSDisplacements() [i] );
    if ( m_convdata->MaximumDisplacements() [i] < min ) min= ( m_convdata->MaximumDisplacements() [i] );
    if ( m_convdata->RMSDisplacements() [i] < min ) min= ( m_convdata->RMSDisplacements() [i] );

  }
  
  m_displacement->ChangeLimits(first,last,min,max);
}

void QConvWidget::OnForceSliderChanged ( int v )
{
  emit forcescale ( pow ( 2,v ) );
}

void QConvWidget::OnSelectedPoint ( int point )
{
  m_energy->OnSelectedPoint ( point );
  m_gradient->OnSelectedPoint ( point );
  m_displacement->OnSelectedPoint ( point );
}
