/***************************************************************************
 *   Copyright (C) 2005 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 <qpainter.h>
#include "spectrum.h"

Spectrum::Spectrum()
{
  m_scale=0.1;
  m_pivotpos=0;
}


Spectrum::~Spectrum()
{}

void Spectrum::Draw ( QPainter* p, const QRect& rect )
{
  if ( m_points.empty() ) return;
 /* std::vector<QPoint>::iterator it=m_points.begin();
  p->moveTo ( *it );

  for ( it=m_points.begin() +1;it!=m_points.end();++it )
  {
    p->lineTo ( *it );
  }*/

  p->drawPoints( &m_points[0],m_points.size() );

}

void Spectrum::DrawVector ( QPainter* p, const QRect& rect )
{
  int l=m_zoom.firstpoint;
  int r=m_zoom.lastpoint;
  float xscalefactor=static_cast<float> ( rect.width() ) /static_cast<float> ( r-l );
  int baseline=qRound ( rect.top() +rect.height() *m_baseline );
  int i=l;
  int x=qRound ( i-l ) *xscalefactor;

  std::vector<QPoint> points;
  points.resize(r-l+1);
  points.push_back(QPoint ( x,baseline-qRound ( m_scale*m_data[i].real() ) ) );

  for ( size_t i=l+1;i<=r;++i )
  {
    x=qRound ( ( i-l ) *xscalefactor );
    QPoint point ( x, baseline-qRound ( m_scale*m_data[i].real() ) );
    points.push_back(point);
  }

  p->drawPoints(&points[0],m_points.size());


}

void Spectrum::UpdateDrawing ( const QRect& rect, bool autoscale/*=false*/ )
{

  //we need to resample the data set to the widht of the drawing rect
  //std::cout << "On Update Drawing" << std::endl;
  float max=-1e12;
  float min=1e12;
  int l=m_zoom.firstpoint;
  int r=m_zoom.lastpoint;

  if ( autoscale )
  {
    for ( size_t i=l;i<r;i++ )
    {
      //std::cout << "data[" << i << "]=" << m_data[i].re << std::endl;
      if ( m_data[i].real() > max ) max=m_data[i].real();
      if ( m_data[i].real() < min ) min=m_data[i].real();
    }
    float interval=max-min;
    int baseline=qRound ( rect.top() +rect.height() *m_baseline );
    m_scale=- ( rect.top()-baseline ) /max;
    max=-1e12;
    min=1e12;
  }


  m_points.clear();
  m_points.reserve ( 2*rect.width() );
  std::cout << "points here are " << static_cast<double> ( ( m_zoom.fright-m_zoom.fleft)*m_zoom.npoints )  << std::endl;
  double xscalefactor= rect.width()  /static_cast<double> ( ( m_zoom.fright-m_zoom.fleft)*(m_data.size()-1) );
  float value=0;
  
  int baseline=qRound ( rect.top() +rect.height() *m_baseline );
  double dpoint=m_zoom.fleft*(m_data.size()-1);
  int x=qRound( (l-dpoint)*xscalefactor);
  for ( size_t i=l;i<=r;++i )
  {

    if ( m_data[i].real() > max ) max=m_data[i].real();
    if ( m_data[i].real() < min ) min=m_data[i].real();
    int newx=qRound ( ( i-dpoint ) *xscalefactor );
    if ( newx!= x )
    {
      m_points.push_back ( QPoint ( x+rect.left(),baseline-qRound ( m_scale*min ) ) );
      m_points.push_back ( QPoint ( x+rect.left(),baseline-qRound ( m_scale*max ) ) );
      value=0;
      x=newx;
      max=-1e12;
      min=1e12;
    }
  }
}

