/***************************************************************************
**
**  This file is part of QGpCoreWave.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file 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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2008-11-03
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpCoreTools.h>

#include "EllipticityProxy.h"

namespace QGpCoreWave {

/*!
  \class EllipticityProxy EllipticityProxy.h
  \brief Brief description of class still missing

  Full description of class still missing
*/

QString EllipticityProxy::columnName(int col) const
{
  TRACE;
  switch(col) {
    case 0: return tr("Frequency");
    case 1: return tr("Period");
    case 2: return tr("Ellipticity");
    case 3: return tr("Stddev");
    case 4: return tr("Weight");
    default: return QString();
  }
}


QString EllipticityProxy::columnUnit(int col) const
{
  TRACE;
  switch(col) {
  case 0: return tr("Hz");
  case 1: return tr("s");
  case 2: return tr("deg");
  default: return QString();
  }
}

QVector<int> EllipticityProxy::savedColumns() const
{
  TRACE;
  QVector<int> l;
  l << 0 << 2 << 3 << 4;
  return l;
}

QVariant EllipticityProxy::columnValue(int sample, int col) const
{
  TRACE;
  const FactoryPoint& p=curve()[sample];
  switch(col) {
  case 0: return p.x();
  case 1: return 1.0/p.x();
  case 2: return p.mean();
  case 3: return p.stddev();
  case 4: return p.weight();
  default: return QVariant();
  }
}

void EllipticityProxy::setColumnValue(int sample, int col, const QVariant & value)
{
  TRACE;
  FactoryPoint& p=curve()[sample];
  switch(col) {
  case 0: p.setX(value.toDouble()); break;
  case 1: p.setX(1.0/value.toDouble()); break;
  case 2: p.setMean(value.toDouble()); break;
  case 3: p.setStddev(value.toDouble()); break;
  case 4: p.setWeight(value.toDouble()); break;
  default: break;
  }
}

QStringList EllipticityProxy::columnFileTypes() const
{
  TRACE;
  static QStringList types;
  if(types.isEmpty()) {
    types << tr("Frequency (Hz)")             // 1
          << tr("Period (s)")                 // 2
          << tr("Ellipticity atan(H/V) deg")  // 3
          << tr("Ellipticity atan(H/V) rad")  // 4
          << tr("Ellipticity (H/V)")          // 5
          << tr("Ellipticity log(H/V)")       // 6
          << tr("Ellipticity (V/H)")          // 7
          << tr("Ellipticity log(V/H)")       // 8
          << tr("atan(H/V) deg stddev")       // 9
          << tr("atan(H/V) rad stddev")       // 10
          << tr("log(H/V) stddev (approx.)")  // 11
          << tr("H/V stddev (approx.)")       // 12
          << tr("H/V*stddev (approx.)")       // 13
          << tr("H/V/stddev (approx.)")       // 14
          << tr("Weight")                     // 15
          << tr("Valid");                     // 16
  }
  return types;
}

QVector<int> EllipticityProxy::defaultColumnFileTypes() const
{
  TRACE;
  QVector<int> types;
  types << 1 << 3 << 9 << 15 << 16;
  return types;
}

