/***************************************************************************
**
**  This file is part of ArrayGui.
**
**  ArrayGui 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.
**
**  ArrayGui 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: 2005-09-13
**  Copyright: 2005-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <GeopsyGui.h>

#include "RingEditor.h"

namespace ArrayGui {

RingEditorDelegate::RingEditorDelegate(QObject *parent)
    : QItemDelegate(parent)
{
  TRACE;}

QWidget *RingEditorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & ,
    const QModelIndex &index) const
{
  if(index.column()<2) {
    DoubleSpinBox * w=new DoubleSpinBox(parent);
    connect(w,SIGNAL(valueChanged(double)), this, SLOT(commitSpin()));
    w->setMinimum(0.0);
    w->setMaximum(std::numeric_limits<double>::infinity());
    return w;
  } else {
    return nullptr;
  }
}

void RingEditorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
  TRACE;
  DoubleSpinBox * w=qobject_cast<DoubleSpinBox *>(editor);
  if(!w) return;
  w->setValue(index.model()->data(index).toDouble());
}

void RingEditorDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
    const QModelIndex &index) const
{
  TRACE;
  DoubleSpinBox * w=qobject_cast<DoubleSpinBox *>(editor);
  if(!w) return;
  model->setData(index, w->value());
}

/*
 *  Constructs a RingEditor as a child of 'parent', with the
 *  name 'name' and widget flags set to 'f'.
 */
RingEditor::RingEditor(QWidget *parent, Qt::WindowFlags f)
    : QWidget(parent, f)
{
  TRACE;
  setupUi(this);

  QStringList hLabels;
  hLabels << tr("Min")
          << tr("Max")
          << tr("Pairs")
          << tr("Av. radius")
          << tr("Thickness")
          << tr("Angle step")
          << tr("Color");
  rings->setHorizontalHeaderLabels (hLabels);
  rings->setSelectionBehavior(QAbstractItemView::SelectItems);
  rings->setSelectionMode(QAbstractItemView::SingleSelection);
  rings->setEditTriggers(QAbstractItemView::AllEditTriggers);
  rings->setItemDelegate (new RingEditorDelegate(this));

  for(int i=RingEditor_MinCol; i<RingEditor_ColorCol; i++) {
    rings->resizeColumnToContents(i);
  }
#if(QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
  rings->horizontalHeader()->setSectionResizeMode(RingEditor_ColorCol, QHeaderView::Stretch);
#else
  rings->horizontalHeader()->setResizeMode(RingEditor_ColorCol, QHeaderView::Stretch);
#endif
  //rings->setColumnReadOnly(2,TRUE);
  _rings=nullptr;
  _circleLayer=nullptr;
}

/*
 *  Destroys the object and frees any allocated resources
 */
RingEditor::~RingEditor()
{
  TRACE;
  // no need to delete child widgets, Qt does it all for us
}

void RingEditor::setCoArrayGraph(ArrayMap * w)
{
  TRACE;
  _circleLayer=new CircleViewer(w);
  _circleLayer->setObjectName("rings");
}

void RingEditor::setArray(const ArraySelection& array)
{
  TRACE;
  int nStations=array.count();
  int nPairs=nStations*(nStations-1)/2;
  _pairs.resize(nPairs);
  int iPair=0;
  for(int i=nStations-1; i>=0; i--) {
    for(int j=i-1; j>=0; j--) {
      _pairs[iPair++].setStations(array, i, j);
    }
  }
  std::sort(_pairs.begin(), _pairs.end(), StationPair::lessThan);
}

void RingEditor::setArray(const QList<NamedPoint>& array)
{
  TRACE;
  int nStations=array.count();
  int nPairs=nStations*(nStations-1)/2;
  _pairs.resize(nPairs);
  int iPair=0;
  for(int i=nStations-1; i>=0; i--) {
    for(int j=i-1; j>=0; j--) {
      _pairs[iPair++].setStations(array.at(i), array.at(j));
    }
  }
  std::sort(_pairs.begin(), _pairs.end(), StationPair::lessThan);
}

void RingEditor::clear()
{
  TRACE;
  // clear() and clearcontents() do not change the table dimensions
  int n=rings->rowCount();
  for(int i=0; i<n; i++) {
    rings->removeRow(0);
  }
  updateGraph();
}

