/***************************************************************************
**
**  This file is part of GeopsyGui.
**
**  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: 2009-10-20
**  Copyright: 2009-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpGuiTools.h>

#include "AsciiSignalRulesWidget.h"

namespace GeopsyGui {

void AsciiSignalRulesItem::setFormat(AsciiSignalFormat * f)
{
  beginResetModel();
  _format=f;
  endResetModel();
}

void AsciiSignalRulesItem::setHeader(const QString& h)
{
  beginResetModel();
  _header=h;
  StreamRedirection sr(_exampleErrors);
  _format->parseHeader(_header);
  endResetModel();
}

bool AsciiSignalRulesItem::insertRow(int row)
{
  beginInsertRows(QModelIndex(), row, row);
  AsciiSignalFormatRule r;
  if(row<_format->ruleCount()) {
    r=_format->rule(row);
  }
  _format->insertRule(row, r);
  endInsertRows();
  return true;
}

bool AsciiSignalRulesItem::removeRow(int row)
{
  beginRemoveRows(QModelIndex(), row, row);
  _format->removeRule(row);
  endRemoveRows();
  return true;
}

bool AsciiSignalRulesItem::moveUp(int row)
{
  if(beginMoveRows(QModelIndex(), row, row, QModelIndex(), row-1)) {
    _format->moveRuleUp(row);
    endMoveRows();
    return true;
  } else {
    return false;
  }
}

bool AsciiSignalRulesItem::moveDown(int row)
{
  if(beginMoveRows(QModelIndex(), row, row, QModelIndex(), row+2)) {
    _format->moveRuleDown(row);
    endMoveRows();
    return true;
  } else {
    return false;
  }
}

QVariant AsciiSignalRulesItem::data(const QModelIndex & index, int role) const
{
  const AsciiSignalFormatRule& r=_format->rule(index.row());
  switch(role) {
  case Qt::DisplayRole:
    switch(index.column()) {
    case 1:
      return r.value();
    case 3:
      return r.operationString();
    case 4:
      return r.pattern();
    case 5:
      return r.patternIndex();
    case 6:
      return r.factor();
    case 7:
      return r.channel();
    case 8:
      return MetaDataFactory::instance()->baseName(r.data());
    case 9:
      return r.data().index();
    default:
      return QVariant();
    }
    break;
  case Qt::CheckStateRole:
    switch(index.column()) {
    case 0:
      return r.constant() ? Qt::Checked : Qt::Unchecked;
    case 2:
      return r.mandatory() ? Qt::Checked : Qt::Unchecked;
    default:
      return QVariant();
    }
    break;
  default:
    return QVariant();
  }
}

Qt::ItemFlags AsciiSignalRulesItem::flags (const QModelIndex & index) const
{
  const AsciiSignalFormatRule& r=_format->rule(index.row());
  if(r.constant()) {
    switch(index.column()) {
    case 0:
      return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable;
    case 2:
    case 4:
    case 5:
      return Qt::NoItemFlags;
    default:
      return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;
    }
  } else {
    switch(index.column()) {
    case 0:
    case 2:
      return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable;
    case 1:
      return Qt::NoItemFlags;
    default:
      return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;;
    }
  }
}

QVariant AsciiSignalRulesItem::headerData(int section, Qt::Orientation orientation, int role) const
{
  switch(role) {
  case Qt::DisplayRole:
    if(orientation==Qt::Vertical) {
      return section+1;
    } else {
      switch(section) {
      case 0:
        return tr("Constant");
      case 1:
        return tr("Value");
      case 2:
        return tr("Mandatory");
      case 3:
        return tr("Operation");
      case 4:
        return tr("Pattern");
      case 5:
        return tr("Index");
      case 6:
        return tr("Factor");
      case 7:
        return tr("Channel");
      case 8:
        return tr("Data");
      case 9:
        return tr("Index");
      default:
        return QVariant();
      }
    }
    break;
  case Qt::ToolTipRole:
    if(orientation==Qt::Horizontal) {
      switch(section) {
      case 0:
        return tr("Value is not extracted from header but directly assigned.");
      case 1:
        return tr("For non constant values, the value extracted from file example (factor not included).");
      case 2:
        return tr("Generates an error if pattern cannot be found.");
      case 3:
        return tr("Operation between old and new value.");
      case 4:
        return tr("Regular expression to identify information in header");
      case 5:
        return tr("Pattern must contain at least one expression between (), "
                  "this index identifies the value to extract");
      case 6:
        return tr("Multiply the value by this factor");
      case 7:
        return tr("Assign the value only if signal matches this channel number");
      case 8:
        return tr("Signal data to assign");
      case 9:
        return tr("For signal data vectors, the index to assign");
      default:
        return QVariant();
      }
    }
    break;
  default:
    break;
  }
  return QVariant();
}

void AsciiSignalRulesItem::updateValue(const QModelIndex & index)
{
  AsciiSignalFormatRule& r=_format->rule(index.row());
  if(!r.constant()) {
    StreamRedirection sr(_exampleErrors);
    _format->parseHeader(_header);
    dataChanged(AsciiSignalRulesItem::index(index.row(), 1, index.parent()),
                AsciiSignalRulesItem::index(index.row(), 1, index.parent()));
  }
}

bool AsciiSignalRulesItem::setData (const QModelIndex & index, const QVariant & value, int role)
{
  AsciiSignalFormatRule& r=_format->rule(index.row());
  switch(role) {
  case Qt::EditRole:
    switch(index.column()) {
    case 1:
      r.setValue(value);
      return true;
    case 3:
      r.setOperation(value.toString());
      return true;
    case 4:
      r.setPattern(value.toString());
      updateValue(index);
      return true;
    case 5:
      r.setPatternIndex(value.toInt());
      updateValue(index);
      return true;
    case 6:
      r.setFactor(value.toDouble());
      updateValue(index);
      return true;
    case 7:
      r.setChannel(value.toInt());
      updateValue(index);
      return true;
    case 8: {
        MetaDataIndex index=MetaDataFactory::instance()->index(value.toString());
        index.setIndex(r.data().index());
        r.setData(index);
      }
      return true;
    case 9: {
        MetaDataIndex index=r.data();
        index.setIndex(value.toString());
        r.setData(index);
      }
      return true;
    default:
      return false;
    }
    break;
  case Qt::CheckStateRole:
    switch(index.column()) {
    case 0:
      r.setConstant(value==Qt::Checked ? true : false);
      updateValue(index);
      return true;
    case 2:
      r.setMandatory(value==Qt::Checked ? true : false);
      return true;
    default:
      break;
    }
  default:
    return false;
  }
}

void AsciiSignalRulesDelegate::commitWidget(QWidget * w)
{
  TRACE;
  if(!w) {
    w=qobject_cast<QWidget *>(sender());
    ASSERT(w);
  }
  emit commitData(w);
}

AsciiSignalRulesDelegate::AsciiSignalRulesDelegate(QObject *parent)
    : QItemDelegate(parent)
{
  TRACE;
  _tableFieldList=MetaDataFactory::instance()->registeredNames();
}

QWidget * AsciiSignalRulesDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option,
                                                     const QModelIndex &index) const
{
  TRACE;
  switch(index.column()) {
  case 1:
  case 4:
  case 5:
  case 6:
  case 7:
  case 9: {
      QLineEdit * w=new QLineEdit(parent);
      connect(w, SIGNAL(textChanged(QString)), this, SLOT(commitWidget()));
      return w;
    }
  case 3: {
      QComboBox * w=new QComboBox(parent);
      QStringList items;
      items << tr("Assign") << tr("Add") << tr("Subtract") << tr("Multiply") << tr("Divide");
      w->addItems(items);
      return w;
    }
  case 8: {
      QComboBox * w=new QComboBox(parent);
      w->addItems(_tableFieldList);
      return w;
    }
  default:
    return QItemDelegate::createEditor(parent, option, index);
  }
}

void AsciiSignalRulesDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
  TRACE;
  switch(index.column()) {
  case 1:
  case 4:
  case 5:
  case 6:
  case 7:
  case 9: {
      QLineEdit * w= qobject_cast<QLineEdit *>(editor);
      if(!w) return;
      w->setText(index.model()->data(index).toString());
      w->selectAll();
    }
    break;
  case 3:
  case 8: {
      QComboBox * w=qobject_cast<QComboBox *>(editor);
      if(!w) return;
      int pos=w->findText(index.model()->data(index).toString(), Qt::MatchExactly);
      w->setCurrentIndex(pos);
    }
    break;
  default:
    return QItemDelegate::setEditorData(editor, index);
  }
}

void AsciiSignalRulesDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
  TRACE;
  switch(index.column()) {
  case 1:
  case 4:
  case 5:
  case 6:
  case 7:
  case 9: {
      QLineEdit * w=qobject_cast<QLineEdit *>(editor);
      if(!w) return;
      model->setData(index, w->text());
    }
    break;
  case 3:
  case 8: {
      QComboBox * w=qobject_cast<QComboBox *>(editor);
      if(!w) return;
      model->setData(index, w->currentText());
    }
    break;
  default:
    return QItemDelegate::setModelData(editor, model, index);
  }
}

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

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
AsciiSignalRulesWidget::AsciiSignalRulesWidget(QWidget * parent, Qt::WindowFlags f)
    : QWidget(parent, f)
{
  TRACE;
  setupUi(this);
  rulesTable->setModel(new AsciiSignalRulesItem(this));
  rulesTable->setItemDelegate(new AsciiSignalRulesDelegate(this));
  rulesTable->setSelectionBehavior(QAbstractItemView::SelectRows);
  rulesTable->setSelectionMode(QAbstractItemView::SingleSelection);
  rulesTable->setEditTriggers(QAbstractItemView::AllEditTriggers);
  rulesTable->resizeColumnsToContents();
  Settings::columnWidth(rulesTable, "RulesTable");
  Settings::getRect(this, "RulesTable");
}

/*!
  Description of destructor still missing
*/
AsciiSignalRulesWidget::~AsciiSignalRulesWidget()
{
  TRACE;
  Settings::setColumnWidth(rulesTable, "RulesTable");
  Settings::setRect(this, "RulesTable");
}

