////////////////////////////////////////////////////////////////////////////////////////
//
//  Copyright 2026 OVITO GmbH, Germany
//
//  This file is part of OVITO (Open Visualization Tool).
//
//  OVITO is free software; you can redistribute it and/or modify it either under the
//  terms of the GNU General Public License version 3 as published by the Free Software
//  Foundation (the "GPL") or, at your option, under the terms of the MIT License.
//  If you do not alter this notice, a recipient may use your version of this
//  file under either the GPL or the MIT License.
//
//  You should have received a copy of the GPL along with this program in a
//  file LICENSE.GPL.txt.  You should have received a copy of the MIT License along
//  with this program in a file LICENSE.MIT.txt
//
//  This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
//  either express or implied. See the GPL or the MIT License for the specific language
//  governing rights and limitations.
//
////////////////////////////////////////////////////////////////////////////////////////

#pragma once


#include <ovito/gui/desktop/GUI.h>
#include "MainWindowUI.h"

namespace Ovito {

/**
 * \brief The main window of the application.
 *
 * Note that it is possible to open multiple main windows per
 * application instance to edit multiple datasets simultaneously.
 */
class OVITO_GUI_EXPORT MainWindow : public QMainWindow, public UserInterfaceComponent<MainWindowUI>
{
    Q_OBJECT

public:

    /// The pages of the command panel.
    enum CommandPanelPage {
        MODIFY_PAGE     = 0,
        RENDER_PAGE     = 1,
        OVERLAY_PAGE    = 2,
        UTILITY_PAGE    = 3
    };

public:

    /// Destructor.
    virtual ~MainWindow();

    /// Returns the main toolbar of the window.
    QToolBar* mainToolbar() const { return _mainToolbar; }

    /// Returns the frame buffer window for displaying the rendered image (may be null).
    FrameBufferWindow* frameBufferWindow() const { return _frameBufferWindow; }

    /// Returns the recommended size for this window.
    virtual QSize sizeHint() const override { return QSize(1024,768); }

    /// Restores a previously saved maximized/non-maximized state and shows the window.
    void restoreMainWindowGeometry();

    /// Saves the maximized/non-maximized state of the window in the settings store.
    void saveMainWindowGeometry();

    /// Loads the layout of the docked widgets from the settings store.
    void restoreLayout();

    /// Saves the layout of the docked widgets to the settings store.
    void saveLayout();

    /// Returns the widget that numerically displays the transformation.
    CoordinateDisplayWidget* coordinateDisplay() const { return _coordinateDisplay; }

    /// Returns the container widget for viewports.
    ViewportsPanel* viewportsPanel() const { return _viewportsPanel; }

    /// Returns the layout manager for the status bar area of the main window.
    QHBoxLayout* statusBarLayout() const { return _statusBarLayout; }

    /// Returns the page of the command panel that is currently visible.
    CommandPanelPage currentCommandPanelPage() const;

    /// Sets the page of the command panel that is currently visible.
    void setCurrentCommandPanelPage(CommandPanelPage page);

    /// Provides access to the main window's command panel.
    CommandPanel* commandPanel() const { return _commandPanel; }

    /// Opens the data inspector panel and shows the data object generated by the given data pipeline node.
    bool openDataInspector(PipelineNode* createdByNode, const QStringView objectIdentifierHint = {}, const QVariant& modeHint = {});

    /// Opens the data inspector panel and activates the given tab page.
    bool openDataInspector(const OvitoClass& appletClass);

    /// Sets the file path associated with this window and updates the window's title.
    void setWindowFilePath(const QString& filePath);

    /// Opens another main window (in addition to the existing windows) and optionally loads a file in the new window.
    static void openNewWindow(const QStringList& arguments = {});

    /// Displays an error message to the user that is associated with a particular child window or dialog.
    void reportError(const Exception& exception, QWidget* window);

    /// Checks if the current application has accessability access. This is required on macOS to move the cursor using QCursor::setPos().
    /// This method will prompt the user the first time it is called (for each ovito version). Returns true on non macOS.
    bool checkAccessibilityAccess(QWidget* parent = nullptr) const;

