/***************************************************************************
                                   qucs.h
                                  --------
    begin                : Thu Aug 28 2003
    copyright            : (C) 2003 by Michael Margraf
    email                : michael.margraf@alumni.tu-berlin.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef QUCS_H
#define QUCS_H

#include "dialogs/qucsshortcutdialog.h"
#include "qucsshortcutmanager.h"
#include <QFileSystemModel>
#include <QHash>
#include <QMainWindow>
#include <QSortFilterProxyModel>
#include <QStack>
#include <QString>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

class QucsDoc;
class Schematic;
class SimMessage;
class MouseActions;
class SearchDialog;
class OctaveWindow;
class MessageDock;
class ProjectView;
class ContextMenuTabWidget;
class TunerDialog;
class tunerElement;
class ExternSimDialog;

class QLabel;
class QAction;
class QLineEdit;
class QComboBox;
class QTabWidget;
class QDir;
class QMouseEvent;
class QCloseEvent;
class QMenu;
class QToolBar;
class QSettings;
class QListWidgetItem;
class QTreeWidget;
class QTreeWidgetItem;
class QListWidget;
class QShortcut;
class QListView;
class QModelIndex;
class QPushButton;
class QTextEdit;
class QFrame;

class SymbolWidget;

typedef bool (Schematic::*pToggleFunc)();
typedef void (MouseActions::*pMouseFunc)(Schematic *, QMouseEvent *);
typedef void (MouseActions::*pMouseFunc2)(Schematic *, QMouseEvent *, float,
                                          float);

class QucsFileSystemModel : public QFileSystemModel {
  Q_OBJECT
public:
  explicit QucsFileSystemModel(QObject *parent = nullptr)
      : QFileSystemModel(parent){};
  QVariant data(const QModelIndex &index, int role) const override;
};

class QucsSortFilterProxyModel : public QSortFilterProxyModel {
  Q_OBJECT

public:
  explicit QucsSortFilterProxyModel(QObject *parent = nullptr)
      : QSortFilterProxyModel(parent){};

protected:
  bool lessThan(const QModelIndex &left,
                const QModelIndex &right) const override;
};

class QucsApp : public QMainWindow {
  Q_OBJECT
public:
  QucsApp(bool netlist2Console);
  ~QucsApp();
  bool closeTabsRange(int startTab, int stopTab, int exceptTab = -1);
  bool closeAllFiles(int exceptTab = -1);
  bool closeAllLeft(int);
  bool closeAllRight(int);
  bool gotoPage(const QString &, bool reloadPage = false); // to load a document
  QucsDoc *getDoc(int No = -1);
  QucsDoc *findDoc(QString, int *Pos = 0);
  QString fileType(const QString &);
  static bool isTextDocument(QWidget *);

  QString ProjName; // name of the project, that is open
  // QHash<QString,QString> schNameHash; // QHash for the schematic files lookup
  // QHash<QString,QString> spiceNameHash; // QHash for the spice files lookup

  QLineEdit *editText;     // for edit component properties on schematic
  SearchDialog *SearchDia; // global in order to keep values
  TunerDialog *tunerDia;   // global in order to keep values
  SimMessage *sim;         // global in order to keep values

  // current mouse methods
  void (MouseActions::*MouseMoveAction)(Schematic *, QMouseEvent *);
  void (MouseActions::*MousePressAction)(Schematic *, QMouseEvent *, float,
                                         float);
  void (MouseActions::*MouseDoubleClickAction)(Schematic *, QMouseEvent *);
  void (MouseActions::*MouseReleaseAction)(Schematic *, QMouseEvent *);

  /**
   * @brief Registers the default keyboard shortcuts
   */
  void setDefaultShortcut();

protected:
  void closeEvent(QCloseEvent *);