void RingEditor::addRing(double minR, double maxR)
{
  int n=rings->rowCount();
  rings->setRowCount(n+1);
  initRow(n);
  minR=floor(minR*100.0)*0.01;
  maxR=ceil(maxR*100.0)*0.01;
  rings->item(n, RingEditor_MinCol)->setText(QLocale().toString(minR));
  rings->item(n, RingEditor_MaxCol)->setText(QLocale().toString(maxR));
  rings->item(n, RingEditor_ColorCol)->setBackground(Qt::red);
  updateGraph();
}

void RingEditor::initRow(int row)
{
  TRACE;
  rings->blockSignals(true);
  for(int i=RingEditor_MinCol; i<=RingEditor_ColorCol; i++) {
    rings->setItem(row, i, new QTableWidgetItem);
  }
  rings->item(row, RingEditor_ColorCol)->setFlags(Qt::ItemIsEnabled);
  rings->resizeRowToContents(row);
  rings->blockSignals(false);
}

void RingEditor::on_addBut_clicked()
{
  TRACE;
  int n=rings->rowCount();
  rings->setRowCount(n+1);
  initRow(n);
  if(n>0) { // Copy info from last ring
    QString maxR=rings->item(n-1, RingEditor_MaxCol)->text();
    rings->blockSignals(true);
    rings->item(n, RingEditor_MinCol)->setText(maxR);
    rings->item(n, RingEditor_MaxCol)->setText(maxR);
    rings->blockSignals(false);
  }
  rings->item(n, RingEditor_ColorCol)->setBackground(Qt::red);
  updateGraph();
}

void RingEditor::on_removeBut_clicked()
{
  TRACE;
  int index=rings->currentRow();
  if(index>=0) {
    rings->removeRow(index);
    rings->setCurrentItem(rings->item(index, 0));
    updateGraph();
    updateProperties();
  }
}

void RingEditor::on_rings_itemClicked(QTableWidgetItem * item)
{
  TRACE;
  if( !item) return ;
  int col=rings->column(item);
  if(col==RingEditor_ColorCol) {
    QColor c=item->background().color();
    c=QColorDialog::getColor(c, this);
    if(c.isValid()) {
      item->setBackground(c);
      updateGraph();
    }
  }
}

void RingEditor::on_rings_itemChanged(QTableWidgetItem * item)
{
  TRACE;
  if(!item) return;
  int col=rings->column(item);
  if(col==RingEditor_MinCol || col==RingEditor_MaxCol) {
    updateGraph();
    updateProperties();
  }
}

void RingEditor::updateProperties()
{
  TRACE;
  int sum=0;
  int n=rings->rowCount();
  for(int i=0; i<n; i++) {
    double minR=rings->item(i, RingEditor_MinCol)->text().toDouble();
    double maxR=rings->item(i, RingEditor_MaxCol)->text().toDouble();
    SPACRing r(minR, maxR);
    r.setPairs(_pairs);
    int count=r.count();
    sum+=count;
    rings->item(i, RingEditor_PairsNumCol)->setText(QLocale().toString(count));
    rings->item(i, RingEditor_AverageRadiusCol)->setText(QLocale().toString(r.averageRadius()));
    rings->item(i, RingEditor_ThicknessCol)->setText(QLocale().toString(r.thickness()));
    rings->item(i, RingEditor_AngleStepCol)->setText(QLocale().toString(r.maximumAngleStep()));
  }
  coupleTotal->display(static_cast<int>(sum));
}

void RingEditor::updateGraph()
{
  TRACE;
  if(!_circleLayer) {
    return;
  }
  int n=rings->rowCount();
  _circleLayer->resize(2*n);
  double r;
  for(int i=0; i<n; i++) {
    QColor c=rings->item(i, RingEditor_ColorCol)->background().color();
    r=rings->item(i, RingEditor_MinCol) ->text().toDouble();
    _circleLayer->set(2*i, 0, 0, r, r, 0.0, c);
    r=rings->item(i, RingEditor_MaxCol)->text().toDouble();
    _circleLayer->set(2*i+1, 0, 0, r, r, 0.0, c);
  }
  _circleLayer->deepUpdate();
}