    /// Invokes a visitor function for every MainWindow instance of the application.
    template<typename Visitor>
    static void visitMainWindows(Visitor&& visitor) {
        for(QWidget* widget : QApplication::topLevelWidgets()) {
            if(MainWindow* mainWindow = qobject_cast<MainWindow*>(widget)) {
                visitor(mainWindow);
            }
        }
    }

Q_SIGNALS:

    /// Emitted whenever the progress state of the published tasks changed.
    void taskProgressUpdate();

    /// This signal is sent when the main window is being closed (typically by the user).
    void closingWindow();

protected:

    /// Is called by the system when the user tries to close the window.
    virtual void closeEvent(QCloseEvent* event) override;

    /// Is called when the window receives an event.
    virtual bool event(QEvent* event) override;

    /// Handles global key input.
    virtual void keyPressEvent(QKeyEvent* event) override;

    /// Called by the system when a drag is in progress and the mouse enters this window.
    virtual void dragEnterEvent(QDragEnterEvent* event) override;

    /// Called by the system when the drag is dropped on this window.
    virtual void dropEvent(QDropEvent* event) override;

    /// Called by the system when the window is moved
    virtual void moveEvent(QMoveEvent* event) override;

private Q_SLOTS:

    /// Displays an error message box. This slot is called by reportError().
    void showErrorMessages();

private:

    /// Constructor.
    explicit MainWindow(MainWindowUI& ui) : UserInterfaceComponent<MainWindowUI>(ui) {
        setAttribute(Qt::WA_DeleteOnClose);
    }

    /// Initializes the main window.
    void initializeWindow();

    /// Creates the main menu.
    void createMainMenu();

    /// Creates the main toolbar.
    void createMainToolbar();

    /// Creates a dock panel.
    QDockWidget* createDockPanel(const QString& caption, const QString& objectName, Qt::DockWidgetArea dockArea, Qt::DockWidgetAreas allowedAreas, QWidget* contents);

    /// Handler function for exceptions.
    void reportError(const Exception& ex, bool blocking);

    /// Creates a frame buffer of the requested size and displays it as a window in the user interface.
    std::shared_ptr<FrameBuffer> createAndShowFrameBuffer(int width, int height);

    /// Shows a progress bar or a similar UI to indicate the current rendering progress and let the user cancel the operation if necessary.
    void showRenderingProgress(const std::shared_ptr<FrameBuffer>& frameBuffer, SharedFuture<void> renderingFuture);

    /// Displays a modal message box to the user. Blocks until the user closes the message box.
    UserInterface::MessageBoxButton showMessageBoxImpl(QWidget* window, UserInterface::MessageBoxIcon icon, const QString& title, const QString& text, int buttons, UserInterface::MessageBoxButton defaultButton, const QString& detailedText);

private:

    /// The UI object associated with this widget.
    OORef<MainWindowUI> _ui;

    /// The upper main toolbar.
    QToolBar* _mainToolbar;

    /// The internal status bar widget.
    StatusBar* _statusBar;

    /// The frame buffer window displaying the rendered image.
    FrameBufferWindow* _frameBufferWindow = nullptr;

    /// The command panel.
    CommandPanel* _commandPanel;

    /// The container widget for viewports.
    ViewportsPanel* _viewportsPanel;

    /// The widget that numerically displays the object transformation.
    CoordinateDisplayWidget* _coordinateDisplay;

    /// The layout manager for the status bar area of the main window.
    QHBoxLayout* _statusBarLayout;

    /// The UI panel containing the data inspector tabs.
    DataInspectorPanel* _dataInspector;

    /// The title string to use for the main window (without any dynamic content).
    QString _baseWindowTitle;

    /// List of errors to be displayed by showErrorMessages().
    std::deque<Exception> _errorList;

    /// The current screen the window is on (to detect screen changes).
    QScreen* _currentScreen = nullptr;

    friend class MainWindowUI; // Allow MainWindowUI to access private constructor
};

}   // End of namespace
