From cc3d3e8021171366f854a6b4620bd1f351b900be Mon Sep 17 00:00:00 2001
From: Roman Telezhynskyi <dismine@gmail.com>
Date: Tue, 14 Apr 2020 11:36:21 +0300
Subject: [PATCH] Redesign command-line interface to support client server
 architecture.

---
 src/app/puzzle/puzzleapplication.cpp  | 80 ++++++++++++++++-----------
 src/app/puzzle/puzzleapplication.h    |  8 +--
 src/app/puzzle/puzzlemainwindow.cpp   |  6 ++
 src/app/puzzle/puzzlemainwindow.h     | 15 +++--
 src/app/puzzle/vpuzzlecommandline.cpp | 10 +++-
 src/app/puzzle/vpuzzlecommandline.h   |  3 +-
 6 files changed, 77 insertions(+), 45 deletions(-)

diff --git a/src/app/puzzle/puzzleapplication.cpp b/src/app/puzzle/puzzleapplication.cpp
index b7ca164c9..e88f231d1 100644
--- a/src/app/puzzle/puzzleapplication.cpp
+++ b/src/app/puzzle/puzzleapplication.cpp
@@ -198,7 +198,7 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
 
         if (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg)
         {
-            if (not qApp->IsTestMode())
+            if (qApp->IsAppInGUIMode())
             {
                 if (topWinAllowsPop)
                 {
@@ -235,8 +235,7 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
 PuzzleApplication::PuzzleApplication(int &argc, char **argv)
     :VAbstractApplication(argc, argv),
       mainWindows(),
-      localServer(nullptr),
-      testMode(false)
+      localServer(nullptr)
 {
     setApplicationDisplayName(VER_PRODUCTNAME_STR);
     setApplicationName(VER_INTERNALNAME_STR);
@@ -321,19 +320,13 @@ bool PuzzleApplication::notify(QObject *receiver, QEvent *event)
     return false;
 }
 
-//---------------------------------------------------------------------------------------------------------------------
-bool PuzzleApplication::IsTestMode() const
-{
-    return testMode;
-}
-
 //---------------------------------------------------------------------------------------------------------------------
 /**
  * @brief IsAppInGUIMode little hack that allow to have access to application state from VAbstractApplication class.
  */
 bool PuzzleApplication::IsAppInGUIMode() const
 {
-    return IsTestMode();
+    return CommandLine()->IsGuiEnabled();
 }
 
 //---------------------------------------------------------------------------------------------------------------------
@@ -342,7 +335,9 @@ PuzzleMainWindow *PuzzleApplication::MainWindow()
     Clean();
     if (mainWindows.isEmpty())
     {
-        NewMainWindow();
+        VPuzzleCommandLinePtr cmd;
+        VPuzzleCommandLine::ProcessInstance(cmd, QStringList());
+        NewMainWindow(true);
     }
     return mainWindows[0];
 }
@@ -360,11 +355,11 @@ QList<PuzzleMainWindow *> PuzzleApplication::MainWindows()
 }
 
 //---------------------------------------------------------------------------------------------------------------------
-PuzzleMainWindow *PuzzleApplication::NewMainWindow()
+PuzzleMainWindow *PuzzleApplication::NewMainWindow(bool guiMode)
 {
     PuzzleMainWindow *puzzle = new PuzzleMainWindow();
     mainWindows.prepend(puzzle);
-    if (not qApp->IsTestMode())
+    if (guiMode)
     {
         puzzle->show();
     }
@@ -444,10 +439,10 @@ void PuzzleApplication::ActivateDarkMode()
 //---------------------------------------------------------------------------------------------------------------------
 void PuzzleApplication::ParseCommandLine(const SocketConnection &connection, const QStringList &arguments)
 {
-    VPuzzleCommandLinePtr cmd = CommandLine();
-    testMode = cmd->IsTestModeEnabled();
+    VPuzzleCommandLinePtr cmd;
+    VPuzzleCommandLine::ProcessInstance(cmd, arguments);
 
-    if (not testMode && connection == SocketConnection::Client)
+    if (cmd->IsGuiEnabled() && connection == SocketConnection::Client)
     {
         const QString serverName = QCoreApplication::applicationName();
         QLocalSocket socket;
@@ -456,7 +451,7 @@ void PuzzleApplication::ParseCommandLine(const SocketConnection &connection, con
         {
             qCDebug(mApp, "Connected to the server '%s'", qUtf8Printable(serverName));
             QTextStream stream(&socket);
-            stream << QCoreApplication::arguments().join(";;");
+            stream << arguments.join(";;");
             stream.flush();
             socket.waitForBytesWritten();
             qApp->exit(V_EX_OK);
@@ -485,43 +480,65 @@ void PuzzleApplication::ParseCommandLine(const SocketConnection &connection, con
         LoadTranslation(PuzzleSettings()->GetLocale());
     }
 
+    ProcessArguments(cmd);
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+void PuzzleApplication::ProcessArguments(const VPuzzleCommandLinePtr &cmd)
+{
+    const QStringList rawLayouts = cmd->OptionRawLayouts();
     const QStringList args = cmd->OptionFileNames();
     if (args.count() > 0)
     {
-        if (testMode && args.count() > 1)
+        if (not cmd->IsGuiEnabled() && args.count() > 1)
         {
-            qCCritical(mApp, "%s\n", qPrintable(tr("Test mode doesn't support openning several files.")));
+            qCCritical(mApp, "%s\n", qPrintable(tr("Export mode doesn't support openning several files.")));
+            cmd.get()->parser.showHelp(V_EX_USAGE);
+        }
+
+        if (args.count() > 1 && rawLayouts.size() > 0)
+        {
+            qCCritical(mApp, "%s\n",
+                       qPrintable(tr("Import raw layout data does not support penning several layout files.")));
             cmd.get()->parser.showHelp(V_EX_USAGE);
         }
 
         for (auto &arg : args)
         {
-            NewMainWindow();
+            NewMainWindow(cmd->IsGuiEnabled());
             if (not MainWindow()->LoadFile(arg))
             {
-                if (testMode)
+                if (not cmd->IsGuiEnabled())
                 {
                     return; // process only one input file
                 }
                 delete MainWindow();
                 continue;
             }
+
+//            if (rawLayouts.size() > 0)
+//            {
+//                MainWindow()->ImportRawLayouts(rawLayouts);
+//            }
         }
     }
     else
     {
-        if (not testMode)
-        {
-            NewMainWindow();
-        }
-        else
+        if (cmd->IsTestModeEnabled())
         {
             qCCritical(mApp, "%s\n", qPrintable(tr("Please, provide one input file.")));
             cmd.get()->parser.showHelp(V_EX_USAGE);
         }
+
+        NewMainWindow(cmd->IsGuiEnabled());
+//        if (rawLayouts.size() > 0)
+//        {
+//            MainWindow()->New(); // prepare layout settings
+//            MainWindow()->ImportRawLayouts(rawLayouts);
+//        }
     }
 
-    if (testMode)
+    if (cmd->IsGuiEnabled())
     {
         qApp->exit(V_EX_OK); // close program after processing in console mode
     }
@@ -591,19 +608,18 @@ void PuzzleApplication::AboutToQuit()
 //---------------------------------------------------------------------------------------------------------------------
 void PuzzleApplication::NewLocalSocketConnection()
 {
-    QLocalSocket *socket = localServer->nextPendingConnection();
-    if (not socket)
+    QScopedPointer<QLocalSocket>socket(localServer->nextPendingConnection());
+    if (socket.isNull())
     {
         return;
     }
     socket->waitForReadyRead(1000);
-    QTextStream stream(socket);
+    QTextStream stream(socket.data());
     const QString arg = stream.readAll();
     if (not arg.isEmpty())
     {
         ParseCommandLine(SocketConnection::Server, arg.split(";;"));
     }
-    delete socket;
     MainWindow()->raise();
     MainWindow()->activateWindow();
 }
@@ -622,7 +638,7 @@ void PuzzleApplication::Clean()
 }
 
 //--------------------------------------------------------------------------------------------
-const VPuzzleCommandLinePtr PuzzleApplication::CommandLine()
+VPuzzleCommandLinePtr PuzzleApplication::CommandLine() const
 {
     return VPuzzleCommandLine::instance;
 }
diff --git a/src/app/puzzle/puzzleapplication.h b/src/app/puzzle/puzzleapplication.h
index 73d22c03b..c0dcf24ed 100644
--- a/src/app/puzzle/puzzleapplication.h
+++ b/src/app/puzzle/puzzleapplication.h
@@ -55,15 +55,13 @@ public:
 
     virtual bool notify(QObject * receiver, QEvent * event) override;
 
-    bool IsTestMode() const;
     virtual bool IsAppInGUIMode() const override;
     PuzzleMainWindow *MainWindow();
     QList<PuzzleMainWindow*> MainWindows();
-    PuzzleMainWindow *NewMainWindow();
+    PuzzleMainWindow *NewMainWindow(bool guiMode);
 
     void InitOptions();
 
-
     virtual const VTranslateVars *TrVars() override;
 
     virtual void  OpenSettings() override;
@@ -71,7 +69,8 @@ public:
     void ActivateDarkMode();
 
     void ParseCommandLine(const SocketConnection &connection, const QStringList &arguments);
-    const VPuzzleCommandLinePtr CommandLine();
+    void ProcessArguments(const VPuzzleCommandLinePtr &cmd);
+    VPuzzleCommandLinePtr CommandLine() const;
 public slots:
     void ProcessCMD();
 
@@ -89,7 +88,6 @@ private:
     Q_DISABLE_COPY(PuzzleApplication)
     QList<QPointer<PuzzleMainWindow> > mainWindows;
     QLocalServer *localServer;
-    bool testMode;
 
     void Clean();
 };
diff --git a/src/app/puzzle/puzzlemainwindow.cpp b/src/app/puzzle/puzzlemainwindow.cpp
index 7d122e9a9..c663a6858 100644
--- a/src/app/puzzle/puzzlemainwindow.cpp
+++ b/src/app/puzzle/puzzlemainwindow.cpp
@@ -56,6 +56,12 @@ bool PuzzleMainWindow::LoadFile(const QString &path)
     return true;
 }
 
+//---------------------------------------------------------------------------------------------------------------------
+void PuzzleMainWindow::ImportRawLayouts(const QStringList &layouts)
+{
+    Q_UNUSED(layouts)
+}
+
 //---------------------------------------------------------------------------------------------------------------------
 void PuzzleMainWindow::InitMenuBar()
 {
diff --git a/src/app/puzzle/puzzlemainwindow.h b/src/app/puzzle/puzzlemainwindow.h
index 9d2bdf70e..86f786f2c 100644
--- a/src/app/puzzle/puzzlemainwindow.h
+++ b/src/app/puzzle/puzzlemainwindow.h
@@ -28,14 +28,15 @@
 #ifndef PUZZLEMAINWINDOW_H
 #define PUZZLEMAINWINDOW_H
 
-#include "../vmisc/def.h"
-
 #include <QMainWindow>
 #include <QMessageBox>
+
+#include "../vmisc/def.h"
 #include "vpiececarrousel.h"
 
-namespace Ui {
-class PuzzleMainWindow;
+namespace Ui
+{
+    class PuzzleMainWindow;
 }
 
 class PuzzleMainWindow : public QMainWindow
@@ -48,6 +49,11 @@ public:
 
     bool LoadFile(const QString &path);
 
+    void ImportRawLayouts(const QStringList &layouts);
+
+public slots:
+    void New();
+
 private:
     Q_DISABLE_COPY(PuzzleMainWindow)
     Ui::PuzzleMainWindow *ui;
@@ -62,7 +68,6 @@ private:
     void InitPieceCarrousel();
 
 private slots:
-    void New();
     void Open();
     void Save();
     void SaveAs();
diff --git a/src/app/puzzle/vpuzzlecommandline.cpp b/src/app/puzzle/vpuzzlecommandline.cpp
index 0242e1356..6221808e2 100644
--- a/src/app/puzzle/vpuzzlecommandline.cpp
+++ b/src/app/puzzle/vpuzzlecommandline.cpp
@@ -84,15 +84,21 @@ VPuzzleCommandLine::VPuzzleCommandLine():
 
 //-------------------------------------------------------------------------------------------
 VPuzzleCommandLinePtr VPuzzleCommandLine::Instance(const QCoreApplication &app)
+{
+    VPuzzleCommandLine::ProcessInstance(instance, app.arguments());
+    return instance;
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+void VPuzzleCommandLine::ProcessInstance(VPuzzleCommandLinePtr &instance, const QStringList &arguments)
 {
     if (instance == nullptr)
     {
         instance.reset(new VPuzzleCommandLine);
     }
-    instance->parser.process(app);
+    instance->parser.process(arguments);
 
     instance->isGuiEnabled = not (instance->IsGuiEnabled() || instance->IsExportEnabled());
-    return instance;
 }
 
 //-------------------------------------------------------------------------------------------
diff --git a/src/app/puzzle/vpuzzlecommandline.h b/src/app/puzzle/vpuzzlecommandline.h
index 52d0e6836..8eb7edac8 100644
--- a/src/app/puzzle/vpuzzlecommandline.h
+++ b/src/app/puzzle/vpuzzlecommandline.h
@@ -38,7 +38,8 @@ protected:
     VPuzzleCommandLine();
 
     /** @brief create the single instance of the class inside puzzleapplication */
-    static VPuzzleCommandLinePtr Instance(const QCoreApplication &app);
+    static VPuzzleCommandLinePtr Instance(const QCoreApplication &app); 
+    static void ProcessInstance(VPuzzleCommandLinePtr &instance, const QStringList &arguments);
 private:
     Q_DISABLE_COPY(VPuzzleCommandLine)
     static VPuzzleCommandLinePtr instance;