/***************************************************************************
**
**  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: 2004-09-02
**  Copyright: 2004-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <GeopsyGui.h>

#include "MainWindow.h"
#include "ToolFactory.h"
#include "Engine.h"
#include "GraphicWindow.h"
#include "SigSelectionDnD.h"
#include "GeopsyCoreVersion.h"

QString geopsyDLLDir();

void ToolSeparator::createToolActions(QObject * toolFactory)
{
  TRACE;
  QAction * a=new QAction(toolFactory);
  a->setSeparator(true);
  addAction(a);
}

QList<QAction *> ToolSeparator::createImportActions(QWidget * parent) const
{
  TRACE;
  QList<QAction *> actions;
  QAction * a=new QAction(parent);
  a->setSeparator(true);
  actions << a;
  return actions;
}

const char * ToolSeparator::interfaceVersion() const
{
  return GEOPSYCORE_VERSION;
}

ToolFactory::ToolFactory(QObject *parent)
  : QObject(parent)
{
  TRACE;
  // Build list of plugins that contain a GUI extension
  GeopsyPlugins * plugins=GeopsyCoreEngine::instance()->plugins();
  int n=plugins->count();
  for(int i=0; i<n; i++) {
    GeopsyCoreInterface * pCore=plugins->at(i);
    if(pCore) {
      GeopsyGuiInterface * pGui=qobject_cast<GeopsyGuiInterface *>(pCore);
      if(pGui) {
        _list.append(pGui);
      }
    } else {
      _list.append(new ToolSeparator);
    }
  }
  // Build the list of popular tools
  GeopsyPluginSettings reg;
  if(reg.hasPopular()) {
    QStringList l=reg.popular();
    int n=l.count();
    for(int i=0; i< n ;i++) {
      QAction * a=tag2Action(l.at(i));
      if(a) _popular.append(a);
    }
  }
}

ToolFactory::~ToolFactory()
{
  TRACE;
  // Save the list of popular tools
  QStringList l;
  int n=_popular.count();
  for(int i=0 ; i < n; i++ ) {
    l.append(action2Tag(_popular.at(i)));
  }
  GeopsyPluginSettings reg;
  reg.setPopular(l);
}

/*!
  Create actions for menu tools
*/
void ToolFactory::createToolActions()
{
  TRACE;
  QList<GeopsyGuiInterface *>::iterator it;
  for(it=_list.begin();it!=_list.end();++it) {
    (*it)->createToolActions(this);
  }
}

/*!
  Create actions for menu import
*/
QList<QAction *> ToolFactory::createImportActions(QWidget * parent) const
{
  TRACE;
  QList<QAction *> actions;
  if(Application::instance()->hasGui()) {
    QList<GeopsyGuiInterface *>::const_iterator it;
    for(it=_list.begin();it!=_list.end();++it) {
      actions << (*it)->createImportActions(parent);
    }
  }
  return actions;
}

void ToolFactory::setHelp(ApplicationHelp * h)
{
  TRACE;
  QList<GeopsyGuiInterface *>::iterator it;
  for(it=_list.begin();it!=_list.end();++it) {
    (*it)->setHelp(h);
  }
}

GeopsyGuiInterface * ToolFactory::tool(QString toolTag)
{
  TRACE;
  QList<GeopsyGuiInterface *>::iterator it;
  GeopsyGuiInterface * info;
  for(it=_list.begin();it!=_list.end();++it) {
    info=*it;
    if(info->tag()==toolTag) {
      return info;
    }
  }
  return nullptr;
}

/*!
  All actions made available by the plugins are added to widget \a w
  The original actions are received.
*/
void ToolFactory::addActions(QWidget * w)
{
  TRACE;
  w->addActions(findChildren<QAction *>());
}

/*!
  The popular actions are copied and added to menu, and the trigerred() signal is
  connected to the \a slot or \a receiver. The data of the action is set to the pointer of the original
  action.
*/
bool ToolFactory::addPopularActions(QMenu * menu, const QObject * receiver, const char * slot)
{
  TRACE;
  QList<QAction *>::iterator it;
  for(it=_popular.begin();it!=_popular.end();++it) {
    const QAction& original=**it;
    QAction * a=new QAction(original.icon(), original.text(), menu);
    a->setToolTip(original.toolTip());
    QByteArray p((char *) &(*it), sizeof(QAction *));
    a->setData(p);
    connect(a, SIGNAL(triggered()), receiver, slot);
    menu->addAction(a);
  }
  return !_popular.empty();
}

void ToolFactory::showTool()
{
  TRACE;
  QAction * a=qobject_cast<QAction *>(sender());
  if(a) {
    SubPoolWindow * win=WindowEnvironment::window(a->parentWidget())->currentSubPoolWin();
    showTool(win, a);
  }
}