int RingEditor::ringCount()
{
  TRACE;
  return rings->rowCount();
}

void RingEditor::on_loadBut_clicked()
{
  TRACE;
  QString str=Message::getOpenFileName(tr("Open rings file"), tr("Rings file (*.rings)"));
  if(str.length()>0) load(str);
}

void RingEditor::load(QString str)
{
  TRACE;
  QFile f(str);
  f.open(QIODevice::ReadOnly);
  QTextStream s(&f);
  QString line;
  while(!s.atEnd()) {
    line=s.readLine().simplified();
    if(line.left(1)!="#") {
      int row=rings->rowCount();
      rings->setRowCount(row+1);
      initRow(row);
      rings->item(row, RingEditor_MinCol)->setText(line.section(QRegExp("[ \t]"), 0, 0));
      rings->item(row, RingEditor_MaxCol)->setText(line.section(QRegExp("[ \t]"), 1, 1));
      int r=line.section(QRegExp("[ \t]"), 2, 2).toInt();
      int g=line.section(QRegExp("[ \t]"), 3, 3).toInt();
      int b=line.section(QRegExp("[ \t]"), 4, 4).toInt();
      rings->item(row, RingEditor_ColorCol) ->setBackground(QColor(r, g, b));
    }
  }
  rings->resizeColumnToContents(RingEditor_MinCol);
  rings->resizeColumnToContents(RingEditor_MaxCol);
  rings->resizeColumnToContents(RingEditor_PairsNumCol);
  updateGraph();
  updateProperties();
}

void RingEditor::on_saveBut_clicked()
{
  TRACE;
  QString str=Message::getSaveFileName("Save rings file", "Rings file (*.rings)");
  if(!str.isEmpty()) {
    QFile f(str);
    f.open(QIODevice::WriteOnly);
    QTextStream s(&f);
    int nRows=rings->rowCount();
    QString line;
    s << "# MinRadius\tMaxRadius\tRed\tGreen\tBlue" << endl;
    for(int row=0; row<nRows; row++) {
      line="%1\t%2\t%3\t%4\t%5\t%6";
      QColor c=rings->item(row, RingEditor_ColorCol)->background().color();
      s << line.arg(rings->item(row, RingEditor_MinCol)->text()).
      arg(rings->item(row, RingEditor_MaxCol)->text()).
      arg(c.red()).arg(c.green()).arg(c.blue()).
      arg(rings->item(row, RingEditor_PairsNumCol) ->text()) << endl;
    }
  }
}

void RingEditor::on_optimizeBut_clicked()
{
  TRACE;
  int n=rings->rowCount();
  for(int i=0; i<n; i++) {
    double minR=QLocale().toDouble(rings->item(i, RingEditor_MinCol)->text());
    double maxR=QLocale().toDouble(rings->item(i, RingEditor_MaxCol)->text());
    static_cast<ArrayMap *>(_circleLayer->graph())->optimizeRing(minR, maxR);
    minR=floor(minR*100.0)*0.01;
    maxR=ceil(maxR*100.0)*0.01;
    rings->item(i, RingEditor_MinCol)->setText(QLocale().toString(minR));
    rings->item(i, RingEditor_MaxCol)->setText(QLocale().toString(maxR));
  }
  on_rings_itemChanged(rings->item(0, RingEditor_MinCol));
}

void RingEditor::on_autoBut_clicked()
{
  TRACE;
  QVector<SPACRing> r=SPACRing::autoRings(_pairs);
  int n=r.count();
  rings->setRowCount(n);
  for(int i=0; i<n; i++) {
    initRow(i);
    const RingPairs& p=r.at(i);
    rings->blockSignals(true);
    rings->item(i, RingEditor_MinCol)->setText(QLocale().toString(p.minRadius()));
    rings->item(i, RingEditor_MaxCol)->setText(QLocale().toString(p.maxRadius()));
    rings->item(i, RingEditor_ColorCol)->setBackground(Qt::red);
    rings->blockSignals(false);
  }
  updateGraph();
  updateProperties();
}

} // namespace ArrayGui