bool EllipticityProxy::parse(ColumnTextIterator& it)
{
  TRACE;
  const ColumnTextParser * parser=it.parser();
  int nColumns=parser->columnCount();
  ModalCurve& c=curve();
  c.clear();
  bool ok=true;
  while(!it.atEnd() && c.isEmpty()) {
    while(!it.atSectionEnd()) {
      FactoryPoint p;
      enum StddevType {Unknown, Deg, Rad, Linear, Log, LowerBound, UpperBound};
      StddevType defaultStddevType=Deg;
      StddevType stddevType=Unknown;
      for(int iCol=0;iCol<nColumns;iCol++) {
        switch(parser->type(iCol)) {
        case 1:
          p.setX(parser->text(it.currentRow(), iCol).toDouble(&ok));
          break;
        case 2:
          p.setX(1.0/parser->text(it.currentRow(), iCol).toDouble(&ok));
          break;
        case 3:
          p.setMean(parser->text(it.currentRow(), iCol).toDouble(&ok));
          defaultStddevType=Deg;
          break;
        case 4:
          p.setMean(Angle::radiansToDegrees(parser->text(it.currentRow(), iCol).toDouble(&ok)));
          defaultStddevType=Rad;
          break;
        case 5:
          p.setMean(Angle::radiansToDegrees(::atan(parser->text(it.currentRow(), iCol).toDouble(&ok))));
          defaultStddevType=Linear;
          break;
        case 6:
          p.setMean(Angle::radiansToDegrees(::atan(pow(10.0, parser->text(it.currentRow(), iCol).toDouble(&ok)))));
          defaultStddevType=Log;
          break;
        case 7:
          p.setMean(Angle::radiansToDegrees(::atan(1.0/parser->text(it.currentRow(), iCol).toDouble(&ok))));
          defaultStddevType=Linear;
          break;
        case 8:
          p.setMean(Angle::radiansToDegrees(::atan(pow(10.0, -parser->text(it.currentRow(), iCol).toDouble(&ok)))));
          defaultStddevType=Log;
          break;
        case 9:
          p.setStddev(parser->text(it.currentRow(), iCol).toDouble(&ok));
          stddevType=Deg;
          break;
        case 10:
          p.setStddev(Angle::radiansToDegrees(parser->text(it.currentRow(), iCol).toDouble(&ok)));
          stddevType=Rad;
          break;
        case 11:
          p.setStddev(parser->text(it.currentRow(), iCol).toDouble(&ok));
          stddevType=Log;
          break;
        case 12:
          p.setStddev(parser->text(it.currentRow(), iCol).toDouble(&ok));
          stddevType=Linear;
          break;
        case 13:
          p.setStddev(parser->text(it.currentRow(), iCol).toDouble(&ok));
          stddevType=UpperBound;
          break;
        case 14:
          p.setStddev(parser->text(it.currentRow(), iCol).toDouble(&ok));
          stddevType=LowerBound;
          break;
        case 15:
          p.setWeight(parser->text(it.currentRow(), iCol).toDouble(&ok));
          break;
        case 16:
          p.setValid(parser->text(it.currentRow(), iCol).toInt(&ok)==1);
          break;
       default:
          break;
        }
        if(!ok) {
          parser->errorParsingColumn(iCol, it.currentRow());
          return false;
        }
      }
      if(p.stddev()!=0.0 && p.stddev()<0.0) {
        App::log(tr("Standard deviation must be positive\n"));
      }
      if(stddevType==Unknown) {
        stddevType=defaultStddevType;
      }
      switch(stddevType) {
      case Unknown:
      case Deg:
        break;
      case Rad:
        p.setStddev(Angle::radiansToDegrees(p.stddev()));
        break;
      case Linear:
        p.setStddev(0.5*(Angle::radiansToDegrees(::atan(::tan(Angle::degreesToRadians(p.mean()))+p.stddev()))-p.mean()+
                         p.mean()-Angle::radiansToDegrees(::atan(::tan(Angle::degreesToRadians(p.mean()))-p.stddev()))));
        break;
      case Log:
        p.setStddev(0.5*(Angle::radiansToDegrees(::atan(::tan(Angle::degreesToRadians(p.mean()))*p.stddev()))-p.mean()+
                         p.mean()-Angle::radiansToDegrees(::atan(::tan(Angle::degreesToRadians(p.mean()))/p.stddev()))));
        break;
      case LowerBound:
        p.setStddev(p.mean()-Angle::radiansToDegrees(::atan(p.stddev())));
        break;
      case UpperBound:
        p.setStddev(Angle::radiansToDegrees(::atan(p.stddev()))-p.mean());
        break;
      }
      c.append(p);
      it.nextRow();
    }
  }
  // For convenience add a default mode
  if(c.modes().isEmpty()) {
    c.addMode(Mode(Mode::Signed, 0));
  }
  return true;
}

} // namespace QGpCoreWave