public slots:
  void slotFileNew();         // generate a new schematic in the view TabBar
  void slotTextNew();         // generate a new text editor in the view TabBar
  void slotSymbolNew();       // create new symbol
  void slotFileOpen();        // open a document
  void slotFileSave();        // save a document
  void slotFileSaveAs();      // save a document under a different filename
  void slotFileSaveAll();     // save all open documents
  void slotFileClose();       // close the actual file
  void slotFileCloseOthers(); // close all documents except the current one
  void
  slotFileCloseAllLeft(); // close all documents to the left of the current one
  void slotFileCloseAllRight(); // close all documents to the right of the
                                // current one
  void slotFileCloseAll();      //  close all documents
  void slotFileExamples();      // show the examples in a file browser
  void slotHelpTutorial();      // Open a pdf tutorial
  void slotHelpReport();        // Open a pdf report
  void slotHelpTechnical();     // Open a pdf technical document
  void slotFileClose(int);      // close the file with given index
  void slotSymbolEdit();        // edit the symbol for the schematic
  void slotFileSettings();      // open dialog to change file settings
  void slotFilePrint();         // print the current file
  void slotFilePrintFit();      // Print and fit to page
  void slotFileQuit();          // exits the application
  void slotApplSettings();      // open dialog to change application settings
  void slotRefreshSchPath();    // refresh the schematic path hash

  void slotIntoHierarchy();
  void slotPopHierarchy();

  void slotShowAll();
  void slotZoomToSelection();
  void slotShowOne();
  void slotZoomOut(); // Zoom out by 2

  void slotToPage();
  void slotSelectComponent(QListWidgetItem *);
  void slotSearchComponent(const QString &);
  void slotSearchClear();

  void slotEditElement();
  void slotPowerMatching();
  void slot2PortMatching();

  // for menu that appears by right click in content ListView
  void slotShowContentMenu(const QPoint &);

  void slotCMenuOpen();
  void slotCMenuCopy();
  void slotCMenuRename();
  void slotCMenuDelete();
  void slotCMenuInsert();

  void slotUpdateTreeview();

  void slotMenuProjClose();

  void slotSimulate(QWidget *w = nullptr);
  void slotSimulateWithSpice();
  void slotTune(bool checked);

private slots:
  void slotMenuProjOpen();
  void slotMenuProjDel();
  void slotListProjOpen(const QModelIndex &);
  void slotSelectSubcircuit(const QModelIndex &);
  void slotSelectLibComponent(QTreeWidgetItem *);
  void slotOpenContent(const QModelIndex &);
  void slotSetCompView(int);
  void slotChangeSimulator(int);
  void slotButtonProjNew();
  void slotButtonProjOpen();
  void slotButtonProjDel();
  void slotChangeView();
  void slotAfterSimulation(int, SimMessage *);
  void slotDCbias();
  void slotChangePage(const QString &, const QString &);
  void slotHideEdit();
  void slotFileChanged(bool);
  void slotSimSettings();
  void slotSaveNetlist();
  void slotSaveCdlNetlist();
  void slotCdlSettings();
  void slotAfterSpiceSimulation(ExternSimDialog *SimDlg);
  void slotBuildVAModule();
  /*void slotBuildXSPICEIfs(int mode = 0);
  void slotEDDtoIFS();
  void slotEDDtoMOD();*/
  void slotShowModel();
  void slotSearchLibComponent(const QString &);
  void slotSearchLibClear();

  // Tab navigation
  ///
  /// \brief Go to the first tab
  ///
  void slotFirstTab();

  ///
  /// \brief Go to the next tab
  ///
  void slotNextTab();

  ///
  /// \brief Go to the previous tab
  ///
  void slotPreviousTab();

  ///
  /// \brief Go to the last tab
  ///
  void slotLastTab();

  void slotSwitchToTab(int index);

signals:
  void signalKillEmAll();

public:
  MouseActions *view;
  ContextMenuTabWidget *DocumentTab;
  QListWidget *CompComps;
  QTreeWidget *libTreeWidget;
  QTextEdit *CompDescr;
  QLineEdit *LibCompSearch;
  SymbolWidget *Symbol;
  QPushButton *btnShowModel;

  // menu appearing by right mouse button click on content listview
  QMenu *ContentMenu;

  // corresponding actions
  QAction *ActionCMenuOpen, *ActionCMenuCopy, *ActionCMenuRename,
      *ActionCMenuDelete, *ActionCMenuInsert;

  QAction *fileNew, *textNew, *symNew, *fileNewDpl, *fileOpen, *fileSave,
      *fileSaveAs, *fileSaveAll, *fileClose, *fileCloseOthers,
      *fileCloseAllLeft, *fileCloseAllRight, *fileCloseAll, *fileExamples,
      *fileSettings, *filePrint, *fileQuit, *projNew, *projOpen, *projDel,
      *projClose, *applSettings, *refreshSchPath, *editCut, *editCopy, *magAll,
      *magSel, *magOne, *magMinus, *filePrintFit, *tune, *symEdit, *intoH,
      *popH, *simulate, *save_netlist, *dpl_sch, *undo, *redo, *dcbias,
      *saveCdlNetlist, *cdlSettings;

  // Navigate tabs
  QAction *TabFirstAction;    /// Action for raising the first document tab
  QAction *TabLastAction;     /// Action for raising the last document tab
  QAction *TabNextAction;     /// Action for raising the next document tab
  QAction *TabPreviousAction; /// Action for raising the previous document tab

  QAction *exportAsImage;

  QAction *activeAction; // pointer to the action selected by the user
  bool TuningMode;
  QString windowTitle;

