/***************************************************************************
**
**  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: 2007-02-19
**  Copyright: 2007-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <GeopsyGui.h>

#include "Engine.h"
#include "GroupView.h"
#include "SigSelectionDnD.h"
#include "ToolFactory.h"
#include "GroupProperties.h"
#include "MainWindow.h"

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

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
GroupView::GroupView(QWidget * parent)
    : QTreeView(parent)
{
  TRACE;
  sigDnD=new SigSelectionDnD(this);
  viewport()->installEventFilter(sigDnD);
  sigDnD->setDropEnabled(false);
  connect(sigDnD, SIGNAL(getSelection(SubSignalPool& )),
          this, SLOT(getSelection(SubSignalPool& )));

  _menu=new QMenu(this);
  setContextMenuPolicy(Qt::DefaultContextMenu);

  SignalGroupItem * model=new SignalGroupItem(this);
  model->setDatabase(Engine::instance()->database(this));
  setModel(model);
  header()->hide();
  setSelectionBehavior(QAbstractItemView::SelectRows);
  setSelectionMode(QAbstractItemView::ExtendedSelection);
  setAutoExpandDelay(1000);
  setAnimated(true);
  setUniformRowHeights(true); // Speedup rendering

  setMode(Browse);
}

inline SignalGroupItem * GroupView::model() const
{
  return static_cast<SignalGroupItem *>(QTreeView::model());
}

void GroupView::addActions()
{
  TRACE;

  MainWindow * mw=WindowEnvironment::instance()->window(this);

  _tableAction=MainWindow::newAction(*mw->viewTableAction(), this);
  connect(_tableAction, SIGNAL(triggered()), this, SLOT(newTableWindow()) );
  _graphicAction=MainWindow::newAction(*mw->viewGraphicAction(), this);
  connect(_graphicAction, SIGNAL(triggered()), this, SLOT(newGraphicWindow()) );
  _mapAction=MainWindow::newAction(*mw->viewMapAction(), this);
  connect(_mapAction, SIGNAL(triggered()), this, SLOT(newMapWindow()) );
  _chronogramAction=MainWindow::newAction(*mw->viewChronogramAction(), this);
  connect(_chronogramAction, SIGNAL(triggered()), this, SLOT(newChronogramWindow()));

  _newFolderAction=new QAction(this);
  _newFolderAction->setText(tr("New folder"));
  connect(_newFolderAction, SIGNAL(triggered()), this, SLOT(newFolder()) );

  _expandAllAction=new QAction(this);
  _expandAllAction->setText(tr("Expand all"));
  connect(_expandAllAction, SIGNAL(triggered()), this, SLOT(expandAll()));

  _collapseAllAction=new QAction(this);
  _collapseAllAction->setText(tr("Collapse all"));
  connect(_collapseAllAction, SIGNAL(triggered()), this, SLOT(collapseAll()));
}

void GroupView::getSelection(SubSignalPool& sel)
{
  TRACE;
  model()->getSelection(sel, *selectionModel());
}

/*!
  If \a readWriteOnly is true, only the modifiable groups are returned.
*/
QList<AbstractSignalGroup *> GroupView::getSelection(bool readWriteOnly, bool warn)
{
  TRACE;
  QList<AbstractSignalGroup *> sel;
  model()->getSelection(sel, *selectionModel());
  if(readWriteOnly) {
    QMutableListIterator<AbstractSignalGroup *> it(sel);
    while (it.hasNext()) {
      AbstractSignalGroup * g=it.next();
      if(g->readOnly()) {
        if(warn) {
          Message::warning(MSG_ID, tr("Group properties"),
                           tr("Group %1 cannot be edited").arg(g->pathName()), Message::ok(), true);
        }
        it.remove();
      }
    }
  }
  return sel;
}

QSize GroupView::sizeHint() const
{
  TRACE;
  return QSize(200, -1);
}

void GroupView::contextMenuEvent(QContextMenuEvent * e)
{
  TRACE;
  _menu->clear();

  QList<AbstractSignalGroup *> selReadWrite=getSelection(true);
  QList<AbstractSignalGroup *> selAll=getSelection(false);
  QModelIndex index=indexAt(e->pos());
  if(selAll.isEmpty()) {
    _menu->addAction(_newFolderAction);
  } else {
    if(index.isValid()) {
      AbstractSignalGroup * g=SignalGroupItem::group(index);
      if(!selAll.contains(g)) {
        selAll.clear();
        selAll.append(g);
        if(!g->readOnly()) {
          selReadWrite.append(g);
        }
        selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
      }
      if(!selReadWrite.isEmpty()) {
        _menu->addAction(tr("Properties"), this, SLOT(properties()));
        _menu->addAction(tr("Remove"), this, SLOT(remove()));
        _menu->addSeparator();
      }
      _menu->addAction(_tableAction);
      _menu->addAction(_graphicAction);
      _menu->addAction(_mapAction);
      _menu->addAction(_chronogramAction);
      QAction * lastSep=_menu->addSeparator();
      ToolFactory * f=WindowEnvironment::instance()->window(this)->toolFactory();
      if(!f->addPopularActions(_menu, this, SLOT(newTool()))) {
        _menu->removeAction(lastSep);
      }
      if(selAll.count()==1) {
        _menu->addSeparator();
        _menu->addAction(_newFolderAction);
      }
    } else {
      selectionModel()->clear();
    }
  }
  _menu->addSeparator();
  _menu->addAction(_expandAllAction);
  _menu->addAction(_collapseAllAction);
  _menu->popup(mapToGlobal(e->pos()));
}

