/***************************************************************************
**
**  This file is part of geopsy.
**
**  geopsy 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 3 of the License, or
**  (at your option) any later version.
**
**  geopsy 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 Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2016-12-22
**  Copyright: 2016-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "InstrumentalResponseWidget.h"

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

  Full description of class still missing
*/

class ComplexTableModel: public QAbstractTableModel
{
public:
  ComplexTableModel(QObject * parent) : QAbstractTableModel(parent) {_values=0;}

  void setValues(QVector<Complex> * v);

  virtual int rowCount(const QModelIndex &) const {return _values ? _values->count() : 0;}
  virtual int columnCount(const QModelIndex &) const {return 2;}

  virtual QVariant data(const QModelIndex & index, int role=Qt::DisplayRole) const;
  virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const;
  virtual bool setData (const QModelIndex & index, const QVariant & value, int role=Qt::EditRole);

  virtual bool insertRows(int row, int count, const QModelIndex & parent=QModelIndex());
  virtual bool removeRows(int row, int count, const QModelIndex & parent=QModelIndex());

  virtual Qt::ItemFlags flags (const QModelIndex &) const {return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;}

  void beginResetModel() {QAbstractTableModel::beginResetModel();}
  void endResetModel() {QAbstractTableModel::endResetModel();}
private:
  QVector<Complex> * _values;
};

void ComplexTableModel::setValues(QVector<Complex> * v)
{
  beginResetModel();
  _values=v;
  endResetModel();
}

bool ComplexTableModel::insertRows(int row, int count, const QModelIndex & parent)
{
  beginInsertRows(parent, row, row+count-1);
  for(int i=0;i<count;i++) {
    _values->insert(row, Complex());
  }
  endInsertRows();
  return true;
}

bool ComplexTableModel::removeRows(int row, int count, const QModelIndex & parent)
{
  beginRemoveRows(parent, row, row+count-1);
  for(int i=0;i<count;i++) {
    _values->remove(row);
  }
  endRemoveRows();
  return true;
}

QVariant ComplexTableModel::data(const QModelIndex & index, int role) const
{
  switch(role) {
  case Qt::DisplayRole:
  case Qt::EditRole:
    if(index.column()==0) {
      return QString::number(_values->at(index.row()).re());
    } else {
      return QString::number(_values->at(index.row()).im());
    }
  default:
    return QVariant();
  }
}

QVariant ComplexTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
  switch(role) {
  case Qt::DisplayRole:
    if(orientation==Qt::Vertical) {
      return section+1;
    } else {
      if(section==0) {
        return tr("Real");
      } else {
        return tr("Imaginary");
      }
    }
  default:
    return QVariant();
  }
}

bool ComplexTableModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
  switch(role) {
  case Qt::EditRole:
    if(index.column()==0) {
      (*_values)[index.row()].setRe(value.toDouble());
    } else {
      (*_values)[index.row()].setIm(value.toDouble());
    }
    return true;
  default:
    return false;
  }
}
/*!
  Description of constructor still missing
*/
InstrumentalResponseWidget::InstrumentalResponseWidget(QWidget * parent)
  : Dialog(parent)
{
  TRACE;
  setupUi(this);
  ComplexTableModel * m;
  m=new ComplexTableModel(this);
  m->setValues(&_response.poles());
  poleView->setModel(m);
  connect(poleView->itemDelegate(), SIGNAL(commitData(QWidget*)), this, SLOT(updatePlots()));
  m=new ComplexTableModel(this);
  m->setValues(&_response.zeros());
  zeroView->setModel(m);
  connect(zeroView->itemDelegate(), SIGNAL(commitData(QWidget*)), this, SLOT(updatePlots()));
  connect(saveButton, SIGNAL(clicked(bool)), this, SLOT(save()));
  connect(loadButton, SIGNAL(clicked(bool)), this, SLOT(load()));

  _transfer=new LineLayer(transferPlot);
  _transfer->setObjectName("transferFunction");
  _transfer->setReferenceLine(new ComplexStatisticalLine);
  _transfer->addLine();
  _transfer->setPointOptions(new ComplexPointOptions);
  transferPlot->xAxis()->setFrequency();

  sensorFile->addItems(Settings::getHistory("InstrumentalResponse"));
  Settings::getWidget(this);
  on_transferMode_currentIndexChanged(transferMode->currentIndex());

  Settings::columnWidth(poleView, "InstrumentalResponse::poles");
  Settings::columnWidth(zeroView, "InstrumentalResponse::zeros");
  updatePlots();
}

/*!
  Description of destructor still missing
*/
InstrumentalResponseWidget::~InstrumentalResponseWidget()
{
  TRACE;
  Settings::setColumnWidth(poleView, "InstrumentalResponse::poles");
  Settings::setColumnWidth(zeroView, "InstrumentalResponse::zeros");
}

void InstrumentalResponseWidget::on_sensorFile_editTextChanged(const QString& text)
{
  TRACE;
  QFileInfo fi(text);
  if(fi.exists() && load(text)) {
    Settings::setHistory("InstrumentalResponse", text);
  }
}

void InstrumentalResponseWidget::on_addPoleButton_clicked()
{
  TRACE;
  poleView->model()->insertRow(poleView->model()->rowCount());
}

void InstrumentalResponseWidget::on_removePoleButton_clicked()
{
  TRACE;
  QModelIndexList sel=poleView->selectionModel()->selectedRows();
  if(!sel.isEmpty()) {
    poleView->model()->removeRow(sel.first().row());
  }
}

void InstrumentalResponseWidget::on_addZeroButton_clicked()
{
  TRACE;
  zeroView->model()->insertRow(zeroView->model()->rowCount());
}

