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

#ifndef COREAPPLICATIONPRIVATE_H
#define COREAPPLICATIONPRIVATE_H

#include "QGpCoreToolsDLLExport.h"
#include "PackageInfo.h"
#include "AbstractStream.h"
#include "Message.h"
#include "Number.h"
#include "ApplicationHelp.h"

namespace QGpCoreTools {

  class PathTranslator;
  class PathTranslatorOptions;
  class ApplicationHelp;
  class Thread;

  class QGPCORETOOLS_EXPORT GlobalObject
  {
  public:
    GlobalObject() {}
    virtual ~GlobalObject() {}

    virtual bool inherits(const char * className);
  };

  class QGPCORETOOLS_EXPORT CoreApplicationPrivate
  {
    TRANSLATIONS("CoreApplicationPrivate")
  protected:
    CoreApplicationPrivate() {int argc=0; init(argc, nullptr, false);}
  public:
    CoreApplicationPrivate(int & argc, char ** argv, bool setPath=true) {init(argc, argv, setPath);}
    virtual ~CoreApplicationPrivate();

    static QString version();
    static QString version(QString item);
    static void signature(QTextStream& s);
    static QString authors();
    virtual bool hasGui() const=0;

    static void checkBlocks(bool showLib, bool showUndeleted, const char * file=nullptr, int line=0);
    static bool isBlockDamaged(void* v, const char * file, int line);
    static void printBlockInfo(void * ptr, const char * file, int line);
    static void setMemoryVerbose(bool v);

    static bool checkOptionArg(int& i, int argc, char ** argv, bool mandatory=true);
    static bool checkRemainingArgs(int argc, char ** argv);
    void showHelp(int& i, int argc, char ** argv, ApplicationHelp * (*help)());

    static CoreApplicationPrivate * instance() {return _self;}

    void debugUserInterrupts(bool d);
    static QString backTrace();
    static QString backTraceBug();
    static QString bugInfo(Message::Severity sev, const QString& msg);
    static QString appInfo();
    static void osSignal(int sigNum);
  #if(QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
    static void messageOutput(QtMsgType type, const QMessageLogContext &, const QString & msg);
    static void messageOutputAbort(QtMsgType type, const QMessageLogContext &, const QString & msg);
  #else
    static void messageOutput(QtMsgType type, const char *msg);
    static void messageOutputAbort(QtMsgType type, const char *msg);
  #endif
    void reportBug(Message::Severity sev, const QString& msg);
    void reportBugNow(Message::Severity sev, const QString& msg);
    static void reportBugDirectly(QString bug);

    QThread * mainThread() const {return _thread;}
    static void sleep(int ms);
    static QString currentThreadName();

    Message * messageHandler() const {return _messageHandler;}
    QString translatePath(const QString& originalFile, const PathTranslatorOptions& options);

    void setStream(AbstractStream * s, QThread * thread=nullptr);
    void beginRedirectStream(AbstractStream * s, QThread * thread=nullptr);
    void endRedirectStream(AbstractStream * s, QThread * thread=nullptr);
    bool freezeStream(bool b) const;
    void setStreamPrefix(const QString& prefix) {stream()->setPrefix(prefix);}
    QString streamPrefix() const {return stream()->prefix();}
    AbstractStream * stream(QThread * thread=nullptr) const;
    void threadDeleted(Thread * thread);

    static void addGlobalObject(GlobalObject * ptr);
    static void removeGlobalObject(GlobalObject * ptr);
    static void removeGlobalObjects(const char * className);

    static QString getStdin();
    static QStringList getFileList(int argc, char ** argv);
    static int userId();
    int terminalRows() const;
    int terminalCols() const;    
    static size_t getMemorySize();

    void setBatch(bool b) {_batch=b;}
    bool isBatch() const {return _batch;}

    void addPlugin(QObject * p);
    void deletePlugin(QObject * p);

    static QString htmlBugReport(const QString& platform, const QString& email=QString(),
                                 const QString &userInfo=QString());

    int maximumThreadCount() const {return _maximumThreadCount;}