private:
  // ********* Widgets on the main area **********************************
  QDockWidget *dock;
  QTabWidget *TabView;
  QDockWidget *octDock;
  OctaveWindow *octave;
  MessageDock *messageDock;

  QListView *Projects;
  ProjectView *Content;

  QLineEdit *CompSearch;
  QPushButton *CompSearchClear;
  QComboBox *CompChoose;

  // ********** Properties ************************************************
  QStack<QString> HierarchyHistory; // keeps track of "go into subcircuit"
  QString QucsFileFilter;
  QFileSystemModel *a_homeDirModel;
  QucsSortFilterProxyModel *a_proxyModel;
  int ccCurIdx; // CompChooser current index (used during search)
  bool a_netlist2Console;

  // ********** Methods ***************************************************
  void initView();
  void initCursorMenu();

  int addDocumentTab(QFrame *widget, const QString &title = QString());
  void setDocumentTabChanged(int index, bool changed);
  void printCurrentDocument(bool);
  bool saveFile(QucsDoc *Doc = 0);
  bool saveAs();
  void openProject(const QString &);
  bool deleteProject(const QString &);
  void updatePortNumber(QucsDoc *, int);
  int fillComboBox(bool);
  void fillSimulatorsComboBox();
  void switchSchematicDoc(bool);
  void switchEditMode(bool);
  void changeSchematicSymbolMode(Schematic *);
  static bool recurRemove(const QString &);
  void closeFile(int);

  void updateRecentFilesList(QString s);
  void updateRecentProjectsList(QString pathToProj);
  void updateRecentProjectsList();
  void successExportMessages(bool ok);
  void fillLibrariesTreeView(void);
  bool populateLibTreeFromDir(const QString &LibDirPath,
                              QList<QTreeWidgetItem *> &topitems,
                              bool relpath = false);
  void saveSettings();
  QWidget *getSchematicWidget(QucsDoc *Doc);

public:
  void readProjects();
  void
  updatePathList(void); // update the list of paths, pruning non-existing paths
  void updatePathList(QStringList);
  // void updateSchNameHash(void); // maps all schematic files in the path list
  // void updateSpiceNameHash(void); // maps all spice files in the path list

  /* **************************************************
   *****  The following methods are located in  *****
   *****  "qucs_init.cpp".                      *****
   ************************************************** */

public slots:
  void slotShowWarnings();
  void slotResetWarnings();
  void printCursorPosition(int, int, QString);
  void slotUpdateUndo(bool); // update undo available state
  void slotUpdateRedo(bool); // update redo available state

private slots:
  void slotViewBrowseDock(bool toggle); // toggle the dock window
  void slotViewOctaveDock(bool);        // toggle the dock window
  void slotToggleOctave(bool);
  void slotToggleDock(bool);
  void slotHelpAbout(); // shows an about dialog

  ///
  /// @brief Opens the shortcut editor dialog
  ///
  void slotShortcutDialog();

