/* -*- C++ -*-
 
  This file is part of ViPEC
  Copyright (C) 1991-2001 Johan Rossouw (jrossouw@alcatel.altech.co.za)
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU Library 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 Library General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
*/

#include <Utils.h>
#include <Strings.h>
#include <Types.h>
#include <Setup.h>
#include <Matrix.h>
#include <DataPoint.h>
#include <MainWindow.h>
#include <Exception.h>

#include <qregexp.h>
#include <qrect.h>
#include <qpoint.h>
#include <qstring.h>
#include <qtextstream.h>
#include <qmessagebox.h>

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

using namespace std;

//-----------------------------------------------------------------
bool Utils::testStream( QTextStream& stream, const QString& expected )
{
  QString buffer;
  stream >> buffer;
  if ( buffer != expected )
    {
      QMessageBox::warning( MainWindow::instance(), "ViPEC", "Invalid file format!\n"
                            "Expected " + expected + " and got " + buffer
                            , "&Ok", 0, 0, 0, 0);
      return FALSE;
    }
  return TRUE;
}

//-----------------------------------------------------------------
QString Utils::replaceWhiteSpace( const QString& str )
{
  QString result = str;
  for ( unsigned int i=0; i<str.length(); i++ )
    {
      if ( str.at(i) == QChar(' ') )
        {
          result.ref(i) = QChar('_');
        }
    }
  return result;
}

//-----------------------------------------------------------------
QString Utils::reinsertWhiteSpace( const QString& str )
{
  QString result = str;
  for ( unsigned int i=0; i<str.length(); i++ )
    {
      if ( str.at(i) == QChar('_') )
        {
          result.ref(i) = QChar(' ');
        }
    }
  return result;
}

//-----------------------------------------------------------------
void Utils::getFirstPart(QString& source, QString& dest)
{
  dest = "";
  source = source.stripWhiteSpace();

  //Hunt for 1st space or tab character
  QRegExp space("\\s+");
  int count = source.find(space);

  if (count < 0)
    {
      dest = source;
      source = "";
      return;
    }

  dest = source.left(count);
  dest = dest.stripWhiteSpace();
  source = source.right(source.length() - count);

}

//-----------------------------------------------------------------
QRect Utils::makeRect(const QPoint& p1, const QPoint& p2)
{
  int x = p1.x()<p2.x() ? p1.x() : p2.x();
  int y = p1.y()<p2.y() ? p1.y() : p2.y();
  int w = abs( p1.x() - p2.x() );
  int h = abs( p1.y() - p2.y() );
  QRect rect( x, y, w, h );
  return rect;
}

//-----------------------------------------------------------------
void Utils::growRect(QRect& rect, int bySize)
{
  rect.moveBy(-bySize, -bySize);
  rect.setWidth(rect.width()+2*bySize);
  rect.setHeight(rect.height()+2*bySize);
}

//-----------------------------------------------------------------
void Utils::snapToGrid(QPoint& point, int gridSpacing)
{
  int x = point.x() + gridSpacing / 2;
  int y = point.y() + gridSpacing / 2;
  point.setX( (x/gridSpacing) * gridSpacing);
  point.setY( (y/gridSpacing) * gridSpacing);
}

//-----------------------------------------------------------------
void Utils::rotatePoint90Degccw(QPoint& p)
{
  int x = p.x();
  int y = p.y();
  p.setX(-y);
  p.setY(x);
}

//-----------------------------------------------------------------
void Utils::buildFrequencyVector( Vector& vector,
                                  TReal startFreq,
                                  TReal stopFreq,
                                  bool  linear )
{
  uint noOfPoints = vector.size();

  Setup* setup = Setup::instance();
  TReal scale = setup->getDimensionValue(Strings::FrequencyDim);

  TReal start = startFreq * scale;
  TReal stop  = stopFreq * scale;

  if (!linear)
    {
      start = log(start);
      stop  = log(stop);
    }

  TReal step;
  if (noOfPoints>1)
    {
      step = (stop-start)/(noOfPoints-1);
    }
  else
    {
      step = 0;
    }

  for (uint i=0; i<noOfPoints; i++)
    {
      vector.at(i) = start + step*i;
      if (!linear)
        {
          vector.at(i) = exp( vector.at(i) );
        }
    }

}

