/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the msXpertSuite project.
 *
 * The msXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * This program 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.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */

/////////////////////// Std includes
#include <ctime>
#include <iostream>

/////////////////////// Qt includes
#include <QApplication>
#include <QClipboard>
#include <QCommandLineParser>
#include <QDebug>
#include <QLocale>
#include <QQmlApplicationEngine>
#include <QStringList>
#include <QTableView>
#include <QVector>
#include <QtGlobal>
#include <QtQml/qqmlextensionplugin.h>

/////////////////////// pappso includes

// #include <pappsomspp/core/qml/pappsoqml.h>
#include <pappsomspp/core/processing/combiners/mzintegrationparams.h>

/////////////////////// Local includes
#include "config.h"
#include "gui/Application.hpp"
#include "gui/ProgramWindow.hpp"

namespace MsXpS
{
namespace MineXpert
{

#define APPLICATION_NAME "MineXpert3"

void printGreetings();
void printHelp();
void printVersion();
void printConfig(const QString & = QString());

void
printHelp()
{
  QString help  = QObject::tr("The following options are available:\n");
  help         += QObject::tr("? | -h | --help : Display this help\n");
  help         += QObject::tr("-v | --version : Display version info\n");
  help += QObject::tr("-c | --config : Display the software configuration\n");
  help += QObject::tr("-l | --license : Display the software license\n");
  help += QObject::tr("\n");

  std::cout << help.toStdString();
}

void
printGreetings()
{
  QString greetings =
    QString("%1, version %2\n\n").arg(APPLICATION_NAME, VERSION);

  greetings += QString("Type '%1 --help' for help\n\n").arg(APPLICATION_NAME);

  greetings += QString(
                 "%1 is Copyright 2016-2025 \n"
                 "by Filippo Rusconi.\n\n"
                 "%1 comes with ABSOLUTELY NO WARRANTY.\n"
                 "%1 is free software, "
                 "covered by the GNU General\n"
                 "Public License Version 3 or later, "
                 "and you are welcome to change it\n"
                 "and/or distribute copies of it under "
                 "certain conditions.\n"
                 "Check the file COPYING in the distribution "
                 "and/or the\n"
                 "'Help/About(Ctrl+H)' menu item of the program.\n"
                 "\nHappy MineXpert'ing!\n\n")
                 .arg(APPLICATION_NAME);

  std::cout << greetings.toStdString();
}

void
printVersion()
{
  QString version = QString(
                      "%1, version %2 -- "
                      "Compiled against Qt, version %3\n")
                      .arg(APPLICATION_NAME, VERSION, QT_VERSION_STR);

  std::cout << version.toStdString();
}

void
printConfig(const QString &execName)
{
  QString config = QString(
                     "%1: "
                     "Compiled with the following configuration:\n"
                     "EXECUTABLE BINARY FILE: = %2\n"
                     "PROJECT_INSTALL_BIN_DIR = %3\n"
                     "PROJECT_INSTALL_LIB_DIR = %4\n"
                     "PROJECT_INSTALL_LIB_QML_DIR = %5\n"
                     "PROJECT_INSTALL_DATA_DIR = %6\n"
                     "PROJECT_INSTALL_DOC_DIR = %7\n")
                     .arg(APPLICATION_NAME,
                          execName,
                          PROJECT_INSTALL_BIN_DIR,
                          PROJECT_INSTALL_LIB_DIR,
                          PROJECT_INSTALL_LIB_QML_DIR,
                          PROJECT_INSTALL_DATA_DIR,
                          PROJECT_INSTALL_DOC_DIR);

  std::cout << config.toStdString();
}

} // namespace MineXpert
} // namespace MsXpS

void
myMessageOutputFormat(QtMsgType type,
                      const QMessageLogContext &context,
                      const QString &msg)
{
// Define ANSI color codes
#define RESET   "\033[0m"
#define BLACK   "\033[30m"
#define RED     "\033[31m"
#define GREEN   "\033[32m"
#define YELLOW  "\033[33m"
#define BLUE    "\033[34m"
#define MAGENTA "\033[35m"
#define CYAN    "\033[36m"
#define WHITE   "\033[36m"
#define BOLD    "\033[1m"

  QByteArray localMsg  = msg.toLocal8Bit();
  const char *file     = context.file ? context.file : "";
  const char *function = context.function ? context.function : "";

  QString prefix;
  QString color;

  switch(type)
    {
      case QtInfoMsg:
        prefix = "INFO: ";
        color  = QString("%1").arg(GREEN);
        break;
      case QtWarningMsg:
        prefix = "WARNING: ";
        color  = QString("%1").arg(BLUE);
        break;
      case QtCriticalMsg:
        prefix = "CRITICAL: ";
        color  = QString("%1").arg(MAGENTA);
        break;
      case QtFatalMsg:
        prefix = "FATAL: ";
        color  = QString("%1").arg(RED);
        break;
      case QtDebugMsg:
        prefix = "DEBUG: ";
        color  = QString("%1").arg(YELLOW);
        break;
    }

  fprintf(stderr,
          "%s%s%s%s:%s%u%s\n%s\n%s======> %s%s\n\n",
          color.toLocal8Bit().constData(),
          prefix.toLocal8Bit().constData(),
          RESET,
          file,
          color.toLocal8Bit().constData(),
          context.line,
          RESET,
          function,
          color.toLocal8Bit().constData(),
          localMsg.constData(),
          RESET);

  fflush(stderr); // Ensure it actually appears
}