void Spectrum::SetZoom ( float r, float o )
{
  int left;
  int right;

  /*switch(m_orientation)
  {
  case RightLeft:
  left=m_zoom.firstpoint;
  right=m_zoom.lastpoint;
  m_zoom.firstpoint=qRound(left+r*(right -left));
  m_zoom.lastpoint=qRound(left+o*(right-left));
  break;
  case LeftRight:
  left=m_zoom.lastpoint;
  right=m_zoom.firstpoint;
  m_zoom.lastpoint=qRound(left-r*(left-right));
  m_zoom.firstpoint=qRound(left-o*(left-right));
  break;
  }*/
  left=m_zoom.firstpoint;
  right=m_zoom.lastpoint;
  m_zoom.firstpoint=qRound ( left+r* ( right -left ) );
  m_zoom.lastpoint=qRound ( left+o* ( right-left ) );

  float nleft=r* ( m_zoom.fright-m_zoom.fleft );
  float nright=o* ( m_zoom.fright-m_zoom.fleft );
  m_zoom.fright=nright+m_zoom.fleft;
  m_zoom.fleft+=nleft;
}


void Spectrum::MoveZoom ( float inc )
{
  if ( m_zoom.fright+inc >1.0f || m_zoom.fleft+inc<0.0f )
  {//beep
    return;
  }
  m_zoom.fleft+=inc;
  m_zoom.fright+=inc;
  m_zoom.SetPoints ( m_data.size() );
}


void Spectrum::ZeroFilling ( size_t n )
{
  if ( n == 0 )
  {
    bool pt;
    size_t size=m_data.logsize ( pt );
    if ( !pt )
      n = 1 << size  ;
    else return;
  }
  std::cout << "growing to "  << n << std::endl;
  m_data.grow ( n );
  ResetZoom();
}

void Spectrum::ZerothOrderPhase ( float angle )
{
  m_data*=expi<float>( angle );
}

void Spectrum::FirstOrderPhase ( float angle )
{
  size_t size=m_data.size()-1;
  float angle0=-angle* ( static_cast<float> ( m_pivotpos ) /size );
  //m_data*=expi(angle0);
  float angleincrement=static_cast<float> ( angle ) /size;
  float angle1=angle0;
  for ( size_t i=0;i<m_data.size();i++,angle1+=angleincrement )
  {
    //float angle1=angle*i/static_cast<float>(size);
    m_data[i]*=expi<float>( angle1 );
  }
}
void Spectrum::SetPivot()
{
  m_pivotpos=AbsMax ( true,true );
}
size_t Spectrum::AbsMax ( bool real, bool  bzoom )
{
  float max=1e-12;
  size_t first,last,i;
  size_t maxpoint=0;
  if ( bzoom )
  {
    first=m_zoom.firstpoint;
    last=m_zoom.lastpoint;
    std::cout << "first " << first << ",last " << last << std::endl;
  }
  else
  {
    first=0;
    last=m_data.size()-1;
  }

  float* pV=&m_data[first].real();
  if ( !real ) pV++;
  for ( i=0;i<=last;i++,pV+=2 )
  {


    if ( ( *pV ) > max )
    {
      maxpoint=i;
      max=*pV;
    }
  }

  return maxpoint;
}

QPoint Spectrum::PosOnWidget ( size_t pos, const QRect& rect ) const
{
  int x=qRound ( static_cast<float> ( pos-m_zoom.firstpoint ) *rect.width() / ( m_zoom.lastpoint-m_zoom.firstpoint ) ) +rect.left();
  int baseline=qRound ( rect.top() +rect.height() *m_baseline );
  return QPoint ( x,baseline-qRound ( m_scale*m_data[pos].real() ) );
}

size_t Spectrum::PosAt ( const QPoint& point,const QRect& rect ) const
{
  int pos=qRound ( static_cast<float> ( point.x()-rect.left() ) * ( m_zoom.lastpoint-m_zoom.firstpoint ) /rect.width() ) +m_zoom.firstpoint;

  if ( pos < 0 ) return 0;

  if ( pos >= m_data.size() ) return m_data.size()-1;

  return pos;

}

float Spectrum::YValue ( int yscreen, const QRect& rect ) const
{
  float yvalue= ( m_baseline*rect.height() +rect.top()-yscreen ) /m_scale;
  std::cout << "Y value=" << yvalue << std::endl;
  return yvalue;
}

void Spectrum::ScreenLimits ( size_t& first,size_t& last ) const
{
  first=m_zoom.firstpoint;
  last=m_zoom.lastpoint;
}
