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

#include <QGpCoreTools.h>

#include "DinverCoreEngine.h"
#include "DinverCoreInstallPath.h"
#include "DinverCoreVersion.h"
#include "DinverInterface.h"
#include "PluginSettings.h"

namespace DinverCore {

  DinverCoreEngine * DinverCoreEngine::_self=nullptr;

  const char * dinvercoreVersion()
  {
    return DINVERCORE_VERSION;
  }

  DinverCoreEngine::DinverCoreEngine()
  {
    TRACE;
    // Make sure dinverCore is not NULL, if so set this object as the current dinver core
    if(!_self) _self=this;
    _plugin=nullptr;
  }

  DinverCoreEngine::~DinverCoreEngine()
  {
    TRACE;
    delete _plugin;
  }

  /*!
    Add a new window in Dinver framework.
    This function does nothing by default, re-implement it.
  */
  MultiDocumentSubWindow * DinverCoreEngine::addSubWindow(QWidget * parent, QWidget * w)
  {
    TRACE;
    Q_UNUSED(parent)
    Q_UNUSED(w)
    return nullptr;
  }

  /*!
    Remove a window in Dinver framework.
    This function does nothing by default, re-implement it.
  */
  void DinverCoreEngine::removeSubWindow(QWidget * w)
  {
    TRACE;
    Q_UNUSED(w)
  }

  /*!
    Remove a window in Dinver framework.
    This function does nothing by default, re-implement it.
  */
  void DinverCoreEngine::activateSubWindow(QWidget * w)
  {
    TRACE;
    Q_UNUSED(w)
  }

  /*!
    Returns the reports of selected runs.
    This function returns nothing by default, re-implement it.
  */
  QStringList DinverCoreEngine::selectedReports(QWidget *, const QString&) const
  {
    return QStringList();
  }

  QList<const AbstractForward *> DinverCoreEngine::forwardList(QWidget *, const QStringList&) const
  {
    return QList<const AbstractForward *>();
  }

  QStringList DinverCoreEngine::defaultPluginPaths()
  {
    TRACE;
    DinverPluginSettings reg;
    QStringList paths;
    QString dll;
  #ifdef Q_OS_MAC
    dll=QString("/Library/Geopsy.org/%1/plugins").arg(DINVERCORE_VERSION_TYPE);
  #elif defined(Q_OS_WIN)
    dll=QCoreApplication::applicationDirPath();
  #else
    const PackageInfo * info=PackageInfo::package("dinver");
    if(info) {
      dll=info->dllDir();
    }
  #endif
    App::log(1, tr("Adding default plugin path: %1\n").arg(dll));
    if(!dll.isEmpty()) {
      QDir d(dll);
      if(d.exists()) {
        paths << dll;
        reg.setPaths(paths);
      }
    }
    return paths;
  }

  bool DinverCoreEngine::setDefaultPlugin(QString pluginFile, bool debug)
  {
    TRACE;
    ASSERT(!_plugin);
    _plugin=loadPlugin(pluginFile, debug);
    return _plugin;
  }

  DinverInterface * DinverCoreEngine::loadPlugin(QString pluginFile, bool debug)
  {
    TRACE;
    if(debug) {
      App::log(tr("Load library %1\n").arg(pluginFile) );
    }
    // test loading
    QPluginLoader pluginTest;
    pluginTest.setFileName(pluginFile);
    QObject * instance=pluginTest.instance();
    if(instance) {
      if(debug) {
        App::log(tr("  Library loaded successfully\n") );
      }
      DinverInterface * dinverInstance=qobject_cast<DinverInterface *>(instance);
      if(dinverInstance) {
        if(debug) {
          App::log(tr("  Dinver plugin interface version=%1\n")
                   .arg(dinverInstance->interfaceVersion()));
        }
        return dinverInstance;
      }
      CoreApplication::instance()->deletePlugin(instance);
    } else {
      if(debug) {
        App::log("  "+pluginTest.errorString()+"\n");
      }
    }
    return nullptr;
  }

  /*!
    Return the complete path to plugin with \a tag
  */
  QString DinverCoreEngine::pluginFile(QString tag, bool debug)
  {
    TRACE;
    QStringList libs;
    // Make sure plugins are correctly initialized
    DinverPluginSettings reg;
    if(!reg.hasPluginList()) {
      libs=File::getLibList(defaultPluginPaths());
    } else {
      libs=reg.pluginList();
    }

    int n=libs.count();
    for(int i=0; i<n; i++) {
      QString libFile=libs.at(i);
      DinverInterface * dinverInstance=loadPlugin(libFile, debug);
      if(dinverInstance) {
        if(dinverInstance->isValid() && dinverInstance->tag()==tag) {
          CoreApplication::instance()->deletePlugin(dinverInstance);
          return libFile;
        }
        CoreApplication::instance()->deletePlugin(dinverInstance);
      }
    }
    return QString();
  }

  void DinverCoreEngine::printPluginList()
  {
    TRACE;
    QStringList libs;
    // Make sure plugins are correctly initialized
    DinverPluginSettings reg;
    if(!reg.hasPluginList()) {
      libs=File::getLibList(defaultPluginPaths());
    } else {
      libs=reg.pluginList();
    }

    int n=libs.count();
    for(int i=0;i < n;i++ ) {
      QString libFile=libs.at(i);
      DinverInterface * dinverInstance=loadPlugin(libFile, false);
      if(dinverInstance) {
        if(dinverInstance->isValid()) {
          ApplicationHelp::print(dinverInstance->title()+"-"+dinverInstance->version()+" (tag="+dinverInstance->tag()+")", nullptr);
        }
        CoreApplication::instance()->deletePlugin(dinverInstance);
      }
    }
  }

  DinverPluginSettings::DinverPluginSettings()
  {
    _reg=&CoreApplication::instance()->settings();
    _reg->beginGroup("Plugins");
    _reg->beginGroup(DINVERCORE_VERSION_TYPE);
  }

  void DinverPluginSettings::setDefaultPlugin(int i)
  {
    _reg->setValue("defaultPlugin", i);
  }

  int DinverPluginSettings::defaultPlugin() const
  {
    return _reg->value("defaultPlugin", 0).toInt();
  }

} // namespace DinverCore