void InstrumentalResponseWidget::on_removeZeroButton_clicked()
{
  TRACE;
  QModelIndexList sel=zeroView->selectionModel()->selectedRows();
  if(!sel.isEmpty()) {
    zeroView->model()->removeRow(sel.first().row());
  }
}

void InstrumentalResponseWidget::on_transferMode_currentIndexChanged(int index)
{
  TRACE;
  ComplexPointOptions * options=static_cast<ComplexPointOptions *>(_transfer->pointOptions());
  if(index==0) {
    options->setMode(ComplexPointOptions::AbsoluteValue);
    _transfer->graph()->yAxis()->setScaleType(Scale::Log);
    _transfer->graph()->yAxis()->setTitle(tr("[V/(m/s)]"));
  } else {
    options->setMode(ComplexPointOptions::Phase);
    _transfer->graph()->yAxis()->setScaleType(Scale::Linear);
    _transfer->graph()->yAxis()->setTitle(tr("Phase [radians]"));
  }
  setTransferPlotLimits();
}

void InstrumentalResponseWidget::on_nameEdit_textChanged(QString text)
{
  TRACE;
  _response.setName(text);
}

void InstrumentalResponseWidget::on_noiseEdit_textChanged(QString text)
{
  TRACE;
  bool ok=true;
  double val=Number::toDouble(text, ok);
  if(ok) {
    _response.setNoiseLevel(val);
    updatePlots();
  }
}

void InstrumentalResponseWidget::on_transductionEdit_textChanged(QString text)
{
  TRACE;
  bool ok=true;
  double val=Number::toDouble(text, ok);
  if(ok) {
    _response.setTransductionFactor(val);
    updatePlots();
  }
}

void InstrumentalResponseWidget::on_normalizationEdit_textChanged(QString text)
{
  TRACE;
  bool ok=true;
  double val=Number::toDouble(text, ok);
  if(ok) {
    _response.setNormalizationFactor(val);
    updatePlots();
  }
}

void InstrumentalResponseWidget::on_frequencyEdit_currentIndexChanged(int index)
{
  TRACE;
  _response.setFrequencyFactor(index==0 ? 1.0 : 2.0*M_PI);
  updatePlots();
}

bool InstrumentalResponseWidget::save(QString fileName)
{
  TRACE;
  static const QString title=tr("Save sensor description");
  if(fileName.isEmpty()) {
    fileName=Message::getSaveFileName(title, tr("Sensor description (*.sensor)"));
  }
  if(!fileName.isEmpty()) {
    XMLHeader hdr(&_response);
    XMLClass::Error err=hdr.xml_saveFile(fileName, 0);
    if(err==XMLClass::NoError) {
      sensorFile->setEditText(fileName);
      return true;
    } else {
      Message::warning(MSG_ID, title, XMLClass::message(err, fileName));
    }
  }
  return false;
}

bool InstrumentalResponseWidget::load(QString fileName)
{
  TRACE;
  static const QString title=tr("Open an existing sensor description");
  if(fileName.isEmpty()) {
    fileName=Message::getOpenFileName(title, tr("Sensor description (*.sensor)"));
  }
  if(!fileName.isEmpty()) {
    InstrumentalResponse resp;
    XMLHeader hdr(&resp);
    XMLClass::Error err=hdr.xml_restoreFile(fileName);
    if(err==XMLClass::NoError) {
      setResponse(resp);
      if(sensorFile->currentText()!=fileName) { // avoid recursion
        sensorFile->setEditText(fileName);
      }
      return true;
    } else {
      Message::warning(MSG_ID, title, XMLClass::message(err, fileName));
    }
  }
  return false;
}

void InstrumentalResponseWidget::updatePlots()
{
  TRACE;
  Curve<ComplexStatisticalPoint> curve;
  SamplingParameters frequency;
  frequency.setRange(0.01, 50);
  frequency.setType(SamplingParameters::Log);
  frequency.setCount(100);
  curve.resize(frequency.count());
  for(int i=frequency.count()-1; i>=0; i--) {
    double f=frequency.value(i);
    curve[i]=ComplexStatisticalPoint(f, _response.value(f));
  }
  static_cast<ComplexStatisticalLine *>(_transfer->line(0))->setCurve(curve);
  setTransferPlotLimits();
}

void InstrumentalResponseWidget::setResponse(const InstrumentalResponse& resp)
{
  TRACE;
  static_cast<ComplexTableModel *>(poleView->model())->beginResetModel();
  static_cast<ComplexTableModel *>(zeroView->model())->beginResetModel();
  _response=resp;
  static_cast<ComplexTableModel *>(poleView->model())->endResetModel();
  static_cast<ComplexTableModel *>(zeroView->model())->endResetModel();
  nameEdit->setText(_response.name());
  blockSignals(true);
  normalizationEdit->setText(Number::toString(_response.normalizationFactor(), 'g', 6));
  transductionEdit->setText(Number::toString(_response.transductionFactor(), 'g', 6));
  noiseEdit->setText(Number::toString(_response.noiseLevel(), 'g', 6));
  frequencyEdit->setCurrentIndex((_response.frequencyFactor()==1.0) ? 0 : 1);
  blockSignals(false);
  updatePlots();
}

void InstrumentalResponseWidget::setTransferPlotLimits()
{
  TRACE;
  Rect r=_transfer->boundingRect();
  _transfer->graph()->xAxis()->setRange(r.x1(), r.x2());
  _transfer->graph()->yAxis()->setRange(r.y1(), r.y2());
  _transfer->deepUpdate();
}