private:
  void initActions();   // initializes all QActions of the application
  void initMenuBar();   // creates the menu_bar and inserts the menuitems
  void initToolBar();   // creates the toolbars
  void initStatusBar(); // setup the statusbar

  void openFileFromProjectView(const QFileInfo &Info, const QString &note);

  QAction *helpAboutApp, *helpAboutQt, *viewBrowseDock, *viewOctaveDock;

  // menus contain the items of their menubar
  enum { MaxRecentFiles = 8, MaxRecentProjects = 8 };
  QMenu *fileMenu, *editMenu, *insMenu, *projMenu, *recentProjMenu, *simMenu,
      *viewMenu, *helpMenu, *alignMenu, *toolMenu, *recentFilesMenu, *cmMenu;
  QAction *fileRecentAction[MaxRecentFiles];
  QAction *fileClearRecent;

  QAction *projRecentActions[MaxRecentProjects];
  QAction *projClearRecent;

  // submenus for the PDF documents
  QMenu *helpTechnical, *helpReport, *helpTutorial;

  QComboBox *simulatorsCombobox;
  QToolBar *fileToolbar, *editToolbar, *viewToolbar, *workToolbar,
      *simulateToolbar;

  // Shortcuts for scrolling schematic / TextEdit
  // This is rather cumbersome -> Make this with a QScrollView instead??
  QShortcut *cursorUp, *cursorLeft, *cursorRight, *cursorDown;

  QLabel *WarningLabel, *PositionLabel,
      *DiagramValuesLabel; // labels in status bar
  // QLabel *SimulatorLabel;

  /* **************************************************
   *****  The following methods are located in  *****
   *****  "qucs_actions.cpp".                   *****
   ************************************************** */

public:
  void editFile(const QString &, bool reloadFile = false);

  QAction *insWire, *insLabel, *insGround, *insPort, *insEquation, *magPlus,
      *editRotate, *editMirror, *editMirrorY, *editPaste, *select, *editStretch,
      *editMove, *editActivate, *wire, *editDelete, *setMarker,
      *setDiagramLimits, *resetDiagramLimits, *showGrid, *onGrid, *moveText,
      *helpIndex, *helpGetStart, *callEditor, *callFilter, *callLine,
      *callActiveFilter, *showMsg, *showNet, *alignTop, *alignBottom,
      *alignLeft, *alignRight, *distrHor, *distrVert, *selectAll, *callMatch,
      *changeProps, *addToProj, *editFind, *insEntity, *selectMarker,
      *createLib, *callConverter, *graph2csv, *callAtt, *centerHor, *centerVert,
      *loadModule, *buildModule, *callPwrComb, *callRFLayout, *callSPAR_Viewer,
      *callRxcalc;

  QAction *helpQucsIndex;
  QAction *simSettings;
  QAction *buildVAModule;
  QAction *ShortcutManagerAction; /// Action to open the keyboard shortcut
                                  /// manager dialog

public slots:
  void slotEditRotate(bool);  // rotate the selected items
  void slotEditMirrorX(bool); // mirror the selected items about X axis
  void slotEditMirrorY(bool); // mirror the selected items about Y axis
  void slotEditCut();         // put marked object into clipboard and delete it
  void slotEditCopy();        // put the marked object into the clipboard
  void slotEditPaste(bool);   // paste the clipboard into the document
  void slotEditDelete(bool);  // delete the selected items
  void slotInsertEquation(bool);
  void slotInsertGround(bool);
  void slotInsertPort(bool);
  void slotInsertEntity();
  void slotSetWire(bool);
  void slotEscape();
  void slotSelect(bool);
  void slotEditActivate(bool);
  void slotEditStretch(bool); // move selection of components w/wire stretching
  void slotEditMove(bool); // move selection of components and disconnect wires.
  void slotInsertLabel(bool);
  void slotSetMarker(bool);
  void slotSetDiagramLimits(bool);
  void slotResetDiagramLimits();
  void slotShowGrid();     // turn the grid on or off
  void slotOnGrid(bool);   // set selected elements on grid
  void slotMoveText(bool); // move property text of components
  void slotZoomIn(bool);
  void slotEditUndo();     // makes the last operation undone
  void slotEditRedo();     // makes the last undo undone
  void slotEditFind();     // searches for a piece of text
  void slotAlignTop();     // align selected elements with respect to top
  void slotAlignBottom();  // align selected elements with respect to bottom
  void slotAlignLeft();    // align selected elements with respect to left
  void slotAlignRight();   // align selected elements with respect to right
  void slotDistribHoriz(); // distribute horizontally selected elements
  void slotDistribVert();  // distribute vertically selected elements
  void slotCenterHorizontal();
  void slotCenterVertical();
  void slotSelectAll();
  void slotSelectMarker();
  void slotShowLastMsg();
  void slotShowLastNetlist();
  void slotCallEditor();
  void slotCallFilter();
  void slotCallActiveFilter();
  void slotCallLine();
  void slotCallMatch();
  void slotCallAtt();
  void slotCallPwrComb();
  void slotCallSPAR_Viewer();
  void slotCallRxCalc();
  void slotCallRFLayout();
  void slotHelpIndex(); // shows a HTML docu: Help Index
  void slotHelpQucsIndex();
  void slotGettingStarted(); // shows a HTML docu: Getting started
  void slotChangeProps();
  void slotAddToProject();
  void slotApplyCompText();
  void slotOpenRecentFile();
  void slotOpenRecentProject();
  void slotSaveDiagramToGraphicsFile();
  void slotSaveSchematicToGraphicsFile(bool diagram = false);