//-----------------------------------------------------------------
void Utils::findParametersInData( TReal freq,
                                  QList<DataPoint>& data,
                                  Matrix& result)
{
  //Check frequency range
  TReal firstFrequency = data.first()->frequency();
  TReal lastFrequency = data.last()->frequency();
  if ( (freq<firstFrequency) || (freq>lastFrequency) )
    {
      throw Exception::FrequencyOutOfRange();
    }

  TReal prevFreq =  0;
  DataPoint* prevEntry = 0;
  DataPoint* entry;
  for ( entry=data.first(); entry != 0; entry=data.next() )
    {
      //Check for exact match
      if (entry->frequency() == freq)
        {
          result = entry->data();
          return;
        }
      //Check if freq between previous and this entry
      if ( (prevFreq != 0) && (freq>prevFreq) && (freq<entry->frequency()) )
        {
          result = prevEntry->data();
          TReal dF1 = freq - prevFreq;
          TReal dF2 = entry->frequency() - prevFreq;
          TReal scale = dF1/dF2;
          for (int r=0; r<2; r++)
            for (int c=0; c<2; c++)
              {
                TComplex delta = (entry->data())(r,c) - (prevEntry->data())(r,c);
                result(r,c) += scale*delta;
              }
          return;
        }
      prevFreq = entry->frequency();
      prevEntry = entry;
    }
  ASSERT( "Code should never get here!" == 0);
}

//-----------------------------------------------------------------
void Utils::convertZtoS( Matrix& z, Matrix& zo, Matrix& s )
{
  Matrix& zoc = zo; //True for non complex port impedances
  s = ~(z + zo) * (z - zoc);
}

//-----------------------------------------------------------------
void Utils::convertStoZ( Matrix& s, Matrix& zo, Matrix& z )
{
  Matrix unity(s);
  unity.diagonal( TComplex(1,0) );
  z = zo * ( unity + s ) * ~( unity - s );
}

//-----------------------------------------------------------------
void Utils::convertStoY( Matrix& s, Matrix& zo, Matrix& y )
{
  Matrix z;
  convertStoZ( s, zo, z );
  y = ~z;
}

//-----------------------------------------------------------------
TReal Utils::stern( Matrix &y, TComplex& z1, TComplex &z2 )
{
  TComplex Yrf = y(0,1)*y(1,0);
  TReal Gi = real( y(0,0) );
  TReal Go = real( y(1,1) );
  TReal Gs = 1/real(z1);
  TReal Gl = 1/real(z2);
  TReal stern = 2*(Gi+Gs)*(Go+Gl)/(abs(Yrf)+real(Yrf));
  return stern;
}

//-----------------------------------------------------------------
TReal Utils::linvill( Matrix &y )
{
  TComplex Yrf = y(0,1)*y(1,0);
  TReal M = abs(Yrf);
  TReal P = real(Yrf);
  TReal Gi = real( y(0,0) );
  TReal Go = real( y(1,1) );
  TReal linvill = M/(2*Gi*Go - P);
  return linvill;
}

//-----------------------------------------------------------------
TReal Utils::getNumericRangePart( QString str, int pos )
{
  QString subStr = "";
  for ( int i = 0; i<=pos; i++ )
    {
      int separatorPos = str.find( ":" );
      if ( separatorPos < 0 )
        {
          throw Exception::NotRangedValue();
        }
      subStr = str.left( separatorPos );
      str = str.right( str.length() - separatorPos - 1 );
      if (i==0)
        {
          str += ":";
        }
    }

  bool ok = TRUE;
  TReal value = subStr.toDouble(&ok);

  if ( !ok )
    {
      throw Exception::NotRangedValue();
    }
  return value;
}