void AsciiSignalRulesWidget::setFormat(AsciiSignalFormat * f)
{
  TRACE;
  static_cast<AsciiSignalRulesItem *>(rulesTable->model())->setFormat(f);
}

void AsciiSignalRulesWidget::setHeader(const QString& h)
{
  TRACE;
  static_cast<AsciiSignalRulesItem *>(rulesTable->model())->setHeader(h);
}

void AsciiSignalRulesWidget::setExampleErrors(AbstractStream * s)
{
  TRACE;
  static_cast<AsciiSignalRulesItem *>(rulesTable->model())->setExampleErrors(s);
}

void AsciiSignalRulesWidget::on_addButton_clicked()
{
  TRACE;
  QModelIndexList sel=rulesTable->selectionModel()->selectedIndexes();
  if(!sel.isEmpty()) {
    static_cast<AsciiSignalRulesItem *>(rulesTable->model())->insertRow(sel.first().row());
  } else {
    static_cast<AsciiSignalRulesItem *>(rulesTable->model())->insertRow(rulesTable->model()->rowCount());
  }
}

void AsciiSignalRulesWidget::on_removeButton_clicked()
{
  TRACE;
  QModelIndexList sel=rulesTable->selectionModel()->selectedIndexes();
  if(!sel.isEmpty()) {
    static_cast<AsciiSignalRulesItem *>(rulesTable->model())->removeRow(sel.first().row());
  }
}

void AsciiSignalRulesWidget::on_downButton_clicked()
{
  TRACE;
  QModelIndexList sel=rulesTable->selectionModel()->selectedIndexes();
  if(!sel.isEmpty()) {
    int i=sel.first().row();
    if(i<rulesTable->model()->rowCount()-1) {
      static_cast<AsciiSignalRulesItem *>(rulesTable->model())->moveDown(i);
    }
  }
}

void AsciiSignalRulesWidget::on_upButton_clicked()
{
  TRACE;
  QModelIndexList sel=rulesTable->selectionModel()->selectedIndexes();
  if(!sel.isEmpty()) {
    int i=sel.first().row();
    if(i>0) {
      static_cast<AsciiSignalRulesItem *>(rulesTable->model())->moveUp(sel.first().row());
    }
  }
}

} // namespace GeopsyGui