void GroupView::mouseDoubleClickEvent(QMouseEvent * e)
{
  TRACE;
  QModelIndex index=indexAt(e->pos());
  if(index.isValid()) newGraphicWindow();
}

SubPoolWindow * GroupView::newTableWindow()
{
  TRACE;
  SubSignalPool sel;
  getSelection(sel);
  return Engine::instance()->newTableWindow(this, sel);
}

SubPoolWindow * GroupView::newGraphicWindow()
{
  TRACE;
  SubSignalPool sel;
  getSelection(sel);
  return Engine::instance()->newGraphicWindow(this, sel);
}

SubPoolWindow * GroupView::newMapWindow()
{
  TRACE;
  SubSignalPool sel;
  getSelection(sel);
  return Engine::instance()->newMapWindow(this, sel);
}

SubPoolWindow * GroupView::newChronogramWindow()
{
  TRACE;
  SubSignalPool sel;
  getSelection(sel);
  return Engine::instance()->newChronogramWindow(this, sel);
}

void GroupView::newTool()
{
  TRACE;
  QAction * a=qobject_cast<QAction *>(sender());
  if(a) {
    QAction * original=*reinterpret_cast<QAction **>(a->data().toByteArray().data());
    MainWindow * mw=WindowEnvironment::window(this);
    mw->toolFactory()->showTool(newGraphicWindow(), original);
  }
}

/*!
  Unparents all selected groups. Then, it is safe to delete them one by one
  because they are no longer connected by any parentship.
*/
void GroupView::remove()
{
  TRACE;
  QList<AbstractSignalGroup *> sel=getSelection(true, true);
  for(QList<AbstractSignalGroup *>::Iterator it=sel.begin();it!=sel.end();++it) {
    (*it)->setParent(nullptr);
  }
  qDeleteAll(sel);
}

void GroupView::properties()
{
  TRACE;
  QList<AbstractSignalGroup *> sel=getSelection(true, true);
  if(sel.isEmpty()) {
    return;
  }
  Dialog * d=new Dialog(this);
  GroupProperties * dg=new GroupProperties(this);
  d->setMainWidget(dg);
  Settings::getWidget(d, "GroupProperties");
  dg->removeMultiGroup();
  dg->setDatabase(Engine::instance()->database(this));
  dg->setValues(sel);
  if(d->exec()==QDialog::Accepted) {
    Settings::setWidget(d, "GroupProperties");
    AbstractSignalGroup * g;
    foreach(g, sel) {
      dg->setProperties(g);
    }
    if(dg->propertyValue(GroupProperties::Folder)->isTouched()) {
      AbstractSignalGroup * p=dg->folder();
      foreach(g, sel) {
        g->setParent(p);
      }
    }
  }
  delete d;
}

/*!
  Called only if the group selection is reduced to one or empty
*/
void GroupView::newFolder()
{
  TRACE;
  QList<AbstractSignalGroup *> sel=getSelection(false);
  if(sel.count()!=1) {
    return;
  }
  AbstractSignalGroup * parent=sel.first();
  AbstractSignalGroup * g=new SignalGroupFolder(parent);
  g->setName(tr("New folder"));
}

void GroupView::select(const QRegExp& exp)
{
  TRACE;
  selectionModel()->clear();
  select(Engine::instance()->database(this)->masterGroup(), exp);
}

void GroupView::select(AbstractSignalGroup * parent, const QRegExp& exp)
{
  TRACE;
  if(exp.indexIn(parent->name())>=0) {
    QItemSelectionModel * sm=selectionModel();
    sm->select(model()->index(parent), QItemSelectionModel::Select);
  }
  for(AbstractSignalGroup::iterator it=parent->begin(); it!=parent->end(); it++) {
    select(static_cast<AbstractSignalGroup *>(*it), exp);
  }
}

void GroupView::setMode(Mode m)
{
  TRACE;
  switch(m) {
  case GroupView::Browse:
    sigDnD->enableFilter();
    setDragDropMode(QAbstractItemView::NoDragDrop);
    setEditTriggers(QAbstractItemView::NoEditTriggers);
    break;
  case GroupView::Rename:
    sigDnD->disableFilter();
    setDragDropMode(QAbstractItemView::NoDragDrop);
    setEditTriggers(QAbstractItemView::AllEditTriggers);
    break;
  case GroupView::Sort:
    sigDnD->disableFilter();
    setDragDropMode(QAbstractItemView::DragDrop);
    setEditTriggers(QAbstractItemView::NoEditTriggers);
    break;
  }
  mode=m;
}