    QSettings& settings();
    QSettings& globalSettings();

    static void setArgumentList(int argc, char ** argv);
    static const QStringList& argumentList() {return _argumentList;}
  protected:
    void setTerminalSize();
    void askForUserInterrupt();
    void setMessageHandler(Message * m);
    static QString constructorApplicationName();
    void destructorCleanUp();
    void initInternalDebugger(bool reportBugs);
    void initTranslations();
    void checkAtomicOperations();
    virtual void setHelp(ApplicationHelp * h);
    void printArgumentLists(int max) const;
    void updateArgumentLists();
    int startArgumentList(int index);
    void printLibraryPaths() const;
  private:
    void setLibraryPath();
    static QList<QStringList> argumentLists();
    void terminalSize() const;
    void init (int & argc, char ** argv, bool setPath);

    static CoreApplicationPrivate * _self;
    static QString _crashingThread;

    static QStringList _argumentList;
    QMap< QThread *, AbstractStream *> _streams;
    QThread * _thread;
    Message * _messageHandler;
    PathTranslator * _pathTranslator;
    Number * _numberEngine;
    QStringList _errors;
    QList<GlobalObject *> _globalPointers;
    QList<QObject *> _plugins;
    int _maximumThreadCount;
    bool _reportBugs, _debugUserInterrupts, _warningAsError, _batch;
    QSettings * _settings, * _globalSettings;

    mutable int _terminalCols, _terminalRows;
  };

  // Shorter name to access application function through static instance
  class App
  {
  public:
    static void setStream(AbstractStream * stream, QThread * thread=nullptr) {
      return CoreApplicationPrivate::instance()->setStream(stream, thread);
    }
    static AbstractStream * stream(QThread * thread) {
      return CoreApplicationPrivate::instance()->stream(thread);
    }
    static void beginRedirect(AbstractStream * stream, QThread * thread=nullptr) {
      return CoreApplicationPrivate::instance()->beginRedirectStream(stream, thread);
    }
    static void endRedirect(AbstractStream * stream, QThread * thread=nullptr) {
      return CoreApplicationPrivate::instance()->endRedirectStream(stream, thread);
    }
    static void log(const QString& val) {
      CoreApplicationPrivate::instance()->stream(nullptr)->send(0, val);
    }
    static void log(QThread * thread, const QString& val) {
      CoreApplicationPrivate::instance()->stream(thread)->send(0, val);
    }
    static void log(QThread * thread, int verbosity, const QString& val) {
      CoreApplicationPrivate::instance()->stream(thread)->send(verbosity, val);
    }
    static void log(int verbosity, const QString& val) {
      CoreApplicationPrivate::instance()->stream(nullptr)->send(verbosity, val);
    }
    static int verbosity() {
      return AbstractStream::applicationVerbosity();
    }
    static bool freeze(bool b) {
      return CoreApplicationPrivate::instance()->freezeStream(b);
    }
    static void setPrefix(const QString& prefix) {
      CoreApplicationPrivate::instance()->setStreamPrefix(prefix);
    }
    static QString prefix() {
      return CoreApplicationPrivate::instance()->streamPrefix();
    }
    static void sleep(int ms) {
      CoreApplicationPrivate::sleep(ms);
    }
  };


  // Convenient macro to check verbosity level without the cost of the string manipulation
  #define APP_LOG(verbosity, message) \
    if(AbstractStream::applicationVerbosity()>=verbosity) { \
      App::log(message); \
    }

  // Convenient macro definitions with code localization
  #ifdef MEMORY_CHECKER
  #define checkBlocks(showLib, showUndeleted) CoreApplicationPrivate::checkBlocks(showLib, showUndeleted, __FILE__, __LINE__)
  #define isBlockDamaged(ptr) CoreApplicationPrivate::isBlockDamaged(ptr, __FILE__, __LINE__)
  #define printBlockInfo(ptr) CoreApplicationPrivate::printBlockInfo(ptr,__FILE__, __LINE__)
  #endif

} // namespace QGpCoreTools

#endif // COREAPPLICATIONPRIVATE_H
