// SPDX-FileCopyrightText: Copyright (c) Kitware Inc.
// SPDX-FileCopyrightText: Copyright (c) Sandia Corporation
// SPDX-License-Identifier: BSD-3-Clause
#ifndef pqPythonShell_h
#define pqPythonShell_h

#include "pqPythonModule.h" //  needed for PQPYTHON_EXPORT.
#include <QScopedPointer>   // needed for QScopedPointer.
#include <QWidget>

class vtkObject;
class pqConsoleWidget;

/**
 * @class pqPythonShell
 * @brief Widget for a Python shell.
 *
 * pqPythonShell is a QWidget subclass that provides an interactive Python
 * shell. It uses vtkPythonInteractiveInterpreter to provide an interactive Python
 * interpreter. Note that this is still executing Python code using the
 * application wide global Python interpreter, it just keeps the context separate
 * using an instance of `core.InteractiveConsole` internally.
 *
 * @section PythonInit Python initialization
 *
 * pqPythonShell does not initialize Python on creation. By default, it waits
 * till the pqConsoleWidget gets focus or `pqPythonShell::executeScript` is
 * called. One can also call `pqPythonShell::initialize` explicitly to
 * initialize the interpreter.
 *
 * @sa pqConsoleWidget.
 */
class PQPYTHON_EXPORT pqPythonShell : public QWidget
{
  Q_OBJECT
  typedef QWidget Superclass;

public:
  pqPythonShell(QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags{});
  ~pqPythonShell() override;

  /**
   * Returns the interactive console context (the locals() dict).
   * You can use static_cast<PythonObject*>() to convert the void pointer.
   * See vtkPythonInteractiveInterpreter::GetInteractiveConsoleLocalsPyObject().
   */
  void* consoleLocals();

  ///@{
  /**
   * Set/get the font size in points for the Python shell text.
   */
  void setFontSize(int fontSize);
  ///@}

  enum PrintMode
  {
    STATUS,
    OUTPUT,
    ERROR
  };

public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers)
  /**
   * Prints some text on the shell.
   */
  void printMessage(const QString&);

  /**
   * Clears the terminal. This does not change the state of the Python
   * interpreter, just clears the text shown in the Widget.
   */
  void clear();

  /**
   * Execute an arbitrary python script/string. This simply execute the Python
   * script in the global Python interpreter.
   */
  void executeScript(const QString&);

  /**
   * Resets the python interactive interpreter. This does not affect the global
   * Python interpreter. If the interactive interpreter hasn't been initialized
   * this has no effect.
   */
  void reset();

  /**
   * Returns true is the shell is currently executing a script/command.
   */
  bool isExecuting() const;

  /**
   * Use this method instead of calling pqConsoleWidget::printString()
   * directly. That helps us keep track of whether we need to show the prompt
   * or not.
   */
  void printString(const QString&, PrintMode mode = STATUS);

  /**
   * Set a list of statements to be run each time the interpreter is reset.
   *
   * By default, this imports the paraview.simple module.
   * If you call this method, be aware that the preamble is
   * assumed not to have any multi-line statements.
   */
  static void setPreamble(const QStringList& statements);
  static const QStringList& preamble();

  /**
   * Initialize the Python interpreter in the shell, if not already.
   * Has no effect if the interpreter has already been initialized.
   */
  void initialize();

Q_SIGNALS:
  /**
   * signal fired whenever the shell starts (starting=true) and finishes
   * (starting=false) executing a Python command/script. This can be used by
   * the UI to block user input while the script is executing.
   */
  void executing(bool starting);

protected Q_SLOTS:
  void pushScript(const QString&);
  void runScript();

protected: // NOLINT(readability-redundant-access-specifiers)
  pqConsoleWidget* ConsoleWidget;
  const char* Prompt;
  static QStringList Preamble;

  static const char* PS1() { return ">>> "; }
  static const char* PS2() { return "... "; }

  /**
   * Show the user-input prompt, if needed. Returns true if the prompt was
   * re-rendered, otherwise false.
   */
  bool prompt(const QString& indent = QString());

  void HandleInterpreterEvents(vtkObject* caller, unsigned long eventid, void* calldata);

private:
  Q_DISABLE_COPY(pqPythonShell)
  bool Prompted;

  class pqInternals;
  friend class pqInternals;
  QScopedPointer<pqInternals> Internals;
};

#endif // !pqPythonShell_h
