/***************************************************************************
**
**  This file is part of SciFigs.
**
**  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-04-02
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "GraphContentLayerProperties.h"
#include "GraphContent.h"
#include "AxisWindow.h"
#include "XMLSciFigs.h"

namespace SciFigs {

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

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
GraphContentLayerProperties::GraphContentLayerProperties(QWidget * parent)
    : PropertyWidget(parent)
{
  TRACE;
  setupUi(this);

  layers->setModel(new LayerPropertiesItem(this));
  layers->setItemDelegate(new LayerPropertiesDelegate(this));
  layers->setSelectionBehavior(QAbstractItemView::SelectRows);
  layers->setSelectionMode(QAbstractItemView::ExtendedSelection);
  layers->setEditTriggers(QAbstractItemView::AllEditTriggers);
}

GraphContentLayerProperties::~GraphContentLayerProperties()
{
}

void GraphContentLayerProperties::addGraph(GraphContent * g)
{
  TRACE;
  if(_graphSet.contains(g)) return;
  _graphSet.insert(g);
  setGraphList();
  on_currentGraphCombo_currentIndexChanged(0);
}

void GraphContentLayerProperties::removeGraph(GraphContent * g)
{
  TRACE;
  if(!_graphSet.contains(g)) return;
  _graphSet.remove(g);
  setGraphList();
  on_currentGraphCombo_currentIndexChanged(0);
}

bool GraphContentLayerProperties::graphLessThan(const GraphContent * g1, const GraphContent * g2)
{
  QString g1Name, g2Name;
  if(g1->graph()->objectName().isEmpty())
    g1Name="unamed_graph";
  else
    g1Name=g1->graph()->objectName();
  if(g2->graph()->objectName().isEmpty())
    g2Name="unamed_graph";
  else
    g2Name=g2->graph()->objectName();
  return g1Name<g2Name;
}

void GraphContentLayerProperties::setGraphList()
{
  TRACE;
  _graphList.clear();
  for(QSet<GraphContent *>::iterator it=_graphSet.begin();it!=_graphSet.end();it++) {
    _graphList.append(*it);
  }
  std::sort(_graphList.begin(), _graphList.end(), graphLessThan);
  currentGraphCombo->clear();
  for(QList<GraphContent *>::iterator it=_graphList.begin();it!=_graphList.end();it++) {
    GraphContent * g=*it;
    if(g->graph()->objectName().isEmpty())
      currentGraphCombo->addItem( "unamed_graph" );
    else
      currentGraphCombo->addItem(g->graph()->objectName());
  }
}

GraphContent * GraphContentLayerProperties::currentGraph() const
{
  TRACE;
  int i=currentGraphCombo->currentIndex();
  if(i<_graphList.count() && i>=0) {
    return _graphList[i];
  } else {
    return nullptr;
  }
}

void GraphContentLayerProperties::reset()
{
  TRACE;
  static_cast<LayerPropertiesItem *>(layers->model())->setGraph(currentGraph());
}

void GraphContentLayerProperties::on_currentGraphCombo_currentIndexChanged(int)
{
  TRACE;
  GraphContent * selectedGraph=currentGraph();
  if(selectedGraph) {
    static_cast<LayerPropertiesItem *>(layers->model())->setGraph(selectedGraph);
  }
}

void GraphContentLayerProperties::on_saveLayers_clicked()
{
  TRACE;
  GraphContent * selectedGraph=currentGraph();
  QModelIndexList l=layers->selectionModel()->selectedIndexes();
  if(l.isEmpty()) {
    selectedGraph->saveLayers();
  } else {
    LayerPropertiesItem * model=static_cast<LayerPropertiesItem *>(layers->model());
    model->selectAllLayers(false);
    model->selectLayers(l);
    selectedGraph->saveLayers();
    model->selectAllLayers(true);
  }
}

void GraphContentLayerProperties::on_copyLayers_clicked()
{
  TRACE;
  GraphContent * selectedGraph=currentGraph();
  QModelIndexList l=layers->selectionModel()->selectedIndexes();
  if(l.isEmpty()) {
    selectedGraph->copyLayers();
  } else {
    LayerPropertiesItem * model=static_cast<LayerPropertiesItem *>(layers->model());
    model->selectAllLayers(false);
    model->selectLayers(l);
    selectedGraph->copyLayers();
    model->selectAllLayers(true);
  }
}

void GraphContentLayerProperties::on_appendLayers_clicked()
{
  TRACE;
  GraphContent * selectedGraph=currentGraph();
  selectedGraph->appendLayers();
  reset();
}

void GraphContentLayerProperties::on_prependLayers_clicked()
{
  TRACE;
  GraphContent * selectedGraph=currentGraph();
  selectedGraph->prependLayers();
  reset();
}

void GraphContentLayerProperties::on_removeLayers_clicked()
{
  TRACE;
  QModelIndexList l=layers->selectionModel()->selectedRows();
  if(l.count()>0) {
    if(Message::question(MSG_ID, tr("Remove layers"),
                                tr( "Do you want to remove all selected layers(%1)?" ).arg(l.count()),
                                Message::yes(), Message::no())==Message::Answer1) {
      return;
    }
    int nRemoved=static_cast<LayerPropertiesItem *>(layers->model())->removeLayers(l);
    reset();
    if(nRemoved<l.count()) {
      Message::information(MSG_ID, tr("Remove layers"),
                                tr( "%1 layers are not removable." ).arg(l.count()-nRemoved));
    }
  }
}

void GraphContentLayerProperties::on_moveLayerUp_clicked()
{
  TRACE;
  QModelIndexList l=layers->selectionModel()->selectedRows();
  if(l.count()>0) {
    QList<GraphContentLayer *> list;
    LayerPropertiesItem * model=static_cast<LayerPropertiesItem *>(layers->model());
    list=model->moveLayersUp(l);
    select(model, list);
  }
}

void GraphContentLayerProperties::on_moveLayerDown_clicked()
{
  TRACE;
  QModelIndexList l=layers->selectionModel()->selectedRows();
  if(l.count()>0) {
    QList<GraphContentLayer *> list;
    LayerPropertiesItem * model=static_cast<LayerPropertiesItem *>(layers->model());
    list=model->moveLayersDown(l);
    select(model, list);
  }
}

void GraphContentLayerProperties::select(LayerPropertiesItem * model, const QList<GraphContentLayer *>& list)
{
  TRACE;
  if(!list.isEmpty()) {
    int n=list.count();
    for(int i=0; i<n; i++) {
      QModelIndex index=model->indexOf(list.at(i));
      if(i==0) {
        layers->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Rows);
      }
      layers->selectionModel()->select(index,
                                       QItemSelectionModel::Select |
                                       QItemSelectionModel::Rows);
    }
  }
}

void LayerPropertiesItem::setGraph(GraphContent * g)
{
  TRACE;
  beginResetModel();
  _graph=g;
  endResetModel();
}

int LayerPropertiesItem::rowCount(const QModelIndex &) const
{
  TRACE;
  if(_graph) {
    return _graph->layerCount();
  } else {
    return 0;
  }
}

int LayerPropertiesItem::columnCount(const QModelIndex & ) const
{
  TRACE;
  return 4;
}

QModelIndex LayerPropertiesItem::indexOf(GraphContentLayer * l) const
{
  TRACE;
  return index(_graph->layerCount()-_graph->indexOf(l)-1, 0);
}

QVariant LayerPropertiesItem::data(const QModelIndex &index, int role) const
{
  TRACE;
  const GraphContentLayer * layer=_graph->layer(_graph->layerCount()-index.row()-1);
  switch (role) {
  case Qt::DisplayRole:
    switch(index.column()) {
    case 0: return layer->xml_tagName();
    case 1: return layer->objectName();
    case 2: return layer->opacity();
    default: return layer->comments();
    }
  case Qt::ToolTipRole:
    return layer->comments();
  default:
    return QVariant();
  }
}

bool LayerPropertiesItem::setData (const QModelIndex & index, const QVariant & value, int role)
{
  TRACE;
  GraphContentLayer * layer=_graph->layer(_graph->layerCount()-index.row()-1);
  switch (role) {
  case Qt::EditRole:
    switch(index.column()) {
    case 1:
      layer->setObjectName(value.toString());
      return true;
    case 2:
      layer->setOpacity(value.toDouble());
      layer->deepUpdate();
      return true;
    case 3:
      layer->setComments(value.toString());
      return true;
    default:
      return false;
    }
  default:
    return false;
  }
}

QVariant LayerPropertiesItem::headerData(int section, Qt::Orientation orientation, int role) const
{
  TRACE;
  if(role!=Qt::DisplayRole) return QVariant();
  if(orientation==Qt::Horizontal) {
    switch(section) {
    case 0: return tr("Type");
    case 1: return tr("Name");
    case 2: return tr("Opacity");
    default: return tr("Comments");
    }
  } else {
    return _graph->layerCount() - section;
  }
}

Qt::ItemFlags LayerPropertiesItem::flags (const QModelIndex & index) const
{
  TRACE;
  if(index.column()==0) {
    return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
  } else {
    return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
  }
}

int LayerPropertiesItem::removeLayers(QModelIndexList l)
{
  TRACE;
  int nRemoved=0;
  QList<GraphContentLayer *> layers;
  // First convert index list to layer pointers
  QModelIndex index;
  foreach(index, l) {
    GraphContentLayer * layer=_graph->layer(_graph->layerCount()-index.row()-1);
    if(layer->isRemovable()) {
      layers.append(layer);
      nRemoved++;
    }
  }
  beginResetModel();
  int n=layers.count();
  for(int i=0; i<n; i++) {
    _graph->removeLayer(layers.at(i));
  }
  endResetModel();
  return nRemoved;
}

QList<GraphContentLayer *> LayerPropertiesItem::moveLayersUp(QModelIndexList l)
{
  TRACE;
  QList<int> indexList;
  QModelIndex index;
  QList<GraphContentLayer *> layers;
  foreach(index, l) {
    indexList << _graph->layerCount()-index.row()-1;
  }
  std::sort(indexList.begin(), indexList.end());
  if(indexList.last()==_graph->layerCount()-1) {
    return layers; // Selected layers are already on top
  }
  // Transform into a ptr list, sorted from top to bottom
  int n=indexList.count();
  for(int i=n-1; i>=0; i--) {
    layers << _graph->layer(indexList.at(i));
  }
  beginResetModel();
  for(int i=0; i<n; i++) {
    _graph->moveLayerUp(layers.at(i));
  }
  endResetModel();
  return layers;
}

QList<GraphContentLayer *> LayerPropertiesItem::moveLayersDown(QModelIndexList l)
{
  TRACE;
  QList<int> indexList;
  QModelIndex index;
  QList<GraphContentLayer *> layers;
  foreach(index, l) {
    indexList << _graph->layerCount()-index.row()-1;
  }
  std::sort(indexList.begin(), indexList.end());
  if(indexList.first()==0) {
    return layers; // Selected layers are already on bottom
  }
  // Transform into a ptr list, sorted from bottom to top
  int n=indexList.count();
  for(int i=0;i<n;i++) {
    layers << _graph->layer(indexList.at(i));
  }
  beginResetModel();
  for(int i=0; i<n; i++) {
    _graph->moveLayerDown(layers.at(i));
  }
  endResetModel();
  return layers;
}

void LayerPropertiesItem::selectLayers(QModelIndexList l)
{
  TRACE;
  QModelIndex index;
  foreach(index, l) _graph->layer(_graph->layerCount()-index.row()-1)->setSelected(true);
}

void LayerPropertiesItem::selectAllLayers(bool s)
{
  TRACE;
  for(int i=_graph->layerCount()-1;i>=0;i--) {
    _graph->layer(i)->setSelected(s);
  }
}

QWidget * LayerPropertiesDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem &,
    const QModelIndex &index) const
{
  TRACE;
  const LayerPropertiesItem * item=qobject_cast<const LayerPropertiesItem *>(index.model());
  ASSERT(item);

  switch (index.column()) {
  case 2: {
      DoubleSpinBox * w=new DoubleSpinBox(parent);
      w->setMinimum(0.0);
      w->setMaximum(1.0);
      w->setSingleStep(0.05);
      connect(w,SIGNAL(valueChanged(double)), this, SLOT(commitWidgetData()));
      return w;
    }
  default: {
      QLineEdit * w=new QLineEdit(parent);
      w->setAlignment(Qt::AlignRight);
      return w;
    }
  }
}

void LayerPropertiesDelegate::commitWidgetData()
{
  QWidget * w=qobject_cast<QWidget *>(sender());
  if(w) {
    emit commitData(w);
  }
}

void LayerPropertiesDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
  TRACE;
  const LayerPropertiesItem * item=qobject_cast<const LayerPropertiesItem *>(index.model());
  ASSERT(item);

  switch (index.column()) {
  case 2: {
      DoubleSpinBox * w= qobject_cast<DoubleSpinBox *>(editor);
      if( !w) return;
      w->setValue(index.model() ->data(index).toDouble());
    }
  default: {
      QLineEdit * w= qobject_cast<QLineEdit *>(editor);
      if( !w) return;
      w->setText(index.model() ->data(index).toString());
      w->selectAll();
    }
    break;
  }
}

void LayerPropertiesDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
    const QModelIndex &index) const
{
  TRACE;
  const LayerPropertiesItem * item=qobject_cast<const LayerPropertiesItem *>(index.model());
  ASSERT(item);

  switch (index.column()) {
  case 2: {
      DoubleSpinBox * w=qobject_cast<DoubleSpinBox *>(editor);
      if( !w) return;
      if(model->data(index)!=w->value()) {
        model->setData(index, w->value());
      }
    }
    break;
  default: {
      QLineEdit * w=qobject_cast<QLineEdit *>(editor);
      if( !w) return;
      if(model->data(index)!=w->text()) {
        model->setData(index, w->text());
      }
    }
    break;
  }
}

} // namespace SciFigs