private slots:
  void slotCursorLeft(bool left = true);
  void slotCursorRight() { return slotCursorLeft(false); }
  void slotCursorUp(bool up = true);
  void slotCursorDown() { return slotCursorUp(false); }
  void slotResizePropEdit(const QString &);
  void slotCreateLib();
  void slotImportData();
  void slotExportGraphAsCsv();
  void slotUpdateRecentFiles();
  void slotClearRecentFiles();
  void slotUpdateRecentProjects();
  void slotClearRecentProjects();
  void slotLoadModule();
  void slotBuildModule();

private:
  void buildWithOpenVAF();
  bool performToggleAction(bool, QAction *, pToggleFunc, pMouseFunc,
                           pMouseFunc2);
  void launchTool(const QString &, const QString &,
                  const QStringList & = QStringList(),
                  bool qucs_tool = false); // tool, description and args
  friend class SaveDialog;

  /// @brief Scans the schematic for CMD components and executes their commands
  ///        after simulation completes.
  ///
  ///
  /// Multi-line commands are joined with @c && so that they run sequentially
  /// in the same shell session. Comment lines (starting with @c #) and blank
  /// lines are stripped before joining. The @c ~ home-directory shorthand is
  /// expanded before the command is passed to the shell.
  ///
  /// On Linux, different terminal emulators are tried:
  /// konsole, gnome-terminal, xfce4-terminal, lxterminal, xterm. Each is
  /// launched with a clean environment (without @c LD_LIBRARY_PATH and
  /// @c LD_PRELOAD) to avoid Qt library version conflicts between Qucs-S and
  /// the terminal emulator.
  ///
  /// @param sch  Pointer to the schematic to scan. If @c nullptr, returns
  ///             immediately without doing anything.
  ///
  /// @note This method is called from both @c slotAfterSimulation() and
  ///       @c slotAfterSpiceSimulation(), covering all simulation backends.
  void runPostSimCommands(Schematic* sch);

  QString lastExportFilename;
};

/** \brief Provide a template to declare singleton classes.
 *
 * Classes implemented using this template will be singletons (i.e., only
 * one instance of the class will exist per invokation). Primarily this
 * is used to support static / access to an application-wide function, e.g.
 * settings.
 *
 */
template <typename T> class QucsSingleton final {
public:
  static T &Get() {
    static T instance;
    return instance;
  }

  // Prevent overriding default ctor, dtor, copying, or multiple instances.
private:
  QucsSingleton() = default;
  ~QucsSingleton() = default;

  QucsSingleton(const QucsSingleton &) = delete;
  QucsSingleton &operator=(const QucsSingleton &) = delete;
  QucsSingleton(QucsSingleton &&) = delete;
  QucsSingleton &operator=(QucsSingleton &&) = delete;
};

class ContextMenuTabWidget : public QTabWidget {
  Q_OBJECT
public:
  ContextMenuTabWidget(QucsApp *parent = 0);
public slots:
  void showContextMenu(const QPoint &point);

private:
  int contextTabIndex; // index of tab where context menu was opened
  QucsApp *App;        // the main application - parent widget
private slots:
  void slotCxMenuClose();
  void slotCxMenuCloseOthers();
  void slotCxMenuCloseAll();
  void slotCxMenuCloseRight();
  void slotCxMenuCloseLeft();
  void slotCxMenuCopyPath();
  void slotCxMenuOpenFolder();
};

#endif /* QUCS_H */