void ToolFactory::newGraphicWindow(QWidget * parent, const SubSignalPool& subPool)
{
  TRACE;
  SigSelectionDnD * dnd=qobject_cast<SigSelectionDnD *>(sender());
  SubPoolWindow * w=GeopsyGuiEngine::instance()->newGraphicWindow(parent, subPool);
  if(dnd) {
    QToolButton * b=qobject_cast<QToolButton *>(dnd->source());
    if(b) showTool(w, b->defaultAction());
  }
}

/*!
  Return a string identifying the action: "tagName slotNumber"
*/
QString ToolFactory::action2Tag(QAction * a)
{
  TRACE;
  QList<GeopsyGuiInterface *>::iterator it;
  GeopsyGuiInterface * info;
  int slot;
  for(it=_list.begin();it!=_list.end();++it) {
    info=*it;
    slot=info->slotOf(a);
    if(slot > -1) {
      return info->tag()+" "+QString::number(slot);
    }
  }
  return QString();
}

/*!
  Return an action pointer from string: "tagName slotNumber"
*/
QAction * ToolFactory::tag2Action(QString tag)
{
  TRACE;
  QString toolTag=tag.section(" ", 0,0);
  int slot=tag.section(" ", 1,1).toInt();

  QList<GeopsyGuiInterface *>::iterator it;
  GeopsyGuiInterface * info;
  for(it=_list.begin();it!=_list.end();++it) {
    info=*it;
    if(info->tag()==toolTag) {
      if(slot<info->slotCount())
        return info->action(slot);
      else
        return nullptr;
    }
  }
  return nullptr;
}

void ToolFactory::showTool(SubPoolWindow * win, QAction * a)
{
  TRACE;
  QList<GeopsyGuiInterface *>::iterator it;
  GeopsyGuiInterface * info;
  int slot;
  for(it=_list.begin();it!=_list.end();++it) {
    info=*it;
    slot=info->slotOf(a);
    if(slot>-1) {
      showTool(win, info, slot);
      break;
    }
  }
}

void ToolFactory::showTool(SubPoolWindow * win, GeopsyGuiInterface * info, int slot)
{
  TRACE;
  if(!win || win->subPool().isEmpty()) return;
  AbstractToolWidget * tool=win->tool();
  if(tool) {
    if(tool->isLibSlot(info, slot)) tool->showTool();
    else {
      Message::warning(MSG_ID, tr("Toolbox"),tr("The toolbox attached to this viewer is not of type \"%1\"."
                          " To change to another type, open another viewer.").arg(info->slotText(slot)),
                          Message::cancel());
    }
  } else {
    addPopular(info->action(slot));
    tool=info->createTool(slot, nullptr);
    tool->setLibSlot(info, slot);
    if(win->inherits("GraphicWindow")) {
      connect(tool, SIGNAL(updateSubPool()), win, SLOT(subPoolUpdate()));
      GraphicWindow * gwin=static_cast<GraphicWindow *>(win);
      tool->setLayers(gwin->signalLayer(), gwin->pickLayer(), gwin->timeWindowLayer());
    }
    if(tool->setSubPool(win)) {
      MultiDocumentSubWindow * toolw=GeopsyGuiEngine::instance()->addSubWindow(win, tool);
      toolw->setUserClosable(false);
      connect(toolw, SIGNAL(aboutToActivate()), tool, SLOT(windowActivated()));
      win->setTool(tool);
      tool->restoreFields();
    } else {
      delete tool;
    }
  }
}

/*!
  Maintain the list of TOOLFACTORY_NPOPULAR popular actions
*/
void ToolFactory::addPopular(QAction * a)
{
  TRACE;
  int i=_popular.indexOf(a);
  if(i>-1) {
    _popular.removeAt(i);
  }
  _popular.prepend(a);
  while(_popular.count()>TOOLFACTORY_NPOPULAR) _popular.removeLast();
}

void ToolFactory::infoTools()
{
  TRACE;
  printf( "\nTools :\n\n" );
  QList<GeopsyGuiInterface *>::iterator it;
  GeopsyGuiInterface * info;
  for(it=_list.begin();it!=_list.end();++it) {
    info=*it;
    printf( "  %s : ", info->tag().toLatin1().data());
    int n=info->slotCount();
    for(int i=0;i < n;i++ ) {
      if(i > 0) printf( ", " );
      printf( "%s(%i)", info->slotText(i).toLatin1().data(), i);
    }
    printf( "\n" );
  }
}

void ToolFactory::createPreferenceTabs(QTabWidget * tab)
{
  TRACE;
  QList<GeopsyGuiInterface *>::iterator it;
  for(it=_list.begin();it!=_list.end();++it) {
    (*it)->addPreferenceTab(tab);
  }
}

void ToolFactory::setPreferences()
{
  TRACE;
  QList<GeopsyGuiInterface *>::iterator it;
  for(it=_list.begin();it!=_list.end();++it) {
    (*it)->setPreferences();
  }
}