namespace MsXpS
{
namespace MineXpert
{


#if 0

// Magic bullet for global keyboard key tracking.
class GlobalKeyTracker: public QObject
{
  protected:
  bool
  eventFilter(QObject *obj, QEvent *event) override
  {
    if(event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
      {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        QKeySequence keySequence(keyEvent->key() | keyEvent->modifiers());

        // Get detailed key information
        qDebug() << "=== KEY EVENT ==="
                 << "\nWidget:" << obj->metaObject()->className()
                 << "\nObject name:" << obj->objectName()
                 << "\nKey:" << keyEvent->key() << "\nText:" << keyEvent->text()
                 << "\nKey sequence:" << keySequence.toString()
                 << "\nNative scan code:" << keyEvent->nativeScanCode()
                 << "\nNative virtual key:" << keyEvent->nativeVirtualKey()
                 << "\nModifiers:" << keyEvent->modifiers() << "\nType:"
                 << (event->type() == QEvent::KeyPress ? "Press" : "Release");
      }
    return false; // Don't block the event
  }
};

#endif

} // namespace MineXpert
} // namespace MsXpS

int
main(int argc, char **argv)
{
  // Set the debugging message formatting pattern.
  // qSetMessagePattern(QString("Debug: %{file}:%{line}-%{function}():
  // %{message}"));
  qInstallMessageHandler(myMessageOutputFormat);
  // qInfo() << "Installed the message handler.";

  QString project_name = APPLICATION_NAME;

  // Qt stuff starts here.
  MsXpS::MineXpert::Application application(
    argc, argv, project_name.toLatin1().data());
  application.setApplicationVersion(VERSION);
  application.setApplicationName(project_name);
  application.setApplicationDisplayName(project_name);

#if 0
  // This is magic debugging stuff to see how a keyboard stroke is gotten
  // from the application (what widget, what code...)
  MsXpS::MineXpert::GlobalKeyTracker *key_tracker_p =
    new MsXpS::MineXpert::GlobalKeyTracker();
  application.installEventFilter(key_tracker_p);
#endif

#if 0
  // Useful debug code to highlight the widget that gets focus.
  QObject::connect(
    qApp,
    &QApplication::focusChanged,
    &application,
    [](QWidget *old, QWidget *now) {
      if(old)
        old->setStyleSheet(""); // Remove highlight from old widget
      if(now)
        {
          qDebug() << "Focus changed to:" << now->metaObject()->className();
          now->setStyleSheet(
            "border: 2px solid red;"); // Highlight focused widget
        }
    });
#endif


  QCommandLineParser parser;

  parser.setApplicationDescription(
    "Visualization and exploration of MS^n mass spectrometric data");

  QCommandLineOption helpOption("help", "Display this help.");
  parser.addOption(helpOption);

  QCommandLineOption versionOption(QStringList() << "v" << "version",
                                   "Display version info.");
  parser.addOption(versionOption);

  QCommandLineOption configOption(QStringList() << "c" << "config",
                                  "Display the software configuration.");
  parser.addOption(configOption);

  QCommandLineOption licenseOption(QStringList() << "l" << "license",
                                   "Display the software license.");
  parser.addOption(licenseOption);

  QCommandLineOption hostConnectionOption(
    QStringList() << "h" << "host", "Connect to host.", "host_ip");
  parser.addOption(hostConnectionOption);

  QCommandLineOption portListeningOption(
    QStringList() << "p" << "port", "Listen to port.", "port_numb");
  parser.addOption(portListeningOption);

  // Process the actual command line arguments given by the user
  parser.process(application);

  // Check if options are set
  bool isHelpOption    = parser.isSet(helpOption);
  bool isVersionOption = parser.isSet(versionOption);
  bool isConfigOption  = parser.isSet(configOption);
  bool isLicenseOption = parser.isSet(licenseOption);
  bool isHostOption    = parser.isSet(hostConnectionOption);
  bool isPortOption    = parser.isSet(portListeningOption);

  // Handle positional arguments (e.g., filenames)
  QStringList file_names;

  const QStringList positional_args = parser.positionalArguments();
  for(const QString &positional_arg : positional_args)
    {
      // qDebug() << "Positional argument:" << positional_arg;
      file_names.append(positional_arg);
    }

  application.processEvents();

  if(isHelpOption)
    {
      MsXpS::MineXpert::printHelp();
      return 0;
    }

  if(isVersionOption)
    {
      MsXpS::MineXpert::printVersion();
      return 0;
    }

  if(isLicenseOption)
    {
      MsXpS::MineXpert::printGreetings();
      return 0;
    }

  if(isConfigOption)
    {
      MsXpS::MineXpert::printConfig();
      return 0;
    }

  // Only now do we manage to show the main window, since all
  // the options that do not require it have dealt with.

  MsXpS::MineXpert::ProgramWindow *main_window_p =
    new MsXpS::MineXpert::ProgramWindow(
      nullptr, project_name, "Main program window");

  // It is crucial that we set to the Application the pointer to the
  // ProgramWindow as soon as possible. Many functions, including in
  // windows allocated by ProgramWindow during its construction time
  // need to be able to call application.getProgramWindow().
  application.setProgramWindow(main_window_p);

  // qDebug() << "Set the pointer to ProgramWindow in Application to "
  //          << application.getProgramWindow();

  main_window_p->setupWindow();
  dynamic_cast<QWidget *>(main_window_p)->show();


  if(file_names.size())
    {
      for(int iter = 0; iter < file_names.size(); ++iter)
        {
          QString file_name = file_names.at(iter);
          QFileInfo file_info(file_name);
          if(file_info.exists())
            main_window_p->openMassSpectrometryFile(file_name, false);
          else
            main_window_p->openMassSpectrometryFileDlg(file_name, false);
        }
    }
  else
    MsXpS::MineXpert::printGreetings();

  // Now set the default locale to american english.
  QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates));

  if(isHostOption)
    qDebug() << "host option value:" << parser.value(hostConnectionOption);

  if(isPortOption)
    qDebug() << "Port option value:"
             << parser.value(portListeningOption).toInt();

  if(isHostOption && isPortOption)
    {
      qDebug() << "Networking is asked.";
      // Start the client
      main_window_p->startClient(parser.value(hostConnectionOption),
                                 parser.value(portListeningOption).toInt());
    }
  else
    {
      // qDebug() << "No networking is asked for.";
    }

  /*
  // Q_IMPORT_QML_PLUGIN(Ellipseplugin)
  // Q_IMPORT_QML_PLUGIN(DataPointplugin)

  QQmlApplicationEngine engine;
  QObject::connect(
    &engine,
    &QQmlApplicationEngine::objectCreationFailed,
    &application,
    []() {
      QCoreApplication::exit(-1);
    },
    Qt::QueuedConnection);

  qInfo() << "PROJECT_INSTALL_LIB_QML_DIR:"
          << QString(PROJECT_INSTALL_LIB_QML_DIR);

  QStringList import_paths = engine.importPathList();
  qInfo() << "Engine default import paths:" << import_paths;

  // import_paths.clear();
  // import_paths.append("/usr/lib/qml");
  // import_paths.append("/usr/lib/prova");
  // import_paths.prepend(QString(PROJECT_INSTALL_LIB_QML_DIR));
  // qInfo() << "Now the import paths are:" << import_paths;
  // engine.setImportPathList(import_paths);

  // I discovered that, if the DataPoint, Ellipse modules are
  // installed to .local/lib/x86_64-linux-gnu/qml, then setting
  // the import path to that directory is enough, no need to
  // add paths for each module subdirectory, like
  // .local/lib/x86_64-linux-gnu/qml/DataPoint is not required.

  QString new_import_path;

  // new_import_path =
  //   QString("%1/%2").arg(PROJECT_INSTALL_LIB_QML_DIR).arg("DataPoint");
  //
  // import_paths.prepend(new_import_path);
  //
  // new_import_path =
  //   QString("%1/%2").arg(PROJECT_INSTALL_LIB_QML_DIR).arg("Ellipse");
  //
  // import_paths.prepend(new_import_path);
  //
  // new_import_path =
  //   QString("%1/%2").arg(PROJECT_INSTALL_LIB_QML_DIR).arg("PappsoMS");
  //
  // import_paths.prepend(new_import_path);
  //
  // engine.setImportPathList(import_paths);

  new_import_path = QString(PROJECT_INSTALL_LIB_QML_DIR);

  import_paths.prepend(new_import_path);

  engine.setImportPathList(import_paths);

  qInfo() << "Engine import paths:" << engine.importPathList();

  engine.loadFromModule("PappsoMS", "MzIntegrationParams");
  engine.loadFromModule("PappsoMS", "Trace");

  engine.load(QUrl("qrc:/qml/test_datapoint.qml"));
  engine.load(QUrl("qrc:/qml/test_mzintegrationparams.qml"));

  if(engine.rootObjects().isEmpty())
    {
      qDebug() << "The engine's root object is empty.";

      return -1;
    }*/

  return application.exec();
}

// End of
// main(int argc, char **argv)
