/***************************************************************************
**
**  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: 2010-05-27
**  Copyright: 2010-2019
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "Resistivity1DModel.h"

namespace QGpCoreWave {

  /*!
    \class Resistivity1DModel::ResistivityContext Resistivity1DModel.h
    \brief Brief description of class still missing

    Full description of class still missing
  */

  /*!
    Description of constructor still missing
  */
  Resistivity1DModel::ResistivityContext::ResistivityContext()
  {
    TRACE;
    addVariable("n", new Resistivity1DModel::VariableN(this));
    addVariable("d", new Resistivity1DModel::VariableD(this));
    addVariable("res", new Resistivity1DModel::VariableRes(this));
  }

  QString Resistivity1DModel::ResistivityContext::helpCode() const
  {
    TRACE;
    return "   n      Number of layers in model\n"
           "   d[i]   Depth for layer i [0,n-2]\n"
           "   res[i  Resistivity for layer i [0,n-1]\n";
  }

  QVariant Resistivity1DModel::VariableN::value(const QString&) const
  {
    TRACE;
    return _context->model()->layerCount();
  }

  QVariant Resistivity1DModel::VariableD::value(const QString& index) const
  {
    TRACE;
    Resistivity1DModel * m=_context->model();
    bool ok;
    int i=index.toInt(&ok);
    if(ok && i>=0 && i<m->layerCount()-1) {
      return m->depths().at(i);
    } else {
      return QVariant();
    }
  }

  void Resistivity1DModel::VariableD::setValue(const QString& index, const QVariant& val)
  {
    TRACE;
    Resistivity1DModel * m=_context->model();
    bool ok;
    int i=index.toInt(&ok);
    if(ok && i>=0 && i<m->layerCount()-1) {
      m->setDepth(i, val.toDouble());
    }
  }

  QVariant Resistivity1DModel::VariableRes::value(const QString& index) const
  {
    TRACE;
    Resistivity1DModel * m=_context->model();
    bool ok;
    int i=index.toInt(&ok);
    if(ok && i>=0 && i<m->layerCount()) {
      return m->resistivities().at(i);
    } else {
      return QVariant();
    }
  }

  void Resistivity1DModel::VariableRes::setValue(const QString& index, const QVariant& val)
  {
    TRACE;
    Resistivity1DModel * m=_context->model();
    bool ok;
    int i=index.toInt(&ok);
    if(ok && i>=0 && i<m->layerCount()) {
      m->setResistivity(i, val.toDouble());
    }
  }

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

    Full description of class still missing
  */


QString Resistivity1DModel::formatHelp()
{
  TRACE;
  return "  Line 1    <number of layers including half-space for first model>\n"
         "  Line 2    <thickness (m)> <Res (ohm m)>\n"
         "  ....\n"
         "  Line n    0 <Res (ohm m)>\n"
         "  Line n+1  <number of layers including half-space for second model>\n"
         "  ....";
}

Profile Resistivity1DModel::profile() const
{
  TRACE;
  Profile p(*this);
  p.setDepth(p.count()-1, std::numeric_limits<double>::infinity());
  return p;
}

bool Resistivity1DModel::setValue(double& var, const StringSection& field, int iLayer, const QString& varName, bool optional)
{
  TRACE;
  if(field.isValid()) {
    var=field.toDouble();
    if(var>0.0) {
      return true;
    } else {
      App::log(tr("Bad value for %1 at layer %2: %3\n").arg(varName).arg(iLayer).arg(var) );
    }
  } else if(!optional) {
    App::log(tr("Missing value for %1 at layer %2\n").arg(varName).arg(iLayer) );
  }
  return false;
}

/*!
  Loads model from a text stream

    Format:
      \verbatim
      layerCount
      h res (layer 1)
      ...
      0 res (layer n)
      \endverbatim

  Returns false if an error occured during reading or if the read model is not consistent.
  It returns true even if the number of layers is null or cannot be read. This is the responsability
  of the caller to test if the number of layers is greater than 1 before doing anything else.
*/
bool Resistivity1DModel::fromStream(QTextStream& s, QString * comments)
{
  TRACE;
  // read the number of layers
  StringSection field, fields;
  const QChar * ptr;
  QString buf;
  buf=File::readLineNoComments(s, comments);
  fields.set(buf);
  fields.trimmed();
  ptr=0;
  field=fields.nextField(ptr);
  if(!field.isValid()) {
    return true;  // This is just a blank line, before doing anything, you must test if the number of layer is >0
  }
  int layerCount=field.toInt();
  if(layerCount>1 && !s.atEnd()) {
    resize(layerCount);
    // read the data
    double d=0.0;
    for(int i=0;i<layerCount;i++) {
      if(s.atEnd()) {
        App::log(tr("Layered model: reaching the end of file, %1 layers found, expecting %2\n")
                  .arg(i).arg(layerCount));
        clear();
        return false;
      }
      buf=File::readLineNoComments(s, comments);
      fields.set(buf);
      fields.trimmed();
      ptr=0;
      double v;
      if(i<layerCount-1) {
        field=fields.nextField(ptr);
        if(!setValue(v, field, i, tr("thickness"), false) ) return false;
        d+=v;
        setDepth(i, d);
      } else {
        fields.nextField(ptr);
      }
      field=fields.nextField(ptr);
      if(!setValue(v, field, i, tr("resistivity"), false) ) return false;
      setResistivity(i, v);
    }
    return true;
  } else {
    return false;
  }
}

/*!
  Saves model to a text stream

    Format:
      \verbatim
      nLayers
      h res (layer 1)
      ...
      0 res (layer n)
      \endverbatim
*/
void Resistivity1DModel::toStream(QTextStream& s) const
{
  TRACE;
  static const QString str2("%1 %2\n");
  s << layerCount() << "\n";
  int n=layerCount()-1;
  double d0=0;
  for(int i=0;i<n;i++) {
    double d1=depths().at(i);
    s << str2.arg(d1-d0, 0, 'g', 20)
             .arg(resistivities().at(i), 0, 'g', 20);
    d0=d1;
  }
  s << str2.arg(0.0, 0, 'g', 20)
           .arg(resistivities().at(n), 0, 'g', 20);
}

/*!
  Returns model as a string
*/
QString Resistivity1DModel::toString() const
{
  TRACE;
  QString str;
  str=QString("%1 layers\n").arg(layerCount());
  str+="          H(m)       Res(ohm m)\n";
  int n=layerCount()-1;
  double d0=0;
  for(int i=0;i<n;i++) {
    double d1=depths().at(i);
    str+=QString::number(d1-d0,'f',1).leftJustified(14,' ',true);
    str+=QString::number(resistivities().at(i),'f',1).leftJustified(14,' ',true);
    str+="\n";
    d0=d1;
  }
  str+=QString("-").leftJustified(14,' ',true);
  str+=QString::number(resistivities().at(n),'f',1).leftJustified(14,' ',true);
  str+="\n";
  return str;
}

} // namespace QGpCoreWave
